main #6
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
|
||||
import ru.myitschool.work.data.entity.Place
|
||||
import java.time.LocalDate
|
||||
|
||||
sealed interface BookIntent {
|
||||
@@ -7,5 +8,5 @@ sealed interface BookIntent {
|
||||
object Refresh : BookIntent
|
||||
object BookPlace : 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.format.DateTimeFormatter
|
||||
import ru.myitschool.work.core.TestIds
|
||||
import ru.myitschool.work.data.entity.Place
|
||||
|
||||
@Composable
|
||||
fun BookScreen(
|
||||
@@ -65,7 +66,7 @@ fun BookScreen(
|
||||
fun BookContentScreen(
|
||||
uiState: BookState.Data,
|
||||
onSelectDate: (LocalDate) -> Unit,
|
||||
onSelectPlace: (String) -> Unit,
|
||||
onSelectPlace: (Place) -> Unit,
|
||||
onBook: () -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onRefresh: () -> Unit
|
||||
@@ -117,7 +118,7 @@ fun BookContentScreen(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = place,
|
||||
text = place.place,
|
||||
modifier = Modifier.weight(1f).testTag(TestIds.Book.ITEM_PLACE_TEXT)
|
||||
)
|
||||
RadioButton(
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package ru.myitschool.work.ui.screen.book
|
||||
|
||||
import ru.myitschool.work.data.entity.Place
|
||||
import java.time.LocalDate
|
||||
|
||||
sealed interface BookState {
|
||||
object Loading : BookState
|
||||
data class Data(
|
||||
val dates: List<LocalDate> = emptyList(),
|
||||
val places: Map<LocalDate, List<String>> = emptyMap(),
|
||||
val places: Map<LocalDate, List<Place>> = emptyMap(),
|
||||
val selectedDate: LocalDate? = null,
|
||||
val selectedPlace: String? = null,
|
||||
val selectedPlace: Place? = null,
|
||||
val isError: Boolean = false,
|
||||
val errorMessage: String? = null
|
||||
) : BookState
|
||||
|
||||
@@ -7,12 +7,21 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
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
|
||||
|
||||
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)
|
||||
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
|
||||
@@ -20,6 +29,8 @@ class BookViewModel : ViewModel() {
|
||||
private val _actionFlow = MutableSharedFlow<BookAction>()
|
||||
val actionFlow: SharedFlow<BookAction> = _actionFlow
|
||||
|
||||
private var selectedPlaceId: Long? = null
|
||||
|
||||
init {
|
||||
loadBookData()
|
||||
}
|
||||
@@ -38,86 +49,82 @@ class BookViewModel : ViewModel() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_uiState.update { BookState.Loading }
|
||||
|
||||
try {
|
||||
// Временные mock данные
|
||||
val mockDates = listOf(
|
||||
LocalDate.now().plusDays(1),
|
||||
LocalDate.now().plusDays(2),
|
||||
LocalDate.now().plusDays(3)
|
||||
)
|
||||
|
||||
val mockPlaces = mapOf(
|
||||
mockDates[0] to listOf("Место 1", "Место 2", "Место 3"),
|
||||
mockDates[1] to listOf("Место 1", "Место 2"),
|
||||
mockDates[2] to listOf("Место 1")
|
||||
)
|
||||
|
||||
val sortedDates = mockDates.sorted()
|
||||
val availableDates = sortedDates.filter { mockPlaces[it]?.isNotEmpty() == true }
|
||||
val defaultDate = availableDates.firstOrNull()
|
||||
|
||||
getAvailableBookingsUseCase().fold(
|
||||
onSuccess = { bookings ->
|
||||
if (bookings.isEmpty()) {
|
||||
_uiState.update {
|
||||
BookState.Data(
|
||||
dates = sortedDates,
|
||||
places = mockPlaces,
|
||||
selectedDate = defaultDate,
|
||||
isError = true,
|
||||
errorMessage = "Нет доступных дат для бронирования"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val dates = bookings.keys.toList()
|
||||
_uiState.update {
|
||||
BookState.Data(
|
||||
dates = dates,
|
||||
places = bookings,
|
||||
selectedDate = dates.first(),
|
||||
selectedPlace = null,
|
||||
isError = false,
|
||||
errorMessage = null
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
},
|
||||
onFailure = { error ->
|
||||
error.printStackTrace()
|
||||
_uiState.update {
|
||||
BookState.Data(
|
||||
isError = true,
|
||||
errorMessage = "Ошибка загрузки данных"
|
||||
errorMessage = error.message ?: "Ошибка загрузки данных"
|
||||
)
|
||||
}
|
||||
_actionFlow.emit(BookAction.ShowError("Ошибка загрузки данных"))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectDate(date: LocalDate) {
|
||||
_uiState.update { currentState ->
|
||||
when (currentState) {
|
||||
is BookState.Data -> currentState.copy(
|
||||
if (currentState is BookState.Data) {
|
||||
currentState.copy(
|
||||
selectedDate = date,
|
||||
selectedPlace = null
|
||||
)
|
||||
else -> currentState
|
||||
} else {
|
||||
currentState
|
||||
}
|
||||
}
|
||||
selectedPlaceId = null
|
||||
}
|
||||
|
||||
private fun selectPlace(place: String) {
|
||||
private fun selectPlace(place: Place) {
|
||||
_uiState.update { currentState ->
|
||||
when (currentState) {
|
||||
is BookState.Data -> currentState.copy(selectedPlace = place)
|
||||
else -> currentState
|
||||
if (currentState is BookState.Data) {
|
||||
currentState.copy(selectedPlace = place)
|
||||
} else {
|
||||
currentState
|
||||
}
|
||||
}
|
||||
selectedPlaceId = place.id
|
||||
}
|
||||
|
||||
private fun bookPlace() {
|
||||
/*
|
||||
selectedPlaceId?.let { placeId ->
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
// вызов API для бронирования
|
||||
// временная имитация успеха
|
||||
createBookingUseCase(placeId).fold(
|
||||
onSuccess = {
|
||||
_actionFlow.emit(BookAction.BookSuccess)
|
||||
} catch (e: Exception) {
|
||||
_uiState.update { currentState ->
|
||||
when (currentState) {
|
||||
is BookState.Data -> currentState.copy(
|
||||
isError = true,
|
||||
errorMessage = "Ошибка бронирования"
|
||||
},
|
||||
onFailure = { error ->
|
||||
error.printStackTrace()
|
||||
_actionFlow.emit(BookAction.ShowError(error.message ?: "Ошибка бронирования"))
|
||||
}
|
||||
)
|
||||
else -> currentState
|
||||
}
|
||||
}
|
||||
_actionFlow.emit(BookAction.ShowError("Ошибка бронирования"))
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
|
||||
Reference in New Issue
Block a user