main #6
@@ -1,7 +1,7 @@
|
||||
package ru.myitschool.work.core
|
||||
|
||||
object Constants {
|
||||
const val HOST = "http://10.230.214.96:8080"
|
||||
const val HOST = "http://192.168.1.39:8080"
|
||||
const val AUTH_URL = "/auth"
|
||||
const val INFO_URL = "/info"
|
||||
const val BOOKING_URL = "/booking"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.myitschool.work.data.source
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
@@ -151,20 +152,20 @@ object NetworkDataSource {
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class CreateBookingBody(val date: String, val placeID: Long)
|
||||
private data class CreateBookingBody(val date: String, val placeId: Long)
|
||||
|
||||
suspend fun createBooking(code: String, date: LocalDate, placeId: Long): Result<Boolean> = withContext(Dispatchers.IO) {
|
||||
return@withContext runCatching {
|
||||
// Формируем тело запроса
|
||||
val requestBody = CreateBookingBody(date.toString(), placeId)
|
||||
|
||||
val response = client.post(getUrl(code, Constants.BOOKING_URL)) { // Используем ту же константу BOOKING_URL
|
||||
val response = client.post(getUrl(code, Constants.BOOK_URL)) {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(requestBody)
|
||||
}
|
||||
|
||||
when (response.status) {
|
||||
HttpStatusCode.OK -> true
|
||||
HttpStatusCode.Created -> true
|
||||
else -> {
|
||||
val errorBody = response.bodyAsText()
|
||||
error(if (errorBody.isNotBlank()) App.context.getString(R.string.error_booking, errorBody) else App.context.getString(R.string.error_booking_default))
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
package ru.myitschool.work.ui.screen.book
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.selection.selectable
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.ScrollableTabRow
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
import ru.myitschool.work.core.TestIds
|
||||
import ru.myitschool.work.data.entity.Place
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Composable
|
||||
fun BookScreen(
|
||||
@@ -23,23 +38,19 @@ fun BookScreen(
|
||||
val viewModel: BookViewModel = viewModel()
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
|
||||
// Обработка действий
|
||||
val event = viewModel.actionFlow.collectAsState(initial = null)
|
||||
LaunchedEffect(event.value) {
|
||||
when (event.value) {
|
||||
is BookAction.BookSuccess -> {
|
||||
LaunchedEffect(viewModel.actionFlow) {
|
||||
viewModel.actionFlow.collect { action ->
|
||||
if (action is BookAction.BookSuccess) {
|
||||
onBookSuccess()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// Загрузка начальных данных
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.onIntent(BookIntent.LoadData)
|
||||
}
|
||||
|
||||
when (uiState) {
|
||||
when (val state = uiState) {
|
||||
is BookState.Loading -> {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@@ -51,7 +62,7 @@ fun BookScreen(
|
||||
|
||||
is BookState.Data -> {
|
||||
BookContentScreen(
|
||||
uiState = uiState as BookState.Data,
|
||||
uiState = state,
|
||||
onSelectDate = { date -> viewModel.onIntent(BookIntent.SelectDate(date)) },
|
||||
onSelectPlace = { place -> viewModel.onIntent(BookIntent.SelectPlace(place)) },
|
||||
onBook = { viewModel.onIntent(BookIntent.BookPlace) },
|
||||
@@ -71,13 +82,10 @@ fun BookContentScreen(
|
||||
onBack: () -> Unit,
|
||||
onRefresh: () -> Unit
|
||||
) {
|
||||
// Сортировка дат по порядку
|
||||
val sortedDates = uiState.dates.sorted()
|
||||
// Фильтрация дат, для которых есть доступные места
|
||||
val availableDates = sortedDates.filter { date -> uiState.places[date]?.isNotEmpty() == true }
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||
// Вкладки для выбора дат
|
||||
if (availableDates.isNotEmpty()) {
|
||||
ScrollableTabRow(
|
||||
selectedTabIndex = availableDates.indexOf(uiState.selectedDate),
|
||||
@@ -100,7 +108,6 @@ fun BookContentScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// Список мест для выбранной даты
|
||||
val placesForDate = uiState.selectedDate?.let { uiState.places[it] } ?: emptyList()
|
||||
|
||||
if (placesForDate.isNotEmpty()) {
|
||||
@@ -131,7 +138,6 @@ fun BookContentScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// пустой список (все забронировано)
|
||||
if (availableDates.isEmpty() && !uiState.isError) {
|
||||
Text(
|
||||
text = "Всё забронировано",
|
||||
@@ -139,7 +145,6 @@ fun BookContentScreen(
|
||||
)
|
||||
}
|
||||
|
||||
// ошибка
|
||||
if (uiState.isError) {
|
||||
Text(
|
||||
text = uiState.errorMessage ?: "Ошибка загрузки",
|
||||
@@ -157,7 +162,6 @@ fun BookContentScreen(
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// Кнопки: Забронировать и Назад
|
||||
if (!uiState.isError && placesForDate.isNotEmpty()) {
|
||||
Button(
|
||||
onClick = onBook,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.myitschool.work.ui.screen.book
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -121,11 +122,22 @@ class BookViewModel : ViewModel() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
createBookingUseCase (date, placeId).fold(
|
||||
onSuccess = {
|
||||
Log.d("AnnaKonda", "method is calling")
|
||||
_actionFlow.emit(BookAction.BookSuccess)
|
||||
},
|
||||
onFailure = { error ->
|
||||
Log.d("AnnaKonda", "ERROR method is calling")
|
||||
error.printStackTrace()
|
||||
_actionFlow.emit(BookAction.ShowError(error.message ?: App.context.getString(R.string.error_booking_default)))
|
||||
_uiState.update { currentState ->
|
||||
if (currentState is BookState.Data) {
|
||||
currentState.copy(
|
||||
isError = true
|
||||
)
|
||||
} else {
|
||||
currentState
|
||||
}
|
||||
}
|
||||
// _actionFlow.emit(BookAction.ShowError(error.message ?: App.context.getString(R.string.error_booking_default)))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import coil3.compose.AsyncImage
|
||||
import ru.myitschool.work.core.TestIds
|
||||
@@ -25,7 +26,7 @@ import ru.myitschool.work.ui.nav.BookScreenDestination
|
||||
fun MainScreen(
|
||||
navController: NavController,
|
||||
) {
|
||||
val viewModel = MainViewModel()
|
||||
val viewModel: MainViewModel = viewModel()
|
||||
// Состояния
|
||||
val event = viewModel.actionFlow.collectAsState(initial = null)
|
||||
// Функция загрузки данных
|
||||
|
||||
@@ -12,14 +12,10 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.myitschool.work.data.repo.AuthRepository
|
||||
import ru.myitschool.work.data.repo.MainRepository
|
||||
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
|
||||
import ru.myitschool.work.domain.main.GetUserDataUseCase
|
||||
import ru.myitschool.work.ui.screen.auth.AuthAction
|
||||
import ru.myitschool.work.ui.screen.auth.AuthIntent
|
||||
import ru.myitschool.work.ui.screen.auth.AuthState
|
||||
|
||||
class MainViewModel : ViewModel() {
|
||||
private val repository by lazy{ MainRepository() }
|
||||
private val repository by lazy { MainRepository() }
|
||||
private val getUserDataUseCase by lazy { GetUserDataUseCase(repository) }
|
||||
|
||||
private val _uiState = MutableStateFlow<MainState>(MainState.Loading)
|
||||
@@ -28,15 +24,12 @@ class MainViewModel : ViewModel() {
|
||||
private val _actionFlow: MutableSharedFlow<MainAction> = MutableSharedFlow()
|
||||
val actionFlow: SharedFlow<MainAction> = _actionFlow
|
||||
|
||||
init {
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onIntent(intent: MainIntent) {
|
||||
when (intent) {
|
||||
is MainIntent.LoadData -> {
|
||||
is MainIntent.LoadData -> {
|
||||
loadData()
|
||||
}
|
||||
|
||||
is MainIntent.LogOut -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_uiState.update { MainState.Data(null) }
|
||||
@@ -46,7 +39,7 @@ class MainViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun loadData() {
|
||||
private fun loadData() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_uiState.update { MainState.Loading }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user