feature: coursework completed
This commit is contained in:
parent
b58bbf4421
commit
35a3213edd
@ -9,20 +9,68 @@
|
||||
<option name="autoReloadType" value="NONE" />
|
||||
</component>
|
||||
<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$/app/src/main/java/com/example/myapplication/MainActivity.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/MainActivity.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/ServerService.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/ServerService.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/ServiceRemote.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/ServiceRemote.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/UserRemote.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/UserRemote.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestServiceRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestServiceRepository.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestUserRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestUserRepository.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/repository/ServiceRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/repository/ServiceRepository.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/AppViewModelProvider.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/AppViewModelProvider.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/BasketViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/BasketViewModel.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/OrderViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/OrderViewModel.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/ServiceViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/ServiceViewModel.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/UserViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/UserViewModel.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/Basket.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/Basket.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/BasketItemUI.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/BasketItemUI.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/AddService.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/AddService.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/ListOfServices.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/ListOfServices.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/Service.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/Service.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavBar.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavBar.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavItem.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavItem.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/OrderItem.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/OrderItem.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/Orders.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/Orders.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Login.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Login.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Profile.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Profile.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ProfileChange.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ProfileChange.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Registration.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Registration.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/UIComponents/MyTextField.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/UIComponents/MyTextField.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/AppDatabase.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/AppDatabase.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/dao/ServiceDao.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/dao/ServiceDao.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/repository/ServiceRepositoryImpl.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/repository/ServiceRepositoryImpl.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppContainer.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppContainer.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppDataContainer.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppDataContainer.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Service.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Service.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/User.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/User.kt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/src/main/res/values/strings.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/res/values/strings.xml" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[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">
|
||||
<projectState path="$PROJECT_DIR$">
|
||||
<ProjectState />
|
||||
@ -46,7 +94,7 @@
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$" value="LabWork04" />
|
||||
<entry key="$PROJECT_DIR$" value="LabWork05" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
@ -72,12 +120,12 @@
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"ToolWindowLogcat.ShowToolbar": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"com.android.tools.idea.devicemanager.tab": "Physical",
|
||||
"last_opened_file_path": "C:/Users/Danil/Desktop/Новая папка",
|
||||
"project.structure.last.edited": "Modules",
|
||||
"com.google.services.firebase.aqiPopupShown": "true",
|
||||
"last_opened_file_path": "C:/Users/Danil/Desktop/MDP/labs/app/src/main/java/com/example/myapplication/composeui/Orders",
|
||||
"project.structure.last.edited": "Project",
|
||||
"project.structure.proportion": "0.17",
|
||||
"project.structure.side.proportion": "0.2",
|
||||
"settings.editor.selected.configurable": "emulator"
|
||||
"settings.editor.selected.configurable": "preferences.lookFeel"
|
||||
},
|
||||
"keyToStringList": {
|
||||
"ExportApk.BuildVariants": [
|
||||
@ -93,7 +141,7 @@
|
||||
}
|
||||
}</component>
|
||||
<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_BUILD_TYPE" value="release" />
|
||||
</component>
|
||||
@ -101,6 +149,10 @@
|
||||
<key name="android.template.-1413981578">
|
||||
<recent name="com.example.myapplication.Navbar" />
|
||||
</key>
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\java\com\example\myapplication\composeui\Orders" />
|
||||
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\res\drawable" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\java\com\example\myapplication\business_logic" />
|
||||
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\res\drawable" />
|
||||
@ -338,7 +390,14 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1703256177985</updated>
|
||||
</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 />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
@ -361,25 +420,16 @@
|
||||
<MESSAGE value="feature: lab4 maybe done" />
|
||||
<MESSAGE value="feature: lab5 almost done, save commit" />
|
||||
<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 name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" type="kotlin-line">
|
||||
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt</url>
|
||||
<line>63</line>
|
||||
<option name="timeStamp" value="23" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="kotlin-line">
|
||||
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/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" />
|
||||
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/UserViewModel.kt</url>
|
||||
<line>49</line>
|
||||
<option name="timeStamp" value="105" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
|
@ -15,10 +15,10 @@ import com.example.myapplication.ui.theme.BlueMain
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
//this.deleteDatabase("my-db")
|
||||
setContent {
|
||||
AppTheme (darkTheme = false){
|
||||
// A surface container using the 'background' color from the theme
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
@ -0,0 +1,3 @@
|
||||
package com.example.myapplication.api
|
||||
|
||||
enum class ApiStatus { LOADING, ERROR, DONE }
|
@ -3,6 +3,7 @@ package com.example.myapplication.api
|
||||
import com.example.myapplication.api.model.BasketServiceRemote
|
||||
import com.example.myapplication.api.model.OrderRemote
|
||||
import com.example.myapplication.api.model.OrderServiceRemote
|
||||
import com.example.myapplication.api.model.ReportRemote
|
||||
import com.example.myapplication.api.model.ServiceRemote
|
||||
import com.example.myapplication.api.model.UserRemote
|
||||
import com.example.myapplication.api.model.UserRemoteSignIn
|
||||
@ -10,6 +11,8 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
@ -20,7 +23,7 @@ import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface ServerService {
|
||||
//SNEAKER
|
||||
// Service
|
||||
@GET("service/get/{id}")
|
||||
suspend fun getService(
|
||||
@Path("id") id: Int,
|
||||
@ -46,7 +49,7 @@ interface ServerService {
|
||||
@DELETE("service/delete/{id}")
|
||||
suspend fun deleteService(
|
||||
@Path("id") id: Int
|
||||
)
|
||||
): Response<String>
|
||||
|
||||
//USER
|
||||
@POST("user/signup")
|
||||
@ -59,6 +62,11 @@ interface ServerService {
|
||||
@Body user: UserRemoteSignIn
|
||||
): UserRemote
|
||||
|
||||
@PUT("user/update")
|
||||
suspend fun updateUser(
|
||||
@Body user: UserRemote
|
||||
): UserRemote
|
||||
|
||||
//BASKET
|
||||
@POST("basket/addServiceToBasket")
|
||||
suspend fun addServiceToBasket(
|
||||
@ -140,6 +148,14 @@ interface ServerService {
|
||||
suspend fun deleteOrder(
|
||||
@Path("orderId") orderId: Int
|
||||
)
|
||||
|
||||
//REPORT
|
||||
@GET("report/getReport/{dateFrom}/{dateTo}")
|
||||
suspend fun getReport(
|
||||
@Path("dateFrom") dateFrom: Long,
|
||||
@Path("dateTo") dateTo: Long
|
||||
): ReportRemote
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL = "https://ftkfjb1l-8080.euw.devtunnels.ms/api/"
|
||||
|
||||
@ -148,7 +164,10 @@ interface ServerService {
|
||||
|
||||
fun getInstance(): ServerService {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val logger = HttpLoggingInterceptor()
|
||||
logger.level = HttpLoggingInterceptor.Level.BASIC
|
||||
val client = OkHttpClient.Builder()
|
||||
.addInterceptor(logger)
|
||||
.build()
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
)
|
@ -8,19 +8,19 @@ data class ServiceRemote (
|
||||
val id: Int? = 0,
|
||||
val name: String = "",
|
||||
val price: Double = 0.0,
|
||||
val photo: Int? = 0
|
||||
val photo: String = ""
|
||||
)
|
||||
|
||||
fun ServiceRemote.toService(): Service = Service(
|
||||
id,
|
||||
name,
|
||||
price,
|
||||
photo
|
||||
RemoteConverters.toBitmap(photo)
|
||||
)
|
||||
|
||||
fun Service.toServiceRemote():ServiceRemote = ServiceRemote(
|
||||
serviceId,
|
||||
name,
|
||||
price,
|
||||
photo
|
||||
RemoteConverters.fromBitmap(photo)
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.example.myapplication.api.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ServiceWithCount(
|
||||
val service: ServiceRemote,
|
||||
val quantity: Int
|
||||
)
|
@ -12,7 +12,7 @@ data class UserRemote (
|
||||
val email: String = "",
|
||||
val password: String = "",
|
||||
val role: RoleEnum = RoleEnum.User,
|
||||
val photo: Int? = 0,
|
||||
val photo: String = "",
|
||||
val basketId: Int? = 0
|
||||
)
|
||||
|
||||
@ -24,7 +24,7 @@ fun UserRemote.toUser(): User = User(
|
||||
email,
|
||||
password,
|
||||
role,
|
||||
photo,
|
||||
RemoteConverters.toBitmap(photo),
|
||||
basketId
|
||||
)
|
||||
|
||||
@ -35,6 +35,6 @@ fun User.toUserRemote():UserRemote = UserRemote(
|
||||
email,
|
||||
password,
|
||||
role,
|
||||
photo,
|
||||
RemoteConverters.fromBitmap(photo),
|
||||
basketId
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -60,4 +60,9 @@ class RestServiceRepository(
|
||||
dbServiceRepository.invalidateService(service.serviceId!!)
|
||||
}catch (ex: Exception){}
|
||||
}
|
||||
|
||||
override fun call(str: String): Flow<PagingData<Service>> {
|
||||
return dbServiceRepository.call(str)
|
||||
}
|
||||
|
||||
}
|
@ -15,7 +15,7 @@ class RestUserRepository(
|
||||
}
|
||||
|
||||
override suspend fun update(user: User) {
|
||||
println()
|
||||
service.updateUser(user.toUserRemote())
|
||||
}
|
||||
|
||||
override suspend fun delete(user: User) {
|
||||
|
@ -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
|
||||
}
|
@ -10,4 +10,5 @@ interface ServiceRepository {
|
||||
suspend fun delete(service: Service)
|
||||
suspend fun getServiceById(id: Int): Service
|
||||
suspend fun getAllServices(): Flow<PagingData<Service>>
|
||||
fun call(str: String): Flow<PagingData<Service>>
|
||||
}
|
@ -12,13 +12,25 @@ object AppViewModelProvider {
|
||||
ServiceViewModel(app().container.serviceRepo)
|
||||
}
|
||||
initializer {
|
||||
UserViewModel(app().container.userRepo, app().container.basketRepo)
|
||||
UserViewModel(
|
||||
app().container.userRepo,
|
||||
app().container.basketRepo
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
OrderViewModel(app().container.orderRepo, app().container.basketRepo)
|
||||
OrderViewModel(
|
||||
app().container.orderRepo,
|
||||
app().container.basketRepo
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
BasketViewModel(app().container.basketRepo, app().container.orderRepo,)
|
||||
BasketViewModel(
|
||||
app().container.basketRepo,
|
||||
app().container.orderRepo,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
ReportViewModel(app().container.reportRepo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.example.myapplication.businessLogic.viewmodel
|
||||
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableDoubleStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.myapplication.GlobalUser
|
||||
import com.example.myapplication.businessLogic.repository.BasketRepository
|
||||
@ -17,9 +16,10 @@ import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
data class ServiceWithQuantity(val service: Service, val quantityStateFlow: MutableStateFlow<Int>)
|
||||
|
||||
class BasketViewModel(private val basketRepository: BasketRepository, private val orderRepository: OrderRepository) : ViewModel() {
|
||||
class BasketViewModel(
|
||||
private val basketRepository: BasketRepository,
|
||||
private val orderRepository: OrderRepository
|
||||
) : MyViewModel() {
|
||||
private val _quantityMap = MutableStateFlow<Map<Int, MutableStateFlow<Int>>>(emptyMap())
|
||||
val quantityMap: StateFlow<Map<Int, MutableStateFlow<Int>>> get() = _quantityMap
|
||||
private val _total = mutableDoubleStateOf(0.00)
|
||||
@ -28,6 +28,7 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
|
||||
val myList: StateFlow<List<Service>> get() = _myList
|
||||
private var _basketId = MutableStateFlow(0)
|
||||
val basketId: StateFlow<Int> get() = _basketId
|
||||
|
||||
fun getQuantityState(basketId: Int, serviceId: Int): Flow<Int> {
|
||||
val quantityMap = _quantityMap.value.toMutableMap()
|
||||
val quantityStateFlow = quantityMap.getOrPut(serviceId) {
|
||||
@ -59,14 +60,13 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
|
||||
}
|
||||
}
|
||||
|
||||
fun getBasketServices() {
|
||||
viewModelScope.launch {
|
||||
val basketId = GlobalUser.getInstance().getUser()?.basketId!!
|
||||
basketRepository.getBasketWithServices(basketId)
|
||||
.collect{services ->
|
||||
_myList.value = services
|
||||
}
|
||||
}
|
||||
fun getBasketServices() = viewModelScope.launch {
|
||||
val userId = GlobalUser.getInstance().getUser()?.userId!!
|
||||
|
||||
basketRepository.getBasketWithServices(userId)
|
||||
.collect{services ->
|
||||
_myList.value = services
|
||||
}
|
||||
}
|
||||
|
||||
fun getUsersBasket(id: Int) {
|
||||
@ -91,34 +91,42 @@ class BasketViewModel(private val basketRepository: BasketRepository, private va
|
||||
}
|
||||
|
||||
fun incrementServiceQuantity(basketId: Int, serviceId: Int) {
|
||||
viewModelScope.launch {
|
||||
basketRepository.incrementServiceQuantity(basketId, serviceId)
|
||||
val currentQuantity = getQuantityState(basketId, serviceId).first()
|
||||
_quantityMap.value.toMutableMap().apply {
|
||||
put(serviceId, MutableStateFlow(currentQuantity + 1))
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
basketRepository.incrementServiceQuantity(basketId, serviceId)
|
||||
}
|
||||
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
|
||||
)
|
||||
var currentQuantity: Int = 0
|
||||
viewModelScope.launch {
|
||||
currentQuantity = getQuantityState(basketId, serviceId).first()
|
||||
}
|
||||
_quantityMap.value.toMutableMap().apply {
|
||||
put(serviceId, MutableStateFlow(currentQuantity + 1))
|
||||
}
|
||||
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
|
||||
}
|
||||
|
||||
fun decrementOrRemoveServiceQuantity(basketId: Int, serviceId: Int) {
|
||||
viewModelScope.launch {
|
||||
val currentQuantity = getQuantityState(basketId, serviceId).first()
|
||||
if (currentQuantity > 1) {
|
||||
basketRepository.decrementServiceQuantity(basketId, serviceId)
|
||||
_quantityMap.value.toMutableMap().apply {
|
||||
put(serviceId, MutableStateFlow(currentQuantity - 1))
|
||||
}
|
||||
fun decrementOrRemoveServiceQuantity(basketId: Int, serviceId: Int) = viewModelScope.launch{
|
||||
val currentQuantity = getQuantityState(basketId, serviceId).first()
|
||||
|
||||
if (currentQuantity > 1) {
|
||||
runInScope(
|
||||
actionSuccess = { basketRepository.decrementServiceQuantity(basketId, serviceId) }
|
||||
)
|
||||
_quantityMap.value.toMutableMap().apply {
|
||||
put(serviceId, MutableStateFlow(currentQuantity - 1))
|
||||
}
|
||||
else{
|
||||
basketRepository.removeServiceFromBasket(basketId, serviceId)
|
||||
_quantityMap.value.toMutableMap().apply {
|
||||
remove(serviceId, MutableStateFlow(currentQuantity - 1))
|
||||
}
|
||||
getBasketServices()
|
||||
}
|
||||
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
|
||||
}
|
||||
else{
|
||||
runInScope(
|
||||
actionSuccess = { basketRepository.removeServiceFromBasket(basketId, serviceId) }
|
||||
)
|
||||
_quantityMap.value.toMutableMap().apply {
|
||||
remove(serviceId, MutableStateFlow(currentQuantity - 1))
|
||||
}
|
||||
getBasketServices()
|
||||
}
|
||||
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
|
||||
}
|
||||
|
||||
fun updateSubTotal(userId: Int) {
|
||||
|
@ -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 = {})
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package com.example.myapplication.businessLogic.viewmodel
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableDoubleStateOf
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.myapplication.GlobalUser
|
||||
import com.example.myapplication.businessLogic.repository.BasketRepository
|
||||
@ -13,10 +12,14 @@ import com.example.myapplication.model.Service
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Date
|
||||
|
||||
class OrderViewModel(private val orderRepository: OrderRepository, private val basketRepository: BasketRepository) : ViewModel() {
|
||||
class OrderViewModel(
|
||||
private val orderRepository: OrderRepository,
|
||||
private val basketRepository: BasketRepository
|
||||
) : MyViewModel() {
|
||||
private var _selectedItems = MutableLiveData<List<Service>>()
|
||||
val selectedItems get() = _selectedItems
|
||||
private val _total = mutableDoubleStateOf(0.00)
|
||||
@ -31,22 +34,32 @@ class OrderViewModel(private val orderRepository: OrderRepository, private val b
|
||||
total = getTotal(userId),
|
||||
creatorUserId = userId
|
||||
)
|
||||
orderRepository.insert(order)
|
||||
runInScope(
|
||||
actionSuccess = { orderRepository.insert(order) }
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
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) {
|
||||
viewModelScope.launch {
|
||||
orderRepository.getUserOrders(id)
|
||||
.collect{items ->
|
||||
_orders.value = items
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
viewModelScope.launch {
|
||||
orderRepository.getUserOrders(id)
|
||||
.collect{items ->
|
||||
_orders.value = items
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun updateSelectedItems(items: List<Service>) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +1,75 @@
|
||||
package com.example.myapplication.businessLogic.viewmodel
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.runtime.mutableDoubleStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
import com.example.myapplication.R
|
||||
import androidx.paging.cachedIn
|
||||
import com.example.myapplication.businessLogic.repository.ServiceRepository
|
||||
import com.example.myapplication.model.Service
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ServiceViewModel(private val serviceRepository: ServiceRepository): ViewModel() {
|
||||
class ServiceViewModel(
|
||||
private val serviceRepository: ServiceRepository
|
||||
): MyViewModel() {
|
||||
var name = mutableStateOf("")
|
||||
var price = mutableDoubleStateOf(0.00)
|
||||
var photo = mutableIntStateOf(R.drawable.image_service)
|
||||
var service = mutableStateOf<Service>(Service(null,"", 0.0, null))
|
||||
private val _serviceList = MutableStateFlow<PagingData<Service>>(PagingData.empty())
|
||||
val serviceList: StateFlow<PagingData<Service>> get() = _serviceList
|
||||
fun insertService() = viewModelScope.launch {
|
||||
private val _serviceList = MutableStateFlow<Flow<PagingData<Service>>>(emptyFlow())
|
||||
val serviceList: StateFlow<Flow<PagingData<Service>>> get() = _serviceList
|
||||
|
||||
init {
|
||||
runInScope(
|
||||
actionSuccess = { getServiceList() }
|
||||
)
|
||||
}
|
||||
|
||||
fun insertService(photo: Bitmap) {
|
||||
val service = Service(
|
||||
name = name.value,
|
||||
price = price.doubleValue,
|
||||
photo = photo.intValue
|
||||
photo = photo
|
||||
)
|
||||
runInScope(
|
||||
actionSuccess = { serviceRepository.insert(service) }
|
||||
)
|
||||
serviceRepository.insert(service)
|
||||
}
|
||||
|
||||
fun deleteService(service : Service) = viewModelScope.launch {
|
||||
serviceRepository.delete(service)
|
||||
fun deleteService(service : Service) {
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
serviceRepository.delete(service)
|
||||
getServiceList()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun updateService() = viewModelScope.launch {
|
||||
serviceRepository.update(service.value)
|
||||
fun updateService(service: Service) {
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
serviceRepository.update(service)
|
||||
getServiceList()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun getServiceList(){
|
||||
try{
|
||||
viewModelScope.launch{
|
||||
_serviceList.value = serviceRepository.getAllServices()
|
||||
}
|
||||
}catch(e: Exception){}
|
||||
|
||||
}
|
||||
|
||||
fun searchServicesByFilter(searchText: String){
|
||||
viewModelScope.launch {
|
||||
serviceRepository.getAllServices()
|
||||
.collect{services ->
|
||||
_serviceList.value = services
|
||||
}
|
||||
val filteredServices = serviceRepository.call(searchText).cachedIn(viewModelScope)
|
||||
_serviceList.value = filteredServices
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +1,55 @@
|
||||
package com.example.myapplication.businessLogic.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import android.graphics.Bitmap
|
||||
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.R
|
||||
import com.example.myapplication.api.model.UserRemoteSignIn
|
||||
import com.example.myapplication.businessLogic.repository.BasketRepository
|
||||
import com.example.myapplication.businessLogic.repository.UserRepository
|
||||
import com.example.myapplication.model.RoleEnum
|
||||
import com.example.myapplication.model.User
|
||||
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 surname = mutableStateOf("")
|
||||
var email = 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 {
|
||||
val user = User(
|
||||
name = name.value,
|
||||
surname = surname.value,
|
||||
email = email.value,
|
||||
password = password.value,
|
||||
role = RoleEnum.User,
|
||||
photo = R.drawable.icon_profile
|
||||
fun createUser(photo: Bitmap){
|
||||
val user =
|
||||
User(
|
||||
name = name.value,
|
||||
surname = surname.value,
|
||||
email = email.value,
|
||||
password = password.value,
|
||||
role = RoleEnum.User,
|
||||
photo = photo
|
||||
)
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
userRepository.insert(user)
|
||||
}
|
||||
)
|
||||
// isBusy = true
|
||||
userRepository.insert(user)
|
||||
// isBusy = false
|
||||
}
|
||||
|
||||
fun authUser() = viewModelScope.launch {
|
||||
// isBusy = true
|
||||
val user = userRepository.authUser(UserRemoteSignIn(email.value, password.value))
|
||||
// isLoggedIn = true
|
||||
// isBusy = false
|
||||
GlobalUser.getInstance().setUser(user)
|
||||
fun authUser() {
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
val user = userRepository.authUser(UserRemoteSignIn(email.value, password.value))
|
||||
GlobalUser.getInstance().setUser(user)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun updateUser() = viewModelScope.launch {
|
||||
val updateUser = GlobalUser.getInstance().getUser()
|
||||
if(email.value != "")
|
||||
updateUser?.email = email.value
|
||||
else
|
||||
updateUser?.email = updateUser?.email.toString()
|
||||
if(name.value != "")
|
||||
updateUser?.name = name.value
|
||||
else
|
||||
updateUser?.name = updateUser?.name.toString()
|
||||
if(surname.value != "")
|
||||
updateUser?.surname = surname.value
|
||||
else
|
||||
updateUser?.surname = updateUser?.surname.toString()
|
||||
if(password.value != "")
|
||||
updateUser?.password = password.value
|
||||
else
|
||||
updateUser?.password = updateUser?.password.toString()
|
||||
//updateUser?.photo =
|
||||
if (updateUser != null) {
|
||||
userRepository.update(updateUser)
|
||||
}
|
||||
fun updateUser(user: User){
|
||||
runInScope(
|
||||
actionSuccess = {
|
||||
userRepository.update(user)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun isValidEmail(): Boolean {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.example.myapplication.composeui.Basket
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@ -24,16 +25,19 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import com.example.myapplication.GlobalUser
|
||||
import com.example.myapplication.api.ApiStatus
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
|
||||
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
|
||||
import com.example.myapplication.composeui.Navbar.NavItem
|
||||
import com.example.myapplication.composeui.NetworkUI.Loading
|
||||
import com.example.myapplication.composeui.Profile.Login
|
||||
import com.example.myapplication.ui.theme.BlueMain
|
||||
import com.example.myapplication.ui.theme.GreenBtn
|
||||
@ -41,85 +45,100 @@ import com.example.myapplication.ui.theme.GreenBtn
|
||||
@Composable
|
||||
fun Basket(navController : NavHostController,
|
||||
basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)){
|
||||
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
|
||||
val user = GlobalUser.getInstance().getUser()
|
||||
|
||||
if (user == null){
|
||||
Login(navController = navController)
|
||||
}else{
|
||||
basketViewModel.updateSubTotal(user.userId!!)
|
||||
val total = basketViewModel.total.value
|
||||
LaunchedEffect(basketViewModel){
|
||||
basketViewModel.getBasketServices()
|
||||
}
|
||||
val serviceList by basketViewModel.myList.collectAsState()
|
||||
orderViewModel.updateSelectedItems(serviceList)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
for (item in serviceList){
|
||||
BasketItemUI(item = item)
|
||||
}
|
||||
Box(modifier = Modifier
|
||||
.padding(15.dp, 0.dp)
|
||||
.clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp))
|
||||
.background(Color.Transparent)
|
||||
.height(130.dp),
|
||||
){
|
||||
Column (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(100.dp)
|
||||
.background(Color.White)
|
||||
.padding(PaddingValues(15.dp)),
|
||||
){
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
){
|
||||
val context = LocalContext.current
|
||||
if (basketViewModel.apiStatus == ApiStatus.ERROR) {
|
||||
Toast.makeText(context, "Error: " + basketViewModel.apiError, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
when (basketViewModel.apiStatus) {
|
||||
ApiStatus.LOADING -> Loading()
|
||||
ApiStatus.DONE ->
|
||||
if (user == null) {
|
||||
Login(navController = navController)
|
||||
} else {
|
||||
basketViewModel.updateSubTotal(user.userId!!)
|
||||
val total = basketViewModel.total.value
|
||||
LaunchedEffect(basketViewModel) {
|
||||
basketViewModel.getBasketServices()
|
||||
}
|
||||
val serviceList by basketViewModel.myList.collectAsState()
|
||||
orderViewModel.updateSelectedItems(serviceList)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = "Total: ",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "$$total",
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
orderViewModel.createOrder()
|
||||
navController.navigate(NavItem.ListOfServices.route)
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape)
|
||||
.align(Alignment.BottomCenter),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(text = "Confirm order", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
|
||||
for (item in serviceList) {
|
||||
BasketItemUI(item = item)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(15.dp, 0.dp)
|
||||
.clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp))
|
||||
.background(Color.Transparent)
|
||||
.height(130.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(100.dp)
|
||||
.background(Color.White)
|
||||
.padding(PaddingValues(15.dp)),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = "Total: ",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "$$total",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
orderViewModel.createOrder()
|
||||
navController.navigate(NavItem.ListOfServices.route)
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape)
|
||||
.align(Alignment.BottomCenter),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Confirm order",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
@ -29,9 +29,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@ -69,9 +69,8 @@ fun BasketItemUI(item: Service, basketViewModel: BasketViewModel = viewModel(fac
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
item.photo?.let { painterResource(id = it) }?.let {
|
||||
Image(
|
||||
painter = it,
|
||||
bitmap = item.photo.asImageBitmap(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
@ -79,7 +78,6 @@ fun BasketItemUI(item: Service, basketViewModel: BasketViewModel = viewModel(fac
|
||||
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
@ -1,5 +1,14 @@
|
||||
package com.example.myapplication.composeui.List_of_Services
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -11,7 +20,6 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
@ -21,206 +29,197 @@ import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
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.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.api.ApiStatus
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
|
||||
import com.example.myapplication.composeui.Navbar.NavItem
|
||||
import com.example.myapplication.composeui.NetworkUI.Loading
|
||||
import com.example.myapplication.composeui.UIComponents.MyTextField
|
||||
import com.example.myapplication.model.Service
|
||||
import com.example.myapplication.ui.theme.BlueMain
|
||||
import com.example.myapplication.ui.theme.GreenBtn
|
||||
import com.example.myapplication.ui.theme.TextPrimary
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
@Composable
|
||||
fun AddService (navController: NavController, service: Service, serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){
|
||||
val create = service.serviceId == null
|
||||
LaunchedEffect(Dispatchers.Default){
|
||||
if(!create){
|
||||
serviceViewModel.service.value.serviceId = service.serviceId
|
||||
serviceViewModel.service.value.photo = service.photo
|
||||
fun AddService (
|
||||
navController: NavController,
|
||||
serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val serviceImage = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.image_service)) }
|
||||
val imageData = remember { mutableStateOf<Uri?>(null) }
|
||||
val launcher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||
imageData.value = uri
|
||||
}
|
||||
imageData.value?.let {
|
||||
if (Build.VERSION.SDK_INT < 28) {
|
||||
serviceImage.value = MediaStore.Images
|
||||
.Media.getBitmap(context.contentResolver, imageData.value)
|
||||
} else {
|
||||
val source = ImageDecoder
|
||||
.createSource(context.contentResolver, imageData.value!!)
|
||||
serviceImage.value = ImageDecoder.decodeBitmap(source)
|
||||
}
|
||||
}
|
||||
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(
|
||||
|
||||
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
|
||||
.fillMaxWidth()
|
||||
.height(145.dp)
|
||||
.background(color = Color.White, RoundedCornerShape(15.dp))
|
||||
.padding(15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
){
|
||||
if(create){
|
||||
Image(
|
||||
painter = painterResource(id = serviceViewModel.photo.intValue),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.heightIn(min = 100.dp)
|
||||
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
.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))
|
||||
)
|
||||
}else{
|
||||
service.photo?.let { painterResource(id = it) }?.let {
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(0.dp, 0.dp, 0.dp, 10.dp)
|
||||
.height(150.dp)
|
||||
.shadow(
|
||||
elevation = 4.dp,
|
||||
shape = RoundedCornerShape(15.dp),
|
||||
clip = false
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(145.dp)
|
||||
.background(color = Color.White, RoundedCornerShape(15.dp))
|
||||
.padding(15.dp),
|
||||
verticalAlignment = CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Image(
|
||||
painter = it,
|
||||
bitmap = serviceImage.value.asImageBitmap(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.align(CenterVertically),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.heightIn(min = 100.dp)
|
||||
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
)
|
||||
verticalArrangement = Arrangement.Top,
|
||||
) {
|
||||
Text(
|
||||
text = serviceViewModel.name.value,
|
||||
color = TextPrimary,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = serviceViewModel.price.doubleValue.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Column(
|
||||
) {
|
||||
Row(modifier = Modifier.padding(vertical = 5.dp)) {
|
||||
MyTextField(label = "Service name") {
|
||||
serviceViewModel.name.value = it
|
||||
}
|
||||
}
|
||||
Row(modifier = Modifier.padding(vertical = 5.dp)) {
|
||||
MyTextField(label = "Price") {
|
||||
try {
|
||||
serviceViewModel.price.doubleValue = it.toDouble()
|
||||
}catch (e: Exception){
|
||||
serviceViewModel.price.doubleValue = 0.0
|
||||
Toast.makeText(context, "Input correct price!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
launcher.launch("image/*")
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Upload image",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if (serviceViewModel.name.value == ""
|
||||
|| serviceViewModel.price.doubleValue == 0.0)
|
||||
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
|
||||
else {
|
||||
serviceViewModel.insertService(serviceImage.value)
|
||||
navController.navigate(NavItem.AddService.route)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Add service",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
){
|
||||
if(create){
|
||||
Text(
|
||||
text = serviceViewModel.name.value,
|
||||
color = TextPrimary,
|
||||
style = MaterialTheme.typography.bodyMedium)
|
||||
}else{
|
||||
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,
|
||||
){
|
||||
if(create){
|
||||
Text(
|
||||
text = serviceViewModel.price.doubleValue.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}else{
|
||||
Text(
|
||||
text = service.price.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Column (
|
||||
){
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
if(create){
|
||||
MyTextField(label = "Service name"){
|
||||
serviceViewModel.name.value = it
|
||||
}
|
||||
}else{
|
||||
MyTextField(label = service.name){
|
||||
serviceViewModel.service.value.name = it
|
||||
}
|
||||
}
|
||||
}
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
if(create){
|
||||
MyTextField(label = "Price"){
|
||||
serviceViewModel.price.doubleValue = it.toDouble()
|
||||
}
|
||||
}else{
|
||||
MyTextField(label = service.price.toString()){
|
||||
serviceViewModel.service.value.price = it.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
// val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
// launcher.launch(intent)
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(text = "Upload image", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if (create)
|
||||
serviceViewModel.insertService()
|
||||
else
|
||||
serviceViewModel.service.let { serviceViewModel.updateService() }
|
||||
navController.navigate(NavItem.ListOfServices.route)
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
if(create) {
|
||||
Text(text = "Add service", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
|
||||
}else{
|
||||
Text(text = "Update service", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 -> {}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.example.myapplication.composeui.List_of_Services
|
||||
|
||||
import SearchBar
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -13,8 +14,10 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -22,8 +25,10 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemKey
|
||||
import com.example.myapplication.api.ApiStatus
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
|
||||
import com.example.myapplication.composeui.NetworkUI.Loading
|
||||
import com.example.myapplication.model.Service
|
||||
import com.example.myapplication.ui.theme.BlueMain
|
||||
|
||||
@ -32,39 +37,53 @@ fun ListOfServices(navController: NavHostController, serviceViewModel: ServiceVi
|
||||
LaunchedEffect(serviceViewModel){
|
||||
serviceViewModel.getServiceList()
|
||||
}
|
||||
val services = serviceViewModel.serviceList.collectAsLazyPagingItems()
|
||||
Column(modifier = Modifier.background(BlueMain).fillMaxSize().padding(bottom = 60.dp)){
|
||||
Row(
|
||||
val services = serviceViewModel.serviceList.collectAsState().value.collectAsLazyPagingItems()
|
||||
val context = LocalContext.current
|
||||
if (serviceViewModel.apiStatus == ApiStatus.ERROR){
|
||||
Toast.makeText(context, "Error: " + serviceViewModel.apiError, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
when(serviceViewModel.apiStatus) {
|
||||
ApiStatus.LOADING -> Loading()
|
||||
ApiStatus.DONE -> Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
LazyColumn(
|
||||
){
|
||||
item{
|
||||
SearchBar(
|
||||
modifier = Modifier)
|
||||
{
|
||||
searchText ->
|
||||
//TODO search logic
|
||||
}
|
||||
.background(BlueMain)
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 60.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
items(
|
||||
count = services.itemCount,
|
||||
key = services.itemKey { service -> service.serviceId!! }
|
||||
){
|
||||
index: Int ->
|
||||
val service: Service? = services[index]
|
||||
if (service != null){
|
||||
Service(navController, item = service)
|
||||
LazyColumn(
|
||||
) {
|
||||
item {
|
||||
SearchBar(
|
||||
modifier = Modifier
|
||||
)
|
||||
{ searchText ->
|
||||
serviceViewModel.searchServicesByFilter(searchText)
|
||||
}
|
||||
}
|
||||
items(
|
||||
count = services.itemCount,
|
||||
key = services.itemKey { service -> service.serviceId!! }
|
||||
) { index: Int ->
|
||||
val service: Service? = services[index]
|
||||
if (service != null) {
|
||||
Service(navController, item = service)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
@ -33,9 +33,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@ -53,7 +53,12 @@ import com.google.gson.Gson
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun Service(navController: NavHostController, item: Service, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){
|
||||
fun Service(
|
||||
navController: NavHostController,
|
||||
item: Service,
|
||||
basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||
serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
){
|
||||
val user = GlobalUser.getInstance().getUser()
|
||||
val basketId by basketViewModel.basketId.collectAsState()
|
||||
LaunchedEffect(basketViewModel){
|
||||
@ -78,9 +83,8 @@ fun Service(navController: NavHostController, item: Service, basketViewModel: Ba
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
){
|
||||
item.photo?.let { painterResource(id = it) }?.let {
|
||||
Image(
|
||||
painter = it,
|
||||
bitmap = item.photo.asImageBitmap(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
@ -88,7 +92,6 @@ fun Service(navController: NavHostController, item: Service, basketViewModel: Ba
|
||||
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -155,8 +158,7 @@ fun Service(navController: NavHostController, item: Service, basketViewModel: Ba
|
||||
if(user?.role == RoleEnum.Admin){
|
||||
Button(
|
||||
onClick = {
|
||||
serviceViewModel.service.value = item
|
||||
navController.navigate("add_service/${Gson().toJson(item)}")
|
||||
navController.navigate("change_service/${Gson().toJson(item)}")
|
||||
},
|
||||
modifier = Modifier
|
||||
.size(42.dp)
|
||||
|
@ -1,49 +1,31 @@
|
||||
package com.example.myapplication.composeui.Navbar
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.BottomNavigation
|
||||
import androidx.compose.material.BottomNavigationItem
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountBox
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ControlledComposition
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.max
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat.getColor
|
||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.ui.theme.BlueMain
|
||||
import com.example.myapplication.ui.theme.BlueNavbar
|
||||
import com.example.myapplication.ui.theme.GreenBtn
|
||||
@ -76,12 +58,14 @@ fun NavBar(){
|
||||
selected = isSelected,
|
||||
icon = {
|
||||
Icon(painterResource(screen.icon),
|
||||
null,
|
||||
modifier = Modifier,
|
||||
GreenBtn)
|
||||
null,
|
||||
modifier = if (isSelected){
|
||||
Modifier.background(BlueMain, CircleShape).size(50.dp).padding(8.dp)
|
||||
} else {
|
||||
Modifier.size(50.dp).padding(8.dp)
|
||||
},
|
||||
GreenBtn)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(15.dp),
|
||||
onClick = {
|
||||
navController.navigate(screen.route){
|
||||
if (!isSelected) {
|
||||
|
@ -7,12 +7,14 @@ import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import com.example.myapplication.composeui.Basket.Basket
|
||||
import com.example.myapplication.composeui.List_of_Services.AddService
|
||||
import com.example.myapplication.composeui.List_of_Services.ChangeService
|
||||
import com.example.myapplication.composeui.List_of_Services.ListOfServices
|
||||
import com.example.myapplication.composeui.Orders.Orders
|
||||
import com.example.myapplication.composeui.Profile.Login
|
||||
import com.example.myapplication.composeui.Profile.Profile
|
||||
import com.example.myapplication.composeui.Profile.ProfileChange
|
||||
import com.example.myapplication.composeui.Profile.Registration
|
||||
import com.example.myapplication.composeui.Profile.Report
|
||||
import com.example.myapplication.model.Service
|
||||
import com.google.gson.Gson
|
||||
|
||||
@ -59,12 +61,22 @@ fun NavController(navController : NavHostController){
|
||||
}
|
||||
composable(
|
||||
NavItem.AddService.route
|
||||
){
|
||||
AddService(navController)
|
||||
}
|
||||
composable(
|
||||
NavItem.ChangeService.route
|
||||
){
|
||||
backStackEntry ->
|
||||
val serviceItemString = backStackEntry.arguments?.getString("serviceItem")
|
||||
val serviceItem = Gson().fromJson(serviceItemString, Service::class.java)
|
||||
serviceItem?.let { AddService(navController, it)
|
||||
serviceItem?.let { ChangeService(it, navController)
|
||||
}
|
||||
}
|
||||
composable(
|
||||
NavItem.Report.route
|
||||
){
|
||||
Report()
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,15 @@ sealed class NavItem(
|
||||
R.drawable.icon_profile
|
||||
)
|
||||
object AddService : NavItem(
|
||||
"add_service/{serviceItem}",
|
||||
"add_service",
|
||||
R.drawable.icon_list_of_services
|
||||
)
|
||||
object ChangeService : NavItem(
|
||||
"change_service/{serviceItem}",
|
||||
R.drawable.icon_list_of_services
|
||||
)
|
||||
object Report : NavItem(
|
||||
"report",
|
||||
R.drawable.profile
|
||||
)
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
@ -1,31 +1,55 @@
|
||||
package com.example.myapplication.composeui.Orders
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
|
||||
import com.example.myapplication.model.Order
|
||||
import com.example.myapplication.model.Service
|
||||
import kotlinx.coroutines.flow.first
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
@Composable
|
||||
fun OrderItem (order: Order){
|
||||
fun OrderItem (
|
||||
order: Order,
|
||||
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
){
|
||||
val dateFormat = SimpleDateFormat("dd-MM-yyyy")
|
||||
var services by remember { mutableStateOf<List<Service>>(emptyList()) }
|
||||
var expanded = remember { mutableStateOf(false) }
|
||||
LaunchedEffect(order.orderId) {
|
||||
services = orderViewModel.getOrderWithServices(order.orderId!!).first()
|
||||
}
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp)
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.clickable {
|
||||
expanded.value = !expanded.value
|
||||
}
|
||||
){
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
@ -45,5 +69,12 @@ fun OrderItem (order: Order){
|
||||
){
|
||||
Text(text = dateFormat.format(Date(order.date)), style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
if(expanded.value){
|
||||
LazyRow(){
|
||||
itemsIndexed(services ?: emptyList()) { index, service ->
|
||||
OrderScrollService(service)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,10 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@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 ordersList by orderViewModel.orders.collectAsState()
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package com.example.myapplication.composeui.Profile
|
||||
|
||||
import android.view.Gravity
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@ -27,127 +26,146 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.example.myapplication.GlobalUser
|
||||
import com.example.myapplication.api.ApiStatus
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
|
||||
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
|
||||
import com.example.myapplication.composeui.Navbar.NavItem
|
||||
import com.example.myapplication.composeui.NetworkUI.Loading
|
||||
import com.example.myapplication.composeui.UIComponents.MyTextField
|
||||
import com.example.myapplication.ui.theme.BlueMain
|
||||
import com.example.myapplication.ui.theme.GreenBtn
|
||||
import com.example.myapplication.ui.theme.TextSecondary
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@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
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(15.dp)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
){
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 30.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Column (
|
||||
){
|
||||
var isEmailValid by remember { mutableStateOf(true)}
|
||||
var isPasswordValid by remember { mutableStateOf(true)}
|
||||
var isEmailValid by remember { mutableStateOf(true) }
|
||||
var isPasswordValid by remember { mutableStateOf(true) }
|
||||
|
||||
if (!isEmailValid) {
|
||||
Text(
|
||||
text = "Invalid email format",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Email", onValueChanged = {
|
||||
userViewModel.email.value = it
|
||||
isEmailValid = userViewModel.isValidEmail()
|
||||
})
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
Text(
|
||||
text = "Password is required",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Password", onValueChanged = {
|
||||
userViewModel.password.value = it
|
||||
isPasswordValid = it.isNotEmpty()
|
||||
})
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
userViewModel.authUser()
|
||||
navController.navigate(NavItem.Profile.route)
|
||||
}
|
||||
val toast = Toast.makeText(context, "message", Toast.LENGTH_SHORT)
|
||||
toast.setGravity(Gravity.TOP, 0, 100)
|
||||
toast.show()
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Login",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "Don't have an account? ",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(TextSecondary)
|
||||
)
|
||||
Text(
|
||||
text = "Sign up",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(GreenBtn),
|
||||
modifier = Modifier
|
||||
.clickable { navController.navigate(NavItem.Registration.route) }
|
||||
)
|
||||
}
|
||||
if(GlobalUser.getInstance().getUser() != null){
|
||||
navController.navigate(NavItem.Profile.route)
|
||||
}
|
||||
}
|
||||
|
||||
when(userViewModel.apiStatus){
|
||||
ApiStatus.LOADING -> Loading()
|
||||
ApiStatus.ERROR -> {
|
||||
Toast.makeText(context, "Error: " + userViewModel.apiError, Toast.LENGTH_SHORT).show()
|
||||
userViewModel.apiStatus = ApiStatus.DONE
|
||||
}
|
||||
ApiStatus.DONE ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(15.dp)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
){
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 30.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Column (
|
||||
){
|
||||
if (!isEmailValid) {
|
||||
Text(
|
||||
text = "Invalid email format",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Email", onValueChanged = {
|
||||
userViewModel.email.value = it
|
||||
isEmailValid = userViewModel.isValidEmail()
|
||||
})
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
Text(
|
||||
text = "Password is required",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Password", visualTransformation = PasswordVisualTransformation(), onValueChanged = {
|
||||
userViewModel.password.value = it
|
||||
isPasswordValid = it.isNotEmpty()
|
||||
})
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if (userViewModel.email.value == ""
|
||||
|| !isEmailValid
|
||||
|| !isPasswordValid){
|
||||
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
else{
|
||||
userViewModel.authUser()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Login",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "Don't have an account? ",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(TextSecondary)
|
||||
)
|
||||
Text(
|
||||
text = "Sign up",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(GreenBtn),
|
||||
modifier = Modifier
|
||||
.clickable { navController.navigate(NavItem.Registration.route) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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
|
||||
@ -24,6 +25,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
@ -66,7 +68,11 @@ fun Profile(navController: NavHostController){
|
||||
.size(200.dp)
|
||||
.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)){
|
||||
Text(
|
||||
@ -129,7 +135,7 @@ fun Profile(navController: NavHostController){
|
||||
}
|
||||
if(user.role == RoleEnum.Admin){
|
||||
Button(
|
||||
onClick = { navController.navigate("add_service/{}") },
|
||||
onClick = { navController.navigate(NavItem.AddService.route) },
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.fillMaxWidth()
|
||||
@ -147,6 +153,25 @@ fun Profile(navController: NavHostController){
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = { navController.navigate(NavItem.Report.route) },
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape)
|
||||
.padding(vertical = 5.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Reports",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
|
@ -1,20 +1,32 @@
|
||||
package com.example.myapplication.composeui.Profile
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -26,7 +38,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
@ -34,126 +47,263 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import com.example.myapplication.GlobalUser
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.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.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.GreenBtn
|
||||
|
||||
@Composable
|
||||
fun ProfileChange (navController: NavHostController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
|
||||
val user = GlobalUser.getInstance().getUser()
|
||||
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))
|
||||
)
|
||||
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
|
||||
}
|
||||
Box(modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(200.dp)
|
||||
.background(Color.White)
|
||||
.padding(PaddingValues(0.dp))
|
||||
){
|
||||
Icon(
|
||||
painterResource(id = R.drawable.upload),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
GreenBtn
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.padding(15.dp)){
|
||||
Text(
|
||||
text = "${user?.name} ${user?.surname}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.Center),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.padding(15.dp)){
|
||||
Text(
|
||||
text = "${user?.email}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.Center),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Column (
|
||||
){
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Name", onValueChanged = {userViewModel.name.value = it})
|
||||
}
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Surname", onValueChanged = {userViewModel.surname.value = it})
|
||||
}
|
||||
var isEmailValid by remember { mutableStateOf(true) }
|
||||
var isPasswordValid by remember { mutableStateOf(true) }
|
||||
|
||||
if (!isEmailValid) {
|
||||
Text(
|
||||
text = "Invalid email format",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Email", onValueChanged = {
|
||||
userViewModel.email.value = it
|
||||
isEmailValid = userViewModel.isValidEmail()
|
||||
})
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
Text(
|
||||
text = "Password is required",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Password", onValueChanged = {
|
||||
userViewModel.password.value = it
|
||||
isPasswordValid = it.isNotEmpty()
|
||||
})
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
userViewModel.updateUser()
|
||||
navController.navigate(NavItem.Profile.route)
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(text = "Confirm changes", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
|
||||
imageData.value?.let {
|
||||
if (Build.VERSION.SDK_INT < 28) {
|
||||
profileImage.value = MediaStore.Images
|
||||
.Media.getBitmap(context.contentResolver, imageData.value)
|
||||
} else {
|
||||
val source = ImageDecoder
|
||||
.createSource(context.contentResolver, imageData.value!!)
|
||||
profileImage.value = ImageDecoder.decodeBitmap(source)
|
||||
}
|
||||
}
|
||||
var isEmailValid by remember { mutableStateOf(true) }
|
||||
var isPasswordValid by remember { mutableStateOf(true) }
|
||||
|
||||
when(userViewModel.apiStatus){
|
||||
ApiStatus.LOADING -> Loading()
|
||||
ApiStatus.DONE -> Column (
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(15.dp)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
){
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(200.dp)
|
||||
.background(Color.White)
|
||||
.padding(PaddingValues(0.dp))
|
||||
){
|
||||
Image(
|
||||
bitmap = profileImage.value.asImageBitmap(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.padding(15.dp)){
|
||||
Text(
|
||||
text = "${name.value} ${surname.value}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.Center),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.padding(15.dp)){
|
||||
Text(
|
||||
text = "${email.value}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.Center),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Column (
|
||||
){
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
|
||||
.height(45.dp)
|
||||
.padding(15.dp, 5.dp),
|
||||
){
|
||||
BasicTextField(
|
||||
value = name.value!!,
|
||||
onValueChange = {
|
||||
name.value = it
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
|
||||
.height(45.dp)
|
||||
.padding(15.dp, 5.dp),
|
||||
){
|
||||
BasicTextField(
|
||||
value = surname.value!!,
|
||||
onValueChange = {
|
||||
surname.value = it
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
if (!isEmailValid) {
|
||||
Text(
|
||||
text = "Invalid email format",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
|
||||
.height(45.dp)
|
||||
.padding(15.dp, 5.dp),
|
||||
){
|
||||
BasicTextField(
|
||||
value = email.value!!,
|
||||
onValueChange = {
|
||||
email.value = it
|
||||
isEmailValid = isValidEmail(email.value!!)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
if (!isPasswordValid) {
|
||||
Text(
|
||||
text = "Password is required",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
|
||||
.height(45.dp)
|
||||
.padding(15.dp, 5.dp),
|
||||
){
|
||||
BasicTextField(
|
||||
value = password.value.toString(),
|
||||
onValueChange = {
|
||||
password.value = it
|
||||
isPasswordValid = it.isNotEmpty()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
launcher.launch("image/*")
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(50.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Upload image",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
if (!isEmailValid
|
||||
|| !isPasswordValid
|
||||
|| name.value == ""
|
||||
|| surname.value == "")
|
||||
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
|
||||
else {
|
||||
val updatedUser = User(
|
||||
user?.userId!!,
|
||||
name = name.value!!,
|
||||
surname = surname.value!!,
|
||||
email = email.value!!,
|
||||
password = password.value!!,
|
||||
role = user.role,
|
||||
photo = profileImage.value,
|
||||
basketId = user.basketId)
|
||||
userViewModel.updateUser(updatedUser)
|
||||
GlobalUser.getInstance().setUser(updatedUser)
|
||||
navController.navigate(NavItem.Profile.route)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(50.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(text = "Confirm changes", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
fun isValidEmail(str: String): Boolean {
|
||||
return android.util.Patterns.EMAIL_ADDRESS.matcher(str).matches()
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.example.myapplication.composeui.Profile
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -24,121 +26,147 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.api.ApiStatus
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
|
||||
import com.example.myapplication.composeui.Navbar.NavItem
|
||||
import com.example.myapplication.composeui.NetworkUI.Loading
|
||||
import com.example.myapplication.composeui.UIComponents.MyTextField
|
||||
import com.example.myapplication.ui.theme.BlueMain
|
||||
import com.example.myapplication.ui.theme.GreenBtn
|
||||
import com.example.myapplication.ui.theme.TextSecondary
|
||||
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
|
||||
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
|
||||
|
||||
@Composable
|
||||
fun Registration (navController: NavController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(15.dp)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
){
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 30.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Column (
|
||||
){
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Name", onValueChanged = { userViewModel.name.value = it })
|
||||
}
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Surname", onValueChanged = { userViewModel.surname.value = it })
|
||||
}
|
||||
var isEmailValid by remember { mutableStateOf(true) }
|
||||
var isPasswordValid by remember { mutableStateOf(true) }
|
||||
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)
|
||||
|
||||
if (!isEmailValid) {
|
||||
Text(
|
||||
text = "Invalid email format",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Email", onValueChanged = {
|
||||
userViewModel.email.value = it
|
||||
isEmailValid = userViewModel.isValidEmail()
|
||||
})
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
Text(
|
||||
text = "Password is required",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Password", onValueChanged = {
|
||||
userViewModel.password.value = it
|
||||
isPasswordValid = it.isNotEmpty()
|
||||
})
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
userViewModel.createUser()
|
||||
navController.navigate(NavItem.Login.route)
|
||||
},
|
||||
when(userViewModel.apiStatus) {
|
||||
ApiStatus.LOADING -> Loading()
|
||||
ApiStatus.DONE ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Registration",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
.fillMaxSize()
|
||||
.background(BlueMain)
|
||||
.padding(15.dp)
|
||||
.padding(bottom = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
){
|
||||
Text(
|
||||
text = "Already have a account? ",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(TextSecondary)
|
||||
)
|
||||
Text(
|
||||
text = "Login",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(GreenBtn),
|
||||
modifier = Modifier.clickable { navController.navigate(NavItem.Login.route) }
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 30.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
){
|
||||
Text(
|
||||
text = "PetMed",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Column (
|
||||
){
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Name", onValueChanged = { userViewModel.name.value = it })
|
||||
}
|
||||
Row (modifier = Modifier.padding(vertical = 5.dp)){
|
||||
MyTextField(label = "Surname", onValueChanged = { userViewModel.surname.value = it })
|
||||
}
|
||||
if (!isEmailValid) {
|
||||
Text(
|
||||
text = "Invalid email format",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Email", onValueChanged = {
|
||||
userViewModel.email.value = it
|
||||
isEmailValid = userViewModel.isValidEmail()
|
||||
})
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
Text(
|
||||
text = "Password is required",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
|
||||
)
|
||||
}
|
||||
|
||||
Row (modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
){
|
||||
MyTextField(label = "Password", visualTransformation = PasswordVisualTransformation(), onValueChanged = {
|
||||
userViewModel.password.value = it
|
||||
isPasswordValid = it.isNotEmpty()
|
||||
})
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if (userViewModel.email.value == ""
|
||||
|| userViewModel.name.value == ""
|
||||
|| userViewModel.surname.value == ""
|
||||
|| !isEmailValid
|
||||
|| !isPasswordValid)
|
||||
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
|
||||
else {
|
||||
userViewModel.createUser(photo)
|
||||
navController.navigate(NavItem.Login.route)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(CircleShape),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = GreenBtn,
|
||||
contentColor = Color.White
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Registration",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(Color.White)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
){
|
||||
Text(
|
||||
text = "Already have a account? ",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(TextSecondary)
|
||||
)
|
||||
Text(
|
||||
text = "Login",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(GreenBtn),
|
||||
modifier = Modifier.clickable { navController.navigate(NavItem.Login.route) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
@ -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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 -> {}
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.myapplication.ui.theme.BlueBorder
|
||||
import com.example.myapplication.ui.theme.TextSecondary
|
||||
@ -28,6 +29,7 @@ import com.example.myapplication.ui.theme.TextSecondary
|
||||
@Composable
|
||||
fun MyTextField (
|
||||
label: String,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
onValueChanged: (String) -> Unit,
|
||||
){
|
||||
val textState = remember { mutableStateOf(TextFieldValue()) }
|
||||
@ -48,8 +50,6 @@ fun MyTextField (
|
||||
text = label,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(color = TextSecondary),
|
||||
)
|
||||
}else{
|
||||
|
||||
}
|
||||
BasicTextField(
|
||||
value = text,
|
||||
@ -57,6 +57,7 @@ fun MyTextField (
|
||||
textState.value = it
|
||||
onValueChanged(it.text)
|
||||
},
|
||||
visualTransformation = visualTransformation,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
singleLine = true,
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.example.myapplication.database.dao.BasketDao
|
||||
import com.example.myapplication.database.dao.OrderDao
|
||||
@ -12,6 +13,7 @@ import com.example.myapplication.database.dao.ServiceDao
|
||||
import com.example.myapplication.database.dao.UserDao
|
||||
import com.example.myapplication.model.Basket
|
||||
import com.example.myapplication.model.BasketService
|
||||
import com.example.myapplication.model.Converters
|
||||
import com.example.myapplication.model.Order
|
||||
import com.example.myapplication.model.OrderService
|
||||
import com.example.myapplication.model.RemoteKeys
|
||||
@ -21,7 +23,14 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Database(entities = [User::class, Service::class, Order::class, OrderService::class, Basket::class, BasketService::class, RemoteKeys::class], version = 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 fun serviceDao(): ServiceDao
|
||||
abstract fun userDao(): UserDao
|
||||
@ -36,15 +45,7 @@ abstract class AppDatabase : RoomDatabase(){
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
suspend fun populateDatabase() {
|
||||
// INSTANCE?.let { database ->
|
||||
// // User
|
||||
// val userDao = database.userDao()
|
||||
// val user1 = User(null, "Danil", "Markov", "danil@mail.ru", "123", RoleEnum.Admin)
|
||||
// userDao.insert(user1)
|
||||
// val basketDao = database.basketDao()
|
||||
// val basket1 = Basket(null, user1.userId!!)
|
||||
// basketDao.insert(basket1)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
fun getInstance(appContext: Context): AppDatabase {
|
||||
|
@ -26,4 +26,6 @@ interface ServiceDao {
|
||||
suspend fun deleteAll()
|
||||
@Query("DELETE FROM tbl_service WHERE serviceId = :id")
|
||||
suspend fun invalidateService(id: Int)
|
||||
@Query("SELECT * FROM tbl_service WHERE LOWER(name) LIKE '%' || LOWER(:searchString) || '%' ")
|
||||
fun findServices(searchString: String): PagingSource<Int, Service>
|
||||
}
|
@ -28,4 +28,14 @@ class ServiceRepositoryImpl(private val serviceDao: ServiceDao): ServiceReposito
|
||||
serviceDao.insert(*services.toTypedArray())
|
||||
fun getAllServicesPagingSource(): PagingSource<Int, Service> = serviceDao.getAll()
|
||||
suspend fun invalidateService(id: Int) = serviceDao.invalidateService(id)
|
||||
override fun call(str: String): Flow<PagingData<Service>> {
|
||||
return Pager(
|
||||
PagingConfig(
|
||||
pageSize = AppContainer.LIMIT,
|
||||
enablePlaceholders = false
|
||||
),
|
||||
) {
|
||||
serviceDao.findServices(str)
|
||||
}.flow
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package com.example.myapplication.di
|
||||
|
||||
import com.example.myapplication.businessLogic.repository.BasketRepository
|
||||
import com.example.myapplication.businessLogic.repository.OrderRepository
|
||||
import com.example.myapplication.businessLogic.repository.ReportRepository
|
||||
import com.example.myapplication.businessLogic.repository.ServiceRepository
|
||||
import com.example.myapplication.businessLogic.repository.UserRepository
|
||||
|
||||
@ -10,6 +11,7 @@ interface AppContainer {
|
||||
val userRepo: UserRepository
|
||||
val orderRepo: OrderRepository
|
||||
val basketRepo: BasketRepository
|
||||
val reportRepo: ReportRepository
|
||||
|
||||
companion object {
|
||||
const val TIMEOUT = 5000L
|
||||
|
@ -4,10 +4,12 @@ import android.content.Context
|
||||
import com.example.myapplication.api.ServerService
|
||||
import com.example.myapplication.api.repository.RestBasketRepository
|
||||
import com.example.myapplication.api.repository.RestOrderRepository
|
||||
import com.example.myapplication.api.repository.RestReportRepository
|
||||
import com.example.myapplication.api.repository.RestServiceRepository
|
||||
import com.example.myapplication.api.repository.RestUserRepository
|
||||
import com.example.myapplication.businessLogic.repository.BasketRepository
|
||||
import com.example.myapplication.businessLogic.repository.OrderRepository
|
||||
import com.example.myapplication.businessLogic.repository.ReportRepository
|
||||
import com.example.myapplication.businessLogic.repository.ServiceRepository
|
||||
import com.example.myapplication.businessLogic.repository.UserRepository
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
@ -32,6 +34,9 @@ class AppDataContainer(context: Context) : AppContainer {
|
||||
override val basketRepo: BasketRepository by lazy{
|
||||
RestBasketRepository(ServerService.getInstance())
|
||||
}
|
||||
override val reportRepo: ReportRepository by lazy{
|
||||
RestReportRepository(ServerService.getInstance())
|
||||
}
|
||||
|
||||
private val serviceRepository: ServiceRepositoryImpl by lazy {
|
||||
ServiceRepositoryImpl(AppDatabase.getInstance(context).serviceDao())
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.example.myapplication.model
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@ -9,5 +10,5 @@ data class Service (
|
||||
var serviceId: Int? = null,
|
||||
var name: String,
|
||||
var price: Double,
|
||||
var photo: Int? = null
|
||||
var photo: Bitmap
|
||||
)
|
@ -1,5 +1,6 @@
|
||||
package com.example.myapplication.model
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@ -12,6 +13,6 @@ data class User(
|
||||
var email: String,
|
||||
var password: String,
|
||||
val role: RoleEnum,
|
||||
val photo: Int? = null,
|
||||
var photo: Bitmap,
|
||||
val basketId: Int? = null
|
||||
)
|
BIN
app/src/main/res/drawable/profile.png
Normal file
BIN
app/src/main/res/drawable/profile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
@ -2,4 +2,6 @@
|
||||
<string name="app_name">PetMed</string>
|
||||
<string name="not_auth_error">Please login</string>
|
||||
<string name="placeholder_search">Search…</string>
|
||||
<string name="loading">Loading...</string>
|
||||
<string name="back">Return</string>
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user