booking with server
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
package ru.myitschool.work.data.repo
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.entity.Place
|
||||||
|
import ru.myitschool.work.data.source.DataStoreDataSource
|
||||||
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
class BookingRepository {
|
||||||
|
|
||||||
|
suspend fun getAvailableBookings(): Result<Map<LocalDate, List<Place>>> {
|
||||||
|
val code = DataStoreDataSource.getAuthCode()
|
||||||
|
if (code.isEmpty() || code == "0") {
|
||||||
|
return Result.failure(Exception("Auth code not found"))
|
||||||
|
}
|
||||||
|
return NetworkDataSource.getAvailableBookings(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,5 +109,37 @@ object NetworkDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getAvailableBookings(code: String): Result<Map<LocalDate, List<Place>>> = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext runCatching {
|
||||||
|
val response = client.get(getUrl(code, Constants.BOOKING_URL))
|
||||||
|
|
||||||
|
when (response.status) {
|
||||||
|
HttpStatusCode.OK -> {
|
||||||
|
val json = response.bodyAsText()
|
||||||
|
val jsonObject = Json.parseToJsonElement(json).jsonObject
|
||||||
|
val availableBookings = mutableMapOf<LocalDate, List<Place>>()
|
||||||
|
|
||||||
|
for ((dateString, placesArray) in jsonObject) {
|
||||||
|
val date = LocalDate.parse(dateString)
|
||||||
|
val places = placesArray.jsonArray.map { placeElement ->
|
||||||
|
val placeObj = placeElement.jsonObject
|
||||||
|
val id = placeObj["id"]?.jsonPrimitive?.long
|
||||||
|
?: error("Missing 'id' in place")
|
||||||
|
val placeName = placeObj["place"]?.jsonPrimitive?.content
|
||||||
|
?: error("Missing 'place' in place")
|
||||||
|
Place(id, placeName)
|
||||||
|
}
|
||||||
|
if (places.isNotEmpty()) {
|
||||||
|
availableBookings[date] = places
|
||||||
|
}
|
||||||
|
}
|
||||||
|
availableBookings.toSortedMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> error("Request error: ${response.bodyAsText()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
|
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package ru.myitschool.work.domain.book
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.entity.Place
|
||||||
|
import ru.myitschool.work.data.repo.BookingRepository
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
class GetAvailableBookingsUseCase(
|
||||||
|
private val repository: BookingRepository
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(): Result<Map<LocalDate, List<Place>>> {
|
||||||
|
return repository.getAvailableBookings()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package ru.myitschool.work.ui.screen.book
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.entity.Place
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
sealed interface BookIntent {
|
sealed interface BookIntent {
|
||||||
@@ -7,5 +8,5 @@ sealed interface BookIntent {
|
|||||||
object Refresh : BookIntent
|
object Refresh : BookIntent
|
||||||
object BookPlace : BookIntent
|
object BookPlace : BookIntent
|
||||||
data class SelectDate(val date: LocalDate) : BookIntent
|
data class SelectDate(val date: LocalDate) : BookIntent
|
||||||
data class SelectPlace(val place: String) : BookIntent
|
data class SelectPlace(val place: Place) : BookIntent
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import ru.myitschool.work.core.TestIds
|
import ru.myitschool.work.core.TestIds
|
||||||
|
import ru.myitschool.work.data.entity.Place
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BookScreen(
|
fun BookScreen(
|
||||||
@@ -65,7 +66,7 @@ fun BookScreen(
|
|||||||
fun BookContentScreen(
|
fun BookContentScreen(
|
||||||
uiState: BookState.Data,
|
uiState: BookState.Data,
|
||||||
onSelectDate: (LocalDate) -> Unit,
|
onSelectDate: (LocalDate) -> Unit,
|
||||||
onSelectPlace: (String) -> Unit,
|
onSelectPlace: (Place) -> Unit,
|
||||||
onBook: () -> Unit,
|
onBook: () -> Unit,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onRefresh: () -> Unit
|
onRefresh: () -> Unit
|
||||||
@@ -117,7 +118,7 @@ fun BookContentScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = place,
|
text = place.place,
|
||||||
modifier = Modifier.weight(1f).testTag(TestIds.Book.ITEM_PLACE_TEXT)
|
modifier = Modifier.weight(1f).testTag(TestIds.Book.ITEM_PLACE_TEXT)
|
||||||
)
|
)
|
||||||
RadioButton(
|
RadioButton(
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package ru.myitschool.work.ui.screen.book
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.entity.Place
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
sealed interface BookState {
|
sealed interface BookState {
|
||||||
object Loading : BookState
|
object Loading : BookState
|
||||||
data class Data(
|
data class Data(
|
||||||
val dates: List<LocalDate> = emptyList(),
|
val dates: List<LocalDate> = emptyList(),
|
||||||
val places: Map<LocalDate, List<String>> = emptyMap(),
|
val places: Map<LocalDate, List<Place>> = emptyMap(),
|
||||||
val selectedDate: LocalDate? = null,
|
val selectedDate: LocalDate? = null,
|
||||||
val selectedPlace: String? = null,
|
val selectedPlace: Place? = null,
|
||||||
val isError: Boolean = false,
|
val isError: Boolean = false,
|
||||||
val errorMessage: String? = null
|
val errorMessage: String? = null
|
||||||
) : BookState
|
) : BookState
|
||||||
|
|||||||
@@ -7,12 +7,21 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.data.entity.Place
|
||||||
|
import ru.myitschool.work.data.repo.BookingRepository
|
||||||
|
// import ru.myitschool.work.domain.book.CreateBookingUseCase
|
||||||
|
import ru.myitschool.work.domain.book.GetAvailableBookingsUseCase
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
class BookViewModel : ViewModel() {
|
class BookViewModel : ViewModel() {
|
||||||
|
private val repository by lazy { BookingRepository() }
|
||||||
|
private val getAvailableBookingsUseCase by lazy { GetAvailableBookingsUseCase(repository) }
|
||||||
|
// private val createBookingUseCase by lazy { CreateBookingUseCase(repository) }
|
||||||
|
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
|
private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
|
||||||
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
|
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
|
||||||
@@ -20,6 +29,8 @@ class BookViewModel : ViewModel() {
|
|||||||
private val _actionFlow = MutableSharedFlow<BookAction>()
|
private val _actionFlow = MutableSharedFlow<BookAction>()
|
||||||
val actionFlow: SharedFlow<BookAction> = _actionFlow
|
val actionFlow: SharedFlow<BookAction> = _actionFlow
|
||||||
|
|
||||||
|
private var selectedPlaceId: Long? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadBookData()
|
loadBookData()
|
||||||
}
|
}
|
||||||
@@ -38,86 +49,82 @@ class BookViewModel : ViewModel() {
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
_uiState.update { BookState.Loading }
|
_uiState.update { BookState.Loading }
|
||||||
|
|
||||||
try {
|
getAvailableBookingsUseCase().fold(
|
||||||
// Временные mock данные
|
onSuccess = { bookings ->
|
||||||
val mockDates = listOf(
|
if (bookings.isEmpty()) {
|
||||||
LocalDate.now().plusDays(1),
|
_uiState.update {
|
||||||
LocalDate.now().plusDays(2),
|
BookState.Data(
|
||||||
LocalDate.now().plusDays(3)
|
isError = true,
|
||||||
)
|
errorMessage = "Нет доступных дат для бронирования"
|
||||||
|
)
|
||||||
val mockPlaces = mapOf(
|
}
|
||||||
mockDates[0] to listOf("Место 1", "Место 2", "Место 3"),
|
} else {
|
||||||
mockDates[1] to listOf("Место 1", "Место 2"),
|
val dates = bookings.keys.toList()
|
||||||
mockDates[2] to listOf("Место 1")
|
_uiState.update {
|
||||||
)
|
BookState.Data(
|
||||||
|
dates = dates,
|
||||||
val sortedDates = mockDates.sorted()
|
places = bookings,
|
||||||
val availableDates = sortedDates.filter { mockPlaces[it]?.isNotEmpty() == true }
|
selectedDate = dates.first(),
|
||||||
val defaultDate = availableDates.firstOrNull()
|
selectedPlace = null,
|
||||||
|
isError = false,
|
||||||
_uiState.update {
|
errorMessage = null
|
||||||
BookState.Data(
|
)
|
||||||
dates = sortedDates,
|
}
|
||||||
places = mockPlaces,
|
}
|
||||||
selectedDate = defaultDate,
|
},
|
||||||
selectedPlace = null,
|
onFailure = { error ->
|
||||||
isError = false,
|
error.printStackTrace()
|
||||||
errorMessage = null
|
_uiState.update {
|
||||||
)
|
BookState.Data(
|
||||||
|
isError = true,
|
||||||
|
errorMessage = error.message ?: "Ошибка загрузки данных"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
)
|
||||||
_uiState.update {
|
|
||||||
BookState.Data(
|
|
||||||
isError = true,
|
|
||||||
errorMessage = "Ошибка загрузки данных"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_actionFlow.emit(BookAction.ShowError("Ошибка загрузки данных"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectDate(date: LocalDate) {
|
private fun selectDate(date: LocalDate) {
|
||||||
_uiState.update { currentState ->
|
_uiState.update { currentState ->
|
||||||
when (currentState) {
|
if (currentState is BookState.Data) {
|
||||||
is BookState.Data -> currentState.copy(
|
currentState.copy(
|
||||||
selectedDate = date,
|
selectedDate = date,
|
||||||
selectedPlace = null
|
selectedPlace = null
|
||||||
)
|
)
|
||||||
else -> currentState
|
} else {
|
||||||
|
currentState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectedPlaceId = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectPlace(place: String) {
|
private fun selectPlace(place: Place) {
|
||||||
_uiState.update { currentState ->
|
_uiState.update { currentState ->
|
||||||
when (currentState) {
|
if (currentState is BookState.Data) {
|
||||||
is BookState.Data -> currentState.copy(selectedPlace = place)
|
currentState.copy(selectedPlace = place)
|
||||||
else -> currentState
|
} else {
|
||||||
|
currentState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectedPlaceId = place.id
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bookPlace() {
|
private fun bookPlace() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
/*
|
||||||
try {
|
selectedPlaceId?.let { placeId ->
|
||||||
// вызов API для бронирования
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
// временная имитация успеха
|
createBookingUseCase(placeId).fold(
|
||||||
_actionFlow.emit(BookAction.BookSuccess)
|
onSuccess = {
|
||||||
} catch (e: Exception) {
|
_actionFlow.emit(BookAction.BookSuccess)
|
||||||
_uiState.update { currentState ->
|
},
|
||||||
when (currentState) {
|
onFailure = { error ->
|
||||||
is BookState.Data -> currentState.copy(
|
error.printStackTrace()
|
||||||
isError = true,
|
_actionFlow.emit(BookAction.ShowError(error.message ?: "Ошибка бронирования"))
|
||||||
errorMessage = "Ошибка бронирования"
|
|
||||||
)
|
|
||||||
else -> currentState
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
_actionFlow.emit(BookAction.ShowError("Ошибка бронирования"))
|
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refresh() {
|
private fun refresh() {
|
||||||
|
|||||||
Reference in New Issue
Block a user