feature: coursework completed

This commit is contained in:
Danil Markov 2023-12-29 18:42:15 +04:00
parent b58bbf4421
commit 35a3213edd
54 changed files with 2193 additions and 798 deletions

View File

@ -9,20 +9,68 @@
<option name="autoReloadType" value="NONE" /> <option name="autoReloadType" value="NONE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="7c94e195-a540-483e-9a1c-11797aeb1741" name="Changes" comment="feature: lab5 done"> <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$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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/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/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/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/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/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> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </component>
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[6681ed71]" /> <component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[C:\Users\Danil\.android\avd\Pixel_3a_API_34_extension_level_7_x86_64.avd]" />
<component name="ExternalProjectsData"> <component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$"> <projectState path="$PROJECT_DIR$">
<ProjectState /> <ProjectState />
@ -46,7 +94,7 @@
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY"> <option name="RECENT_BRANCH_BY_REPOSITORY">
<map> <map>
<entry key="$PROJECT_DIR$" value="LabWork04" /> <entry key="$PROJECT_DIR$" value="LabWork05" />
</map> </map>
</option> </option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
@ -72,12 +120,12 @@
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;, &quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;ToolWindowLogcat.ShowToolbar&quot;: &quot;false&quot;, &quot;ToolWindowLogcat.ShowToolbar&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;, &quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;com.android.tools.idea.devicemanager.tab&quot;: &quot;Physical&quot;, &quot;com.google.services.firebase.aqiPopupShown&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/Danil/Desktop/Новая папка&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;Modules&quot;, &quot;project.structure.last.edited&quot;: &quot;Project&quot;,
&quot;project.structure.proportion&quot;: &quot;0.17&quot;, &quot;project.structure.proportion&quot;: &quot;0.17&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;, &quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;emulator&quot; &quot;settings.editor.selected.configurable&quot;: &quot;preferences.lookFeel&quot;
}, },
&quot;keyToStringList&quot;: { &quot;keyToStringList&quot;: {
&quot;ExportApk.BuildVariants&quot;: [ &quot;ExportApk.BuildVariants&quot;: [
@ -93,7 +141,7 @@
} }
}</component> }</component>
<component name="PsdUISettings"> <component name="PsdUISettings">
<option name="MODULE_TAB" value="Signing Configs" /> <option name="MODULE_TAB" value="Properties" />
<option name="LAST_EDITED_SIGNING_CONFIG" value="debug" /> <option name="LAST_EDITED_SIGNING_CONFIG" value="debug" />
<option name="LAST_EDITED_BUILD_TYPE" value="release" /> <option name="LAST_EDITED_BUILD_TYPE" value="release" />
</component> </component>
@ -101,6 +149,10 @@
<key name="android.template.-1413981578"> <key name="android.template.-1413981578">
<recent name="com.example.myapplication.Navbar" /> <recent name="com.example.myapplication.Navbar" />
</key> </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"> <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\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\res\drawable" />
@ -338,7 +390,14 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1703256177985</updated> <updated>1703256177985</updated>
</task> </task>
<option name="localTasksCounter" value="11" /> <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 /> <servers />
</component> </component>
<component name="Vcs.Log.Tabs.Properties"> <component name="Vcs.Log.Tabs.Properties">
@ -361,25 +420,16 @@
<MESSAGE value="feature: lab4 maybe done" /> <MESSAGE value="feature: lab4 maybe done" />
<MESSAGE value="feature: lab5 almost done, save commit" /> <MESSAGE value="feature: lab5 almost done, save commit" />
<MESSAGE value="feature: lab5 done" /> <MESSAGE value="feature: lab5 done" />
<option name="LAST_COMMIT_MESSAGE" value="feature: lab5 done" /> <MESSAGE value="feature: add logo" />
<option name="LAST_COMMIT_MESSAGE" value="feature: add logo" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager> <breakpoint-manager>
<breakpoints> <breakpoints>
<line-breakpoint enabled="true" type="kotlin-line"> <line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt</url> <url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/UserViewModel.kt</url>
<line>63</line> <line>49</line>
<option name="timeStamp" value="23" /> <option name="timeStamp" value="105" />
</line-breakpoint>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/BasketViewModel.kt</url>
<line>131</line>
<option name="timeStamp" value="52" />
</line-breakpoint>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/ServiceRemoteMediator.kt</url>
<line>55</line>
<option name="timeStamp" value="53" />
</line-breakpoint> </line-breakpoint>
</breakpoints> </breakpoints>
</breakpoint-manager> </breakpoint-manager>

View File

