main #6

Closed
student-20690 wants to merge 20 commits from (deleted):main into main
7 changed files with 137 additions and 65 deletions
Showing only changes of commit ab03679731 - Show all commits

View File

@@ -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)
}
}

View File

@@ -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"
}

View File

@@ -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()
}
}

View File

@@ -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
}

View File

@@ -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(

View File

@@ -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

View File

@@ -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()
_uiState.update {
BookState.Data(
dates = sortedDates,
places = mockPlaces,
selectedDate = defaultDate,
selectedPlace = null,
isError = false,
errorMessage = null
)
getAvailableBookingsUseCase().fold(
onSuccess = { bookings ->
if (bookings.isEmpty()) {
_uiState.update {
BookState.Data(
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
)
}
}
},
onFailure = { error ->
error.printStackTrace()
_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) {
_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() {
viewModelScope.launch(Dispatchers.IO) {
try {
// вызов API для бронирования
// временная имитация успеха
_actionFlow.emit(BookAction.BookSuccess)
} catch (e: Exception) {
_uiState.update { currentState ->
when (currentState) {
is BookState.Data -> currentState.copy(
isError = true,
errorMessage = "Ошибка бронирования"
)
else -> currentState
/*
selectedPlaceId?.let { placeId ->
viewModelScope.launch(Dispatchers.IO) {
createBookingUseCase(placeId).fold(
onSuccess = {
_actionFlow.emit(BookAction.BookSuccess)
},
onFailure = { error ->
error.printStackTrace()
_actionFlow.emit(BookAction.ShowError(error.message ?: "Ошибка бронирования"))
}
}
_actionFlow.emit(BookAction.ShowError("Ошибка бронирования"))
)
}
}
}*/
}
private fun refresh() {