@ -15,10 +15,10 @@ import com.example.myapplication.ui.theme.BlueMain
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
//this.deleteDatabase("my-db")
setContent { setContent {
AppTheme (darkTheme = false){ AppTheme (darkTheme = false){
// A surface container using the 'background' color from the theme // A surface container using the 'background' color from the theme
Surface( Surface(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

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

View File

@ -3,6 +3,7 @@ package com.example.myapplication.api
import com.example.myapplication.api.model.BasketServiceRemote import com.example.myapplication.api.model.BasketServiceRemote
import com.example.myapplication.api.model.OrderRemote import com.example.myapplication.api.model.OrderRemote
import com.example.myapplication.api.model.OrderServiceRemote 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.ServiceRemote
import com.example.myapplication.api.model.UserRemote import com.example.myapplication.api.model.UserRemote
import com.example.myapplication.api.model.UserRemoteSignIn import com.example.myapplication.api.model.UserRemoteSignIn
@ -10,6 +11,8 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Response
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.DELETE import retrofit2.http.DELETE
@ -20,7 +23,7 @@ import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
interface ServerService { interface ServerService {
//SNEAKER // Service
@GET("service/get/{id}") @GET("service/get/{id}")
suspend fun getService( suspend fun getService(
@Path("id") id: Int, @Path("id") id: Int,
@ -46,7 +49,7 @@ interface ServerService {
@DELETE("service/delete/{id}") @DELETE("service/delete/{id}")
suspend fun deleteService( suspend fun deleteService(
@Path("id") id: Int @Path("id") id: Int
) ): Response<String>
//USER //USER
@POST("user/signup") @POST("user/signup")
@ -59,6 +62,11 @@ interface ServerService {
@Body user: UserRemoteSignIn @Body user: UserRemoteSignIn
): UserRemote ): UserRemote
@PUT("user/update")
suspend fun updateUser(
@Body user: UserRemote
): UserRemote
//BASKET //BASKET
@POST("basket/addServiceToBasket") @POST("basket/addServiceToBasket")
suspend fun addServiceToBasket( suspend fun addServiceToBasket(
@ -140,6 +148,14 @@ interface ServerService {
suspend fun deleteOrder( suspend fun deleteOrder(
@Path("orderId") orderId: Int @Path("orderId") orderId: Int
) )
//REPORT
@GET("report/getReport/{dateFrom}/{dateTo}")
suspend fun getReport(
@Path("dateFrom") dateFrom: Long,
@Path("dateTo") dateTo: Long
): ReportRemote
companion object { companion object {
private const val BASE_URL = "https://ftkfjb1l-8080.euw.devtunnels.ms/api/" private const val BASE_URL = "https://ftkfjb1l-8080.euw.devtunnels.ms/api/"
@ -148,7 +164,10 @@ interface ServerService {
fun getInstance(): ServerService { fun getInstance(): ServerService {
return INSTANCE ?: synchronized(this) { return INSTANCE ?: synchronized(this) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder() val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build() .build()
return Retrofit.Builder() return Retrofit.Builder()
.baseUrl(BASE_URL) .baseUrl(BASE_URL)

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

@ -8,19 +8,19 @@ data class ServiceRemote (
val id: Int? = 0, val id: Int? = 0,
val name: String = "", val name: String = "",
val price: Double = 0.0, val price: Double = 0.0,
val photo: Int? = 0 val photo: String = ""
) )
fun ServiceRemote.toService(): Service = Service( fun ServiceRemote.toService(): Service = Service(
id, id,
name, name,
price, price,
photo RemoteConverters.toBitmap(photo)
) )
fun Service.toServiceRemote():ServiceRemote = ServiceRemote( fun Service.toServiceRemote():ServiceRemote = ServiceRemote(
serviceId, serviceId,
name, name,
price, price,
photo 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

@ -12,7 +12,7 @@ data class UserRemote (
val email: String = "", val email: String = "",
val password: String = "", val password: String = "",
val role: RoleEnum = RoleEnum.User, val role: RoleEnum = RoleEnum.User,
val photo: Int? = 0, val photo: String = "",
val basketId: Int? = 0 val basketId: Int? = 0
) )
@ -24,7 +24,7 @@ fun UserRemote.toUser(): User = User(
email, email,
password, password,
role, role,
photo, RemoteConverters.toBitmap(photo),
basketId basketId
) )
@ -35,6 +35,6 @@ fun User.toUserRemote():UserRemote = UserRemote(
email, email,
password, password,
role, role,
photo, RemoteConverters.fromBitmap(photo),
basketId basketId
) )

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

@ -60,4 +60,9 @@ class RestServiceRepository(
dbServiceRepository.invalidateService(service.serviceId!!) dbServiceRepository.invalidateService(service.serviceId!!)
}catch (ex: Exception){} }catch (ex: Exception){}
} }
override fun call(str: String): Flow<PagingData<Service>> {
return dbServiceRepository.call(str)
}
} }

View File

@ -15,7 +15,7 @@ class RestUserRepository(
} }
override suspend fun update(user: User) { override suspend fun update(user: User) {
println() service.updateUser(user.toUserRemote())
} }
override suspend fun delete(user: User) { override suspend fun delete(user: User) {

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

@ -10,4 +10,5 @@ interface ServiceRepository {
suspend fun delete(service: Service) suspend fun delete(service: Service)
suspend fun getServiceById(id: Int): Service suspend fun getServiceById(id: Int): Service
suspend fun getAllServices(): Flow<PagingData<Service>> suspend fun getAllServices(): Flow<PagingData<Service>>
fun call(str: String): Flow<PagingData<Service>>
} }

View File

@ -12,13 +12,25 @@ object AppViewModelProvider {
ServiceViewModel(app().container.serviceRepo) ServiceViewModel(app().container.serviceRepo)
} }
initializer { initializer {
UserViewModel(app().container.userRepo, app().container.basketRepo) UserViewModel(
app().container.userRepo,
app().container.basketRepo
)
} }
initializer { initializer {
OrderViewModel(app().container.orderRepo, app().container.basketRepo) OrderViewModel(
app().container.orderRepo,
app().container.basketRepo
)
} }
initializer { initializer {
BasketViewModel(app().container.basketRepo, app().container.orderRepo,) BasketViewModel(
app().container.basketRepo,
app().container.orderRepo,
)
}
initializer {
ReportViewModel(app().container.reportRepo)
} }
} }
} }

View File

@ -2,7 +2,6 @@ package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableDoubleStateOf import androidx.compose.runtime.mutableDoubleStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser import com.example.myapplication.GlobalUser
import com.example.myapplication.businessLogic.repository.BasketRepository import com.example.myapplication.businessLogic.repository.BasketRepository
@ -17,9 +16,10 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
data class ServiceWithQuantity(val service: Service, val quantityStateFlow: MutableStateFlow<Int>) class BasketViewModel(
private val basketRepository: BasketRepository,
class BasketViewModel(private val basketRepository: BasketRepository, private val orderRepository: OrderRepository) : ViewModel() { private val orderRepository: OrderRepository
) : MyViewModel() {
private val _quantityMap = MutableStateFlow<Map<Int, MutableStateFlow<Int>>>(emptyMap()) private val _quantityMap = MutableStateFlow<Map<Int, MutableStateFlow<Int>>>(emptyMap())
val quantityMap: StateFlow<Map<Int, MutableStateFlow<Int>>> get() = _quantityMap val quantityMap: StateFlow<Map<Int, MutableStateFlow<Int>>> get() = _quantityMap
private val _total = mutableDoubleStateOf(0.00) private val _total = mutableDoubleStateOf(0.00)
@ -28,6 +28,7 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
val myList: StateFlow<List<Service>> get() = _myList val myList: StateFlow<List<Service>> get() = _myList
private var _basketId = MutableStateFlow(0) private var _basketId = MutableStateFlow(0)
val basketId: StateFlow<Int> get() = _basketId val basketId: StateFlow<Int> get() = _basketId
fun getQuantityState(basketId: Int, serviceId: Int): Flow<Int> { fun getQuantityState(basketId: Int, serviceId: Int): Flow<Int> {
val quantityMap = _quantityMap.value.toMutableMap() val quantityMap = _quantityMap.value.toMutableMap()
val quantityStateFlow = quantityMap.getOrPut(serviceId) { val quantityStateFlow = quantityMap.getOrPut(serviceId) {
@ -59,15 +60,14 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
} }
} }
fun getBasketServices() { fun getBasketServices() = viewModelScope.launch {
viewModelScope.launch { val userId = GlobalUser.getInstance().getUser()?.userId!!
val basketId = GlobalUser.getInstance().getUser()?.basketId!!
basketRepository.getBasketWithServices(basketId) basketRepository.getBasketWithServices(userId)
.collect{services -> .collect{services ->
_myList.value = services _myList.value = services
} }
} }
}
fun getUsersBasket(id: Int) { fun getUsersBasket(id: Int) {
viewModelScope.launch{ viewModelScope.launch{
@ -91,27 +91,36 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
} }
fun incrementServiceQuantity(basketId: Int, serviceId: Int) { fun incrementServiceQuantity(basketId: Int, serviceId: Int) {
viewModelScope.launch { runInScope(
actionSuccess = {
basketRepository.incrementServiceQuantity(basketId, serviceId) basketRepository.incrementServiceQuantity(basketId, serviceId)
val currentQuantity = getQuantityState(basketId, serviceId).first() }
)
var currentQuantity: Int = 0
viewModelScope.launch {
currentQuantity = getQuantityState(basketId, serviceId).first()
}
_quantityMap.value.toMutableMap().apply { _quantityMap.value.toMutableMap().apply {
put(serviceId, MutableStateFlow(currentQuantity + 1)) put(serviceId, MutableStateFlow(currentQuantity + 1))
} }
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!) updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
} }
}
fun decrementOrRemoveServiceQuantity(basketId: Int, serviceId: Int) { fun decrementOrRemoveServiceQuantity(basketId: Int, serviceId: Int) = viewModelScope.launch{
viewModelScope.launch {
val currentQuantity = getQuantityState(basketId, serviceId).first() val currentQuantity = getQuantityState(basketId, serviceId).first()
if (currentQuantity > 1) { if (currentQuantity > 1) {
basketRepository.decrementServiceQuantity(basketId, serviceId) runInScope(
actionSuccess = { basketRepository.decrementServiceQuantity(basketId, serviceId) }
)
_quantityMap.value.toMutableMap().apply { _quantityMap.value.toMutableMap().apply {
put(serviceId, MutableStateFlow(currentQuantity - 1)) put(serviceId, MutableStateFlow(currentQuantity - 1))
} }
} }
else{ else{
basketRepository.removeServiceFromBasket(basketId, serviceId) runInScope(
actionSuccess = { basketRepository.removeServiceFromBasket(basketId, serviceId) }
)
_quantityMap.value.toMutableMap().apply { _quantityMap.value.toMutableMap().apply {
remove(serviceId, MutableStateFlow(currentQuantity - 1)) remove(serviceId, MutableStateFlow(currentQuantity - 1))
} }
@ -119,7 +128,6 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
} }
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!) updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
} }
}
fun updateSubTotal(userId: Int) { fun updateSubTotal(userId: Int) {
viewModelScope.launch { viewModelScope.launch {

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

@ -3,7 +3,6 @@ package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableDoubleStateOf import androidx.compose.runtime.mutableDoubleStateOf
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser import com.example.myapplication.GlobalUser
import com.example.myapplication.businessLogic.repository.BasketRepository import com.example.myapplication.businessLogic.repository.BasketRepository
@ -13,10 +12,14 @@ import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Date import java.util.Date
class OrderViewModel(private val orderRepository: OrderRepository, private val basketRepository: BasketRepository) : ViewModel() { class OrderViewModel(
private val orderRepository: OrderRepository,
private val basketRepository: BasketRepository
) : MyViewModel() {
private var _selectedItems = MutableLiveData<List<Service>>() private var _selectedItems = MutableLiveData<List<Service>>()
val selectedItems get() = _selectedItems val selectedItems get() = _selectedItems
private val _total = mutableDoubleStateOf(0.00) private val _total = mutableDoubleStateOf(0.00)
@ -31,16 +34,24 @@ class OrderViewModel(private val orderRepository: OrderRepository, private val b
total = getTotal(userId), total = getTotal(userId),
creatorUserId = userId creatorUserId = userId
) )
orderRepository.insert(order) runInScope(
actionSuccess = { orderRepository.insert(order) }
)
} }
return true return true
} }
suspend fun getOrderWithServices(id: Int) : Flow<List<Service>> { suspend fun getOrderWithServices(id: Int) : Flow<List<Service>> {
return orderRepository.getServiceFromOrder(id) return try{
orderRepository.getServiceFromOrder(id)
}catch (e: Exception){
emptyFlow()
}
} }
suspend fun getUserOrders(id: Int) { suspend fun getUserOrders(id: Int) {
runInScope(
actionSuccess = {
viewModelScope.launch { viewModelScope.launch {
orderRepository.getUserOrders(id) orderRepository.getUserOrders(id)
.collect{items -> .collect{items ->
@ -48,6 +59,8 @@ class OrderViewModel(private val orderRepository: OrderRepository, private val b
} }
} }
} }
)
}
fun updateSelectedItems(items: List<Service>) { fun updateSelectedItems(items: List<Service>) {
_selectedItems.value = items _selectedItems.value = items

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

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

View File

@ -1,75 +1,55 @@
package com.example.myapplication.businessLogic.viewmodel package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.getValue import android.graphics.Bitmap
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser import com.example.myapplication.GlobalUser
import com.example.myapplication.R
import com.example.myapplication.api.model.UserRemoteSignIn import com.example.myapplication.api.model.UserRemoteSignIn
import com.example.myapplication.businessLogic.repository.BasketRepository import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.UserRepository import com.example.myapplication.businessLogic.repository.UserRepository
import com.example.myapplication.model.RoleEnum import com.example.myapplication.model.RoleEnum
import com.example.myapplication.model.User import com.example.myapplication.model.User
import kotlinx.coroutines.launch
class UserViewModel(private val userRepository: UserRepository, private val basketRepository: BasketRepository): ViewModel() { class UserViewModel(
private val userRepository: UserRepository,
private val basketRepository: BasketRepository
): MyViewModel() {
var name = mutableStateOf("") var name = mutableStateOf("")
var surname = mutableStateOf("") var surname = mutableStateOf("")
var email = mutableStateOf("") var email = mutableStateOf("")
var password = mutableStateOf("") var password = mutableStateOf("")
var photo = mutableIntStateOf(R.drawable.icon_profile)
var isLoggedIn by mutableStateOf(false)
var isBusy by mutableStateOf(false)
// private val _loggedUser = MutableStateFlow(User(null,"null","null","null","null",RoleEnum.User))
// val loggedUser: StateFlow<User> = _loggedUser.asStateFlow()
fun createUser() = viewModelScope.launch { fun createUser(photo: Bitmap){
val user = User( val user =
User(
name = name.value, name = name.value,
surname = surname.value, surname = surname.value,
email = email.value, email = email.value,
password = password.value, password = password.value,
role = RoleEnum.User, role = RoleEnum.User,
photo = R.drawable.icon_profile photo = photo
) )
// isBusy = true runInScope(
actionSuccess = {
userRepository.insert(user) userRepository.insert(user)
// isBusy = false }
)
} }
fun authUser() = viewModelScope.launch { fun authUser() {
// isBusy = true runInScope(
actionSuccess = {
val user = userRepository.authUser(UserRemoteSignIn(email.value, password.value)) val user = userRepository.authUser(UserRemoteSignIn(email.value, password.value))
// isLoggedIn = true
// isBusy = false
GlobalUser.getInstance().setUser(user) GlobalUser.getInstance().setUser(user)
},
)
} }
fun updateUser() = viewModelScope.launch { fun updateUser(user: User){
val updateUser = GlobalUser.getInstance().getUser() runInScope(
if(email.value != "") actionSuccess = {
updateUser?.email = email.value userRepository.update(user)
else
updateUser?.email = updateUser?.email.toString()
if(name.value != "")
updateUser?.name = name.value
else
updateUser?.name = updateUser?.name.toString()
if(surname.value != "")
updateUser?.surname = surname.value
else
updateUser?.surname = updateUser?.surname.toString()
if(password.value != "")
updateUser?.password = password.value
else
updateUser?.password = updateUser?.password.toString()
//updateUser?.photo =
if (updateUser != null) {
userRepository.update(updateUser)
} }
)
} }
fun isValidEmail(): Boolean { fun isValidEmail(): Boolean {

View File

@ -1,5 +1,6 @@
package com.example.myapplication.composeui.Basket package com.example.myapplication.composeui.Basket
import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -24,16 +25,19 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.example.myapplication.GlobalUser import com.example.myapplication.GlobalUser
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
import com.example.myapplication.composeui.Navbar.NavItem 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.composeui.Profile.Login
import com.example.myapplication.ui.theme.BlueMain import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn import com.example.myapplication.ui.theme.GreenBtn
@ -43,7 +47,14 @@ fun Basket(navController : NavHostController,
basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory),
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) { orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val user = GlobalUser.getInstance().getUser() 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) { if (user == null) {
Login(navController = navController) Login(navController = navController)
} else { } else {
@ -75,13 +86,15 @@ fun Basket(navController : NavHostController,
for (item in serviceList) { for (item in serviceList) {
BasketItemUI(item = item) BasketItemUI(item = item)
} }
Box(modifier = Modifier Box(
modifier = Modifier
.padding(15.dp, 0.dp) .padding(15.dp, 0.dp)
.clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp)) .clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp))
.background(Color.Transparent) .background(Color.Transparent)
.height(130.dp), .height(130.dp),
) { ) {
Column (modifier = Modifier Column(
modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(100.dp) .height(100.dp)
.background(Color.White) .background(Color.White)
@ -117,9 +130,15 @@ fun Basket(navController : NavHostController,
), ),
contentPadding = PaddingValues(0.dp), contentPadding = PaddingValues(0.dp),
) { ) {
Text(text = "Confirm order", style = MaterialTheme.typography.bodyMedium.copy(Color.White)) Text(
text = "Confirm order",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
} }
} }
} }
} }
else -> {}
}
} }

View File

@ -29,9 +29,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@ -69,9 +69,8 @@ fun BasketItemUI(item: Service, basketViewModel: BasketViewModel = viewModel(fac
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
) { ) {
item.photo?.let { painterResource(id = it) }?.let {
Image( Image(
painter = it, bitmap = item.photo.asImageBitmap(),
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
@ -79,7 +78,6 @@ fun BasketItemUI(item: Service, basketViewModel: BasketViewModel = viewModel(fac
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp), .widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight, contentScale = ContentScale.FillHeight,
) )
}
Column( Column(
modifier = Modifier modifier = Modifier

View File

@ -1,5 +1,14 @@
package com.example.myapplication.composeui.List_of_Services 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.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -11,7 +20,6 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -21,39 +29,63 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController 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.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
import com.example.myapplication.composeui.Navbar.NavItem 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.composeui.UIComponents.MyTextField
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueMain import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary import com.example.myapplication.ui.theme.TextPrimary
import kotlinx.coroutines.Dispatchers
@Composable @Composable
fun AddService (navController: NavController, service: Service, serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){ fun AddService (
val create = service.serviceId == null navController: NavController,
LaunchedEffect(Dispatchers.Default){ serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
if(!create){ ) {
serviceViewModel.service.value.serviceId = service.serviceId val context = LocalContext.current
serviceViewModel.service.value.photo = service.photo 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( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -88,50 +120,25 @@ fun AddService (navController: NavController, service: Service, serviceViewModel
.height(145.dp) .height(145.dp)
.background(color = Color.White, RoundedCornerShape(15.dp)) .background(color = Color.White, RoundedCornerShape(15.dp))
.padding(15.dp), .padding(15.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
) { ) {
if(create){
Image( Image(
painter = painterResource(id = serviceViewModel.photo.intValue), bitmap = serviceImage.value.asImageBitmap(),
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier.align(CenterVertically),
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
) )
}else{
service.photo?.let { painterResource(id = it) }?.let {
Image(
painter = it,
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp), .widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,
) { ) {
if(create){
Text( Text(
text = serviceViewModel.name.value, text = serviceViewModel.name.value,
color = TextPrimary, color = TextPrimary,
style = MaterialTheme.typography.bodyMedium) style = MaterialTheme.typography.bodyMedium
}else{ )
Text(
text = service.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
}
} }
Column( Column(
modifier = Modifier modifier = Modifier
@ -140,49 +147,34 @@ fun AddService (navController: NavController, service: Service, serviceViewModel
verticalArrangement = Arrangement.SpaceBetween, verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
if(create){
Text( Text(
text = serviceViewModel.price.doubleValue.toString(), text = serviceViewModel.price.doubleValue.toString(),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
) )
}else{
Text(
text = service.price.toString(),
style = MaterialTheme.typography.bodyMedium,
)
}
} }
} }
} }
Column( Column(
) { ) {
Row(modifier = Modifier.padding(vertical = 5.dp)) { Row(modifier = Modifier.padding(vertical = 5.dp)) {
if(create){
MyTextField(label = "Service name") { MyTextField(label = "Service name") {
serviceViewModel.name.value = it serviceViewModel.name.value = it
} }
}else{
MyTextField(label = service.name){
serviceViewModel.service.value.name = it
}
}
} }
Row(modifier = Modifier.padding(vertical = 5.dp)) { Row(modifier = Modifier.padding(vertical = 5.dp)) {
if(create){
MyTextField(label = "Price") { MyTextField(label = "Price") {
try {
serviceViewModel.price.doubleValue = it.toDouble() serviceViewModel.price.doubleValue = it.toDouble()
} }catch (e: Exception){
}else{ serviceViewModel.price.doubleValue = 0.0
MyTextField(label = service.price.toString()){ Toast.makeText(context, "Input correct price!", Toast.LENGTH_SHORT).show()
serviceViewModel.service.value.price = it.toDouble()
} }
} }
} }
} }
Button( Button(
onClick = { onClick = {
// val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) launcher.launch("image/*")
// launcher.launch(intent)
}, },
modifier = Modifier modifier = Modifier
.height(60.dp) .height(60.dp)
@ -195,15 +187,20 @@ fun AddService (navController: NavController, service: Service, serviceViewModel
), ),
contentPadding = PaddingValues(0.dp), contentPadding = PaddingValues(0.dp),
) { ) {
Text(text = "Upload image", style = MaterialTheme.typography.bodyMedium.copy(Color.White)) Text(
text = "Upload image",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
} }
Button( Button(
onClick = { onClick = {
if (create) if (serviceViewModel.name.value == ""
serviceViewModel.insertService() || serviceViewModel.price.doubleValue == 0.0)
else Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
serviceViewModel.service.let { serviceViewModel.updateService() } else {
navController.navigate(NavItem.ListOfServices.route) serviceViewModel.insertService(serviceImage.value)
navController.navigate(NavItem.AddService.route)
}
}, },
modifier = Modifier modifier = Modifier
.height(60.dp) .height(60.dp)
@ -216,11 +213,13 @@ fun AddService (navController: NavController, service: Service, serviceViewModel
), ),
contentPadding = PaddingValues(0.dp), contentPadding = PaddingValues(0.dp),
) { ) {
if(create) { Text(
Text(text = "Add service", style = MaterialTheme.typography.bodyMedium.copy(Color.White)) text = "Add service",
}else{ style = MaterialTheme.typography.bodyMedium.copy(Color.White)
Text(text = "Update 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

@ -1,6 +1,7 @@
package com.example.myapplication.composeui.List_of_Services package com.example.myapplication.composeui.List_of_Services
import SearchBar import SearchBar
import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -13,8 +14,10 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -22,8 +25,10 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey import androidx.paging.compose.itemKey
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.model.Service import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueMain import com.example.myapplication.ui.theme.BlueMain
@ -32,8 +37,20 @@ fun ListOfServices(navController: NavHostController, serviceViewModel: ServiceVi
LaunchedEffect(serviceViewModel){ LaunchedEffect(serviceViewModel){
serviceViewModel.getServiceList() serviceViewModel.getServiceList()
} }
val services = serviceViewModel.serviceList.collectAsLazyPagingItems() val services = serviceViewModel.serviceList.collectAsState().value.collectAsLazyPagingItems()
Column(modifier = Modifier.background(BlueMain).fillMaxSize().padding(bottom = 60.dp)){ 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( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
@ -49,17 +66,16 @@ fun ListOfServices(navController: NavHostController, serviceViewModel: ServiceVi
) { ) {
item { item {
SearchBar( SearchBar(
modifier = Modifier) modifier = Modifier
{ )
searchText -> { searchText ->
//TODO search logic serviceViewModel.searchServicesByFilter(searchText)
} }
} }
items( items(
count = services.itemCount, count = services.itemCount,
key = services.itemKey { service -> service.serviceId!! } key = services.itemKey { service -> service.serviceId!! }
){ ) { index: Int ->
index: Int ->
val service: Service? = services[index] val service: Service? = services[index]
if (service != null) { if (service != null) {
Service(navController, item = service) Service(navController, item = service)
@ -67,4 +83,7 @@ fun ListOfServices(navController: NavHostController, serviceViewModel: ServiceVi
} }
} }
} }
else -> {}
}
} }

View File

@ -33,9 +33,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@ -53,7 +53,12 @@ import com.google.gson.Gson
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun Service(navController: NavHostController, item: Service, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){ fun Service(
navController: NavHostController,
item: Service,
basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory),
serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val user = GlobalUser.getInstance().getUser() val user = GlobalUser.getInstance().getUser()
val basketId by basketViewModel.basketId.collectAsState() val basketId by basketViewModel.basketId.collectAsState()
LaunchedEffect(basketViewModel){ LaunchedEffect(basketViewModel){
@ -78,9 +83,8 @@ fun Service(navController: NavHostController, item: Service, basketViewModel: Ba
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
){ ){
item.photo?.let { painterResource(id = it) }?.let {
Image( Image(
painter = it, bitmap = item.photo.asImageBitmap(),
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
@ -88,7 +92,6 @@ fun Service(navController: NavHostController, item: Service, basketViewModel: Ba
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp), .widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight, contentScale = ContentScale.FillHeight,
) )
}
Column( Column(
modifier = Modifier modifier = Modifier
@ -155,8 +158,7 @@ fun Service(navController: NavHostController, item: Service, basketViewModel: Ba
if(user?.role == RoleEnum.Admin){ if(user?.role == RoleEnum.Admin){
Button( Button(
onClick = { onClick = {
serviceViewModel.service.value = item navController.navigate("change_service/${Gson().toJson(item)}")
navController.navigate("add_service/${Gson().toJson(item)}")
}, },
modifier = Modifier modifier = Modifier
.size(42.dp) .size(42.dp)

View File

@ -1,49 +1,31 @@
package com.example.myapplication.composeui.Navbar package com.example.myapplication.composeui.Navbar
import android.annotation.SuppressLint 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.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.BottomNavigation import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem 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.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ControlledComposition
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp 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.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.example.myapplication.R
import com.example.myapplication.ui.theme.BlueMain import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.BlueNavbar import com.example.myapplication.ui.theme.BlueNavbar
import com.example.myapplication.ui.theme.GreenBtn import com.example.myapplication.ui.theme.GreenBtn
@ -77,11 +59,13 @@ fun NavBar(){
icon = { icon = {
Icon(painterResource(screen.icon), Icon(painterResource(screen.icon),
null, null,
modifier = Modifier, modifier = if (isSelected){
Modifier.background(BlueMain, CircleShape).size(50.dp).padding(8.dp)
} else {
Modifier.size(50.dp).padding(8.dp)
},
GreenBtn) GreenBtn)
}, },
modifier = Modifier
.padding(15.dp),
onClick = { onClick = {
navController.navigate(screen.route){ navController.navigate(screen.route){
if (!isSelected) { if (!isSelected) {

View File

@ -7,12 +7,14 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import com.example.myapplication.composeui.Basket.Basket import com.example.myapplication.composeui.Basket.Basket
import com.example.myapplication.composeui.List_of_Services.AddService 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.List_of_Services.ListOfServices
import com.example.myapplication.composeui.Orders.Orders import com.example.myapplication.composeui.Orders.Orders
import com.example.myapplication.composeui.Profile.Login import com.example.myapplication.composeui.Profile.Login
import com.example.myapplication.composeui.Profile.Profile import com.example.myapplication.composeui.Profile.Profile
import com.example.myapplication.composeui.Profile.ProfileChange import com.example.myapplication.composeui.Profile.ProfileChange
import com.example.myapplication.composeui.Profile.Registration import com.example.myapplication.composeui.Profile.Registration
import com.example.myapplication.composeui.Profile.Report
import com.example.myapplication.model.Service import com.example.myapplication.model.Service
import com.google.gson.Gson import com.google.gson.Gson
@ -59,12 +61,22 @@ fun NavController(navController : NavHostController){
} }
composable( composable(
NavItem.AddService.route NavItem.AddService.route
){
AddService(navController)
}
composable(
NavItem.ChangeService.route
){ ){
backStackEntry -> backStackEntry ->
val serviceItemString = backStackEntry.arguments?.getString("serviceItem") val serviceItemString = backStackEntry.arguments?.getString("serviceItem")
val serviceItem = Gson().fromJson(serviceItemString, Service::class.java) val serviceItem = Gson().fromJson(serviceItemString, Service::class.java)
serviceItem?.let { AddService(navController, it) serviceItem?.let { ChangeService(it, navController)
} }
}
composable(
NavItem.Report.route
){
Report()
} }
} }
} }

View File

@ -36,7 +36,15 @@ sealed class NavItem(
R.drawable.icon_profile R.drawable.icon_profile
) )
object AddService : NavItem( object AddService : NavItem(
"add_service/{serviceItem}", "add_service",
R.drawable.icon_list_of_services 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

@ -1,31 +1,55 @@
package com.example.myapplication.composeui.Orders package com.example.myapplication.composeui.Orders
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding 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.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp 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.Order
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.first
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@Composable @Composable
fun OrderItem (order: Order){ fun OrderItem (
order: Order,
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val dateFormat = SimpleDateFormat("dd-MM-yyyy") 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 ( Column (
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(10.dp) .padding(10.dp)
.clip(RoundedCornerShape(15.dp)) .clip(RoundedCornerShape(15.dp))
.clickable {
expanded.value = !expanded.value
}
){ ){
Row ( Row (
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
@ -45,5 +69,12 @@ fun OrderItem (order: Order){
){ ){
Text(text = dateFormat.format(Date(order.date)), style = MaterialTheme.typography.bodyMedium) Text(text = dateFormat.format(Date(order.date)), style = MaterialTheme.typography.bodyMedium)
} }
if(expanded.value){
LazyRow(){
itemsIndexed(services ?: emptyList()) { index, service ->
OrderScrollService(service)
}
}
}
} }
} }

View File

@ -22,7 +22,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@Composable @Composable
fun Orders (navController: NavController, orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)){ fun Orders (
navController: NavController,
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val user = GlobalUser.getInstance().getUser() val user = GlobalUser.getInstance().getUser()
val ordersList by orderViewModel.orders.collectAsState() val ordersList by orderViewModel.orders.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {

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

@ -1,6 +1,5 @@
package com.example.myapplication.composeui.Profile package com.example.myapplication.composeui.Profile
import android.view.Gravity
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -27,26 +26,43 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController 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.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
import com.example.myapplication.businessLogic.viewmodel.UserViewModel import com.example.myapplication.businessLogic.viewmodel.UserViewModel
import com.example.myapplication.composeui.Navbar.NavItem 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.composeui.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary import com.example.myapplication.ui.theme.TextSecondary
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable @Composable
fun Login (navController: NavController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory), basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory)){ fun Login (
navController: NavController,
userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val context = LocalContext.current 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( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -70,9 +86,6 @@ fun Login (navController: NavController, userViewModel: UserViewModel = viewMode
} }
Column ( Column (
){ ){
var isEmailValid by remember { mutableStateOf(true)}
var isPasswordValid by remember { mutableStateOf(true)}
if (!isEmailValid) { if (!isEmailValid) {
Text( Text(
text = "Invalid email format", text = "Invalid email format",
@ -98,7 +111,7 @@ fun Login (navController: NavController, userViewModel: UserViewModel = viewMode
Row (modifier = Modifier Row (modifier = Modifier
.padding(vertical = 5.dp) .padding(vertical = 5.dp)
){ ){
MyTextField(label = "Password", onValueChanged = { MyTextField(label = "Password", visualTransformation = PasswordVisualTransformation(), onValueChanged = {
userViewModel.password.value = it userViewModel.password.value = it
isPasswordValid = it.isNotEmpty() isPasswordValid = it.isNotEmpty()
}) })
@ -106,13 +119,14 @@ fun Login (navController: NavController, userViewModel: UserViewModel = viewMode
} }
Button( Button(
onClick = { onClick = {
CoroutineScope(Dispatchers.Main).launch { if (userViewModel.email.value == ""
userViewModel.authUser() || !isEmailValid
navController.navigate(NavItem.Profile.route) || !isPasswordValid){
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
}
else{
userViewModel.authUser()
} }
val toast = Toast.makeText(context, "message", Toast.LENGTH_SHORT)
toast.setGravity(Gravity.TOP, 0, 100)
toast.show()
}, },
modifier = Modifier modifier = Modifier
.height(60.dp) .height(60.dp)
@ -150,4 +164,8 @@ fun Login (navController: NavController, userViewModel: UserViewModel = viewMode
) )
} }
} }
}
} }

View File

@ -1,5 +1,6 @@
package com.example.myapplication.composeui.Profile package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -24,6 +25,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
@ -66,7 +68,11 @@ fun Profile(navController: NavHostController){
.size(200.dp) .size(200.dp)
.background(Color.White) .background(Color.White)
){ ){
// TODO: upload profile image Image(
bitmap = user.photo.asImageBitmap(),
contentDescription = null,
modifier = Modifier.align(Alignment.Center),
)
} }
Box(modifier = Modifier.padding(15.dp)){ Box(modifier = Modifier.padding(15.dp)){
Text( Text(
@ -129,7 +135,7 @@ fun Profile(navController: NavHostController){
} }
if(user.role == RoleEnum.Admin){ if(user.role == RoleEnum.Admin){
Button( Button(
onClick = { navController.navigate("add_service/{}") }, onClick = { navController.navigate(NavItem.AddService.route) },
modifier = Modifier modifier = Modifier
.height(60.dp) .height(60.dp)
.fillMaxWidth() .fillMaxWidth()
@ -147,6 +153,25 @@ fun Profile(navController: NavHostController){
color = Color.White 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( Button(
onClick = { onClick = {

View File

@ -1,20 +1,32 @@
package com.example.myapplication.composeui.Profile 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.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape 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.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -26,7 +38,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
@ -34,18 +47,46 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.example.myapplication.GlobalUser import com.example.myapplication.GlobalUser
import com.example.myapplication.R import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.UserViewModel import com.example.myapplication.businessLogic.viewmodel.UserViewModel
import com.example.myapplication.composeui.Navbar.NavItem import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.UIComponents.MyTextField 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.BlueMain
import com.example.myapplication.ui.theme.GreenBtn import com.example.myapplication.ui.theme.GreenBtn
@Composable @Composable
fun ProfileChange (navController: NavHostController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ fun ProfileChange (navController: NavHostController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val user = GlobalUser.getInstance().getUser() val user = GlobalUser.getInstance().getUser()
Column ( 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 modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(BlueMain) .background(BlueMain)
@ -70,16 +111,15 @@ fun ProfileChange (navController: NavHostController, userViewModel: UserViewMode
.background(Color.White) .background(Color.White)
.padding(PaddingValues(0.dp)) .padding(PaddingValues(0.dp))
){ ){
Icon( Image(
painterResource(id = R.drawable.upload), bitmap = profileImage.value.asImageBitmap(),
contentDescription = null, contentDescription = null,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
GreenBtn
) )
} }
Box(modifier = Modifier.padding(15.dp)){ Box(modifier = Modifier.padding(15.dp)){
Text( Text(
text = "${user?.name} ${user?.surname}", text = "${name.value} ${surname.value}",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -89,7 +129,7 @@ fun ProfileChange (navController: NavHostController, userViewModel: UserViewMode
} }
Box(modifier = Modifier.padding(15.dp)){ Box(modifier = Modifier.padding(15.dp)){
Text( Text(
text = "${user?.email}", text = "${email.value}",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -99,15 +139,51 @@ fun ProfileChange (navController: NavHostController, userViewModel: UserViewMode
} }
Column ( Column (
){ ){
Row (modifier = Modifier.padding(vertical = 5.dp)){ Spacer(modifier = Modifier.size(5.dp))
MyTextField(label = "Name", onValueChanged = {userViewModel.name.value = it}) 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,
)
} }
Row (modifier = Modifier.padding(vertical = 5.dp)){ Spacer(modifier = Modifier.size(5.dp))
MyTextField(label = "Surname", onValueChanged = {userViewModel.surname.value = it}) 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,
)
} }
var isEmailValid by remember { mutableStateOf(true) } Spacer(modifier = Modifier.size(5.dp))
var isPasswordValid by remember { mutableStateOf(true) }
if (!isEmailValid) { if (!isEmailValid) {
Text( Text(
text = "Invalid email format", text = "Invalid email format",
@ -115,14 +191,29 @@ fun ProfileChange (navController: NavHostController, userViewModel: UserViewMode
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em)) .copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
) )
} }
Row (modifier = Modifier Row (
.padding(vertical = 5.dp) 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),
){ ){
MyTextField(label = "Email", onValueChanged = { BasicTextField(
userViewModel.email.value = it value = email.value!!,
isEmailValid = userViewModel.isValidEmail() 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) { if (!isPasswordValid) {
Text( Text(
text = "Password is required", text = "Password is required",
@ -130,21 +221,74 @@ fun ProfileChange (navController: NavHostController, userViewModel: UserViewMode
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em)) .copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
) )
} }
Row (modifier = Modifier.padding(vertical = 5.dp)){ Row (
MyTextField(label = "Password", onValueChanged = { verticalAlignment = Alignment.CenterVertically,
userViewModel.password.value = it 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() isPasswordValid = it.isNotEmpty()
}) },
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
} }
} }
Spacer(modifier = Modifier.size(5.dp))
Button( Button(
onClick = { onClick = {
userViewModel.updateUser() launcher.launch("image/*")
navController.navigate(NavItem.Profile.route)
}, },
modifier = Modifier modifier = Modifier
.height(60.dp) .height(50.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)
)
}
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() .fillMaxWidth()
.clip(CircleShape), .clip(CircleShape),
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
@ -156,4 +300,10 @@ fun ProfileChange (navController: NavHostController, userViewModel: UserViewMode
Text(text = "Confirm changes", style = MaterialTheme.typography.bodyMedium.copy(Color.White)) 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

@ -1,5 +1,7 @@
package com.example.myapplication.composeui.Profile package com.example.myapplication.composeui.Profile
import android.graphics.BitmapFactory
import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -24,21 +26,38 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color 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.TextUnit
import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController 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.Navbar.NavItem
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.composeui.UIComponents.MyTextField import com.example.myapplication.composeui.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary import com.example.myapplication.ui.theme.TextSecondary
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
@Composable @Composable
fun Registration (navController: NavController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ 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( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -68,9 +87,6 @@ fun Registration (navController: NavController, userViewModel: UserViewModel = v
Row (modifier = Modifier.padding(vertical = 5.dp)){ Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Surname", onValueChanged = { userViewModel.surname.value = it }) MyTextField(label = "Surname", onValueChanged = { userViewModel.surname.value = it })
} }
var isEmailValid by remember { mutableStateOf(true) }
var isPasswordValid by remember { mutableStateOf(true) }
if (!isEmailValid) { if (!isEmailValid) {
Text( Text(
text = "Invalid email format", text = "Invalid email format",
@ -93,10 +109,11 @@ fun Registration (navController: NavController, userViewModel: UserViewModel = v
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em)) .copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
) )
} }
Row (modifier = Modifier Row (modifier = Modifier
.padding(vertical = 5.dp) .padding(vertical = 5.dp)
){ ){
MyTextField(label = "Password", onValueChanged = { MyTextField(label = "Password", visualTransformation = PasswordVisualTransformation(), onValueChanged = {
userViewModel.password.value = it userViewModel.password.value = it
isPasswordValid = it.isNotEmpty() isPasswordValid = it.isNotEmpty()
}) })
@ -104,8 +121,16 @@ fun Registration (navController: NavController, userViewModel: UserViewModel = v
} }
Button( Button(
onClick = { onClick = {
userViewModel.createUser() 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) navController.navigate(NavItem.Login.route)
}
}, },
modifier = Modifier modifier = Modifier
.height(60.dp) .height(60.dp)
@ -141,4 +166,7 @@ fun Registration (navController: NavController, userViewModel: UserViewModel = v
) )
} }
} }
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

@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.BlueBorder import com.example.myapplication.ui.theme.BlueBorder
import com.example.myapplication.ui.theme.TextSecondary import com.example.myapplication.ui.theme.TextSecondary
@ -28,6 +29,7 @@ import com.example.myapplication.ui.theme.TextSecondary
@Composable @Composable
fun MyTextField ( fun MyTextField (
label: String, label: String,
visualTransformation: VisualTransformation = VisualTransformation.None,
onValueChanged: (String) -> Unit, onValueChanged: (String) -> Unit,
){ ){
val textState = remember { mutableStateOf(TextFieldValue()) } val textState = remember { mutableStateOf(TextFieldValue()) }
@ -48,8 +50,6 @@ fun MyTextField (
text = label, text = label,
style = MaterialTheme.typography.bodyMedium.copy(color = TextSecondary), style = MaterialTheme.typography.bodyMedium.copy(color = TextSecondary),
) )
}else{
} }
BasicTextField( BasicTextField(
value = text, value = text,
@ -57,6 +57,7 @@ fun MyTextField (
textState.value = it textState.value = it
onValueChanged(it.text) onValueChanged(it.text)
}, },
visualTransformation = visualTransformation,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium, textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true, singleLine = true,

View File

@ -4,6 +4,7 @@ import android.content.Context
import androidx.room.Database import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.database.dao.BasketDao import com.example.myapplication.database.dao.BasketDao
import com.example.myapplication.database.dao.OrderDao import com.example.myapplication.database.dao.OrderDao
@ -12,6 +13,7 @@ import com.example.myapplication.database.dao.ServiceDao
import com.example.myapplication.database.dao.UserDao import com.example.myapplication.database.dao.UserDao
import com.example.myapplication.model.Basket import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Converters
import com.example.myapplication.model.Order import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService import com.example.myapplication.model.OrderService
import com.example.myapplication.model.RemoteKeys import com.example.myapplication.model.RemoteKeys
@ -21,7 +23,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Database(entities = [User::class, Service::class, Order::class, OrderService::class, Basket::class, BasketService::class, RemoteKeys::class], version = 10) @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 class AppDatabase : RoomDatabase(){
abstract fun serviceDao(): ServiceDao abstract fun serviceDao(): ServiceDao
abstract fun userDao(): UserDao abstract fun userDao(): UserDao
@ -36,15 +45,7 @@ abstract class AppDatabase : RoomDatabase(){
private var INSTANCE: AppDatabase? = null private var INSTANCE: AppDatabase? = null
suspend fun populateDatabase() { suspend fun populateDatabase() {
// INSTANCE?.let { database ->
// // User
// val userDao = database.userDao()
// val user1 = User(null, "Danil", "Markov", "danil@mail.ru", "123", RoleEnum.Admin)
// userDao.insert(user1)
// val basketDao = database.basketDao()
// val basket1 = Basket(null, user1.userId!!)
// basketDao.insert(basket1)
// }
} }
fun getInstance(appContext: Context): AppDatabase { fun getInstance(appContext: Context): AppDatabase {

View File

@ -26,4 +26,6 @@ interface ServiceDao {
suspend fun deleteAll() suspend fun deleteAll()
@Query("DELETE FROM tbl_service WHERE serviceId = :id") @Query("DELETE FROM tbl_service WHERE serviceId = :id")
suspend fun invalidateService(id: Int) 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

@ -28,4 +28,14 @@ class ServiceRepositoryImpl(private val serviceDao: ServiceDao): ServiceReposito
serviceDao.insert(*services.toTypedArray()) serviceDao.insert(*services.toTypedArray())
fun getAllServicesPagingSource(): PagingSource<Int, Service> = serviceDao.getAll() fun getAllServicesPagingSource(): PagingSource<Int, Service> = serviceDao.getAll()
suspend fun invalidateService(id: Int) = serviceDao.invalidateService(id) 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

@ -2,6 +2,7 @@ package com.example.myapplication.di
import com.example.myapplication.businessLogic.repository.BasketRepository import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.OrderRepository 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.ServiceRepository
import com.example.myapplication.businessLogic.repository.UserRepository import com.example.myapplication.businessLogic.repository.UserRepository
@ -10,6 +11,7 @@ interface AppContainer {
val userRepo: UserRepository val userRepo: UserRepository
val orderRepo: OrderRepository val orderRepo: OrderRepository
val basketRepo: BasketRepository val basketRepo: BasketRepository
val reportRepo: ReportRepository
companion object { companion object {
const val TIMEOUT = 5000L const val TIMEOUT = 5000L

View File

@ -4,10 +4,12 @@ import android.content.Context
import com.example.myapplication.api.ServerService import com.example.myapplication.api.ServerService
import com.example.myapplication.api.repository.RestBasketRepository import com.example.myapplication.api.repository.RestBasketRepository
import com.example.myapplication.api.repository.RestOrderRepository 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.RestServiceRepository
import com.example.myapplication.api.repository.RestUserRepository import com.example.myapplication.api.repository.RestUserRepository
import com.example.myapplication.businessLogic.repository.BasketRepository import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.OrderRepository 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.ServiceRepository
import com.example.myapplication.businessLogic.repository.UserRepository import com.example.myapplication.businessLogic.repository.UserRepository
import com.example.myapplication.database.AppDatabase import com.example.myapplication.database.AppDatabase
@ -32,6 +34,9 @@ class AppDataContainer(context: Context) : AppContainer {
override val basketRepo: BasketRepository by lazy{ override val basketRepo: BasketRepository by lazy{
RestBasketRepository(ServerService.getInstance()) RestBasketRepository(ServerService.getInstance())
} }
override val reportRepo: ReportRepository by lazy{
RestReportRepository(ServerService.getInstance())
}
private val serviceRepository: ServiceRepositoryImpl by lazy { private val serviceRepository: ServiceRepositoryImpl by lazy {
ServiceRepositoryImpl(AppDatabase.getInstance(context).serviceDao()) ServiceRepositoryImpl(AppDatabase.getInstance(context).serviceDao())

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

@ -1,5 +1,6 @@
package com.example.myapplication.model package com.example.myapplication.model
import android.graphics.Bitmap
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@ -9,5 +10,5 @@ data class Service (
var serviceId: Int? = null, var serviceId: Int? = null,
var name: String, var name: String,
var price: Double, var price: Double,
var photo: Int? = null var photo: Bitmap
) )

View File

@ -1,5 +1,6 @@
package com.example.myapplication.model package com.example.myapplication.model
import android.graphics.Bitmap
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@ -12,6 +13,6 @@ data class User(
var email: String, var email: String,
var password: String, var password: String,
val role: RoleEnum, val role: RoleEnum,
val photo: Int? = null, var photo: Bitmap,
val basketId: Int? = null val basketId: Int? = null
) )

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="app_name">PetMed</string>
<string name="not_auth_error">Please login</string> <string name="not_auth_error">Please login</string>
<string name="placeholder_search">Search…</string> <string name="placeholder_search">Search…</string>
<string name="loading">Loading...</string>
<string name="back">Return</string>
</resources> </resources>