main #6

Closed
student-20690 wants to merge 20 commits from (deleted):main into main
6 changed files with 49 additions and 38 deletions
Showing only changes of commit 77f27e52eb - Show all commits

View File

@@ -1,7 +1,7 @@
package ru.myitschool.work.core package ru.myitschool.work.core
object Constants { 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 AUTH_URL = "/auth"
const val INFO_URL = "/info" const val INFO_URL = "/info"
const val BOOKING_URL = "/booking" const val BOOKING_URL = "/booking"

View File

@@ -1,5 +1,6 @@
package ru.myitschool.work.data.source package ru.myitschool.work.data.source
import android.annotation.SuppressLint
import android.util.Log import android.util.Log
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
@@ -151,20 +152,20 @@ object NetworkDataSource {
} }
@Serializable @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) { suspend fun createBooking(code: String, date: LocalDate, placeId: Long): Result<Boolean> = withContext(Dispatchers.IO) {
return@withContext runCatching { return@withContext runCatching {
// Формируем тело запроса // Формируем тело запроса
val requestBody = CreateBookingBody(date.toString(), placeId) 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) contentType(ContentType.Application.Json)
setBody(requestBody) setBody(requestBody)
} }
when (response.status) { when (response.status) {
HttpStatusCode.OK -> true HttpStatusCode.Created -> true
else -> { else -> {
val errorBody = response.bodyAsText() 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)) error(if (errorBody.isNotBlank()) App.context.getString(R.string.error_booking, errorBody) else App.context.getString(R.string.error_booking_default))

View File

@@ -1,19 +1,34 @@
package ru.myitschool.work.ui.screen.book 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.foundation.selection.selectable
import androidx.compose.material3.* import androidx.compose.material3.Button
import androidx.compose.runtime.* 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel 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.core.TestIds
import ru.myitschool.work.data.entity.Place import ru.myitschool.work.data.entity.Place
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@Composable @Composable
fun BookScreen( fun BookScreen(
@@ -23,23 +38,19 @@ fun BookScreen(
val viewModel: BookViewModel = viewModel() val viewModel: BookViewModel = viewModel()
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
// Обработка действий LaunchedEffect(viewModel.actionFlow) {
val event = viewModel.actionFlow.collectAsState(initial = null) viewModel.actionFlow.collect { action ->
LaunchedEffect(event.value) { if (action is BookAction.BookSuccess) {
when (event.value) {
is BookAction.BookSuccess -> {
onBookSuccess() onBookSuccess()
} }
else -> {}
} }
} }
// Загрузка начальных данных
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.onIntent(BookIntent.LoadData) viewModel.onIntent(BookIntent.LoadData)
} }
when (uiState) { when (val state = uiState) {
is BookState.Loading -> { is BookState.Loading -> {
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@@ -51,7 +62,7 @@ fun BookScreen(
is BookState.Data -> { is BookState.Data -> {
BookContentScreen( BookContentScreen(
uiState = uiState as BookState.Data, uiState = state,
onSelectDate = { date -> viewModel.onIntent(BookIntent.SelectDate(date)) }, onSelectDate = { date -> viewModel.onIntent(BookIntent.SelectDate(date)) },
onSelectPlace = { place -> viewModel.onIntent(BookIntent.SelectPlace(place)) }, onSelectPlace = { place -> viewModel.onIntent(BookIntent.SelectPlace(place)) },
onBook = { viewModel.onIntent(BookIntent.BookPlace) }, onBook = { viewModel.onIntent(BookIntent.BookPlace) },
@@ -71,13 +82,10 @@ fun BookContentScreen(
onBack: () -> Unit, onBack: () -> Unit,
onRefresh: () -> Unit onRefresh: () -> Unit
) { ) {
// Сортировка дат по порядку
val sortedDates = uiState.dates.sorted() val sortedDates = uiState.dates.sorted()
// Фильтрация дат, для которых есть доступные места
val availableDates = sortedDates.filter { date -> uiState.places[date]?.isNotEmpty() == true } val availableDates = sortedDates.filter { date -> uiState.places[date]?.isNotEmpty() == true }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// Вкладки для выбора дат
if (availableDates.isNotEmpty()) { if (availableDates.isNotEmpty()) {
ScrollableTabRow( ScrollableTabRow(
selectedTabIndex = availableDates.indexOf(uiState.selectedDate), selectedTabIndex = availableDates.indexOf(uiState.selectedDate),
@@ -100,7 +108,6 @@ fun BookContentScreen(
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
// Список мест для выбранной даты
val placesForDate = uiState.selectedDate?.let { uiState.places[it] } ?: emptyList() val placesForDate = uiState.selectedDate?.let { uiState.places[it] } ?: emptyList()
if (placesForDate.isNotEmpty()) { if (placesForDate.isNotEmpty()) {
@@ -131,7 +138,6 @@ fun BookContentScreen(
} }
} }
// пустой список (все забронировано)
if (availableDates.isEmpty() && !uiState.isError) { if (availableDates.isEmpty() && !uiState.isError) {
Text( Text(
text = "Всё забронировано", text = "Всё забронировано",
@@ -139,7 +145,6 @@ fun BookContentScreen(
) )
} }
// ошибка
if (uiState.isError) { if (uiState.isError) {
Text( Text(
text = uiState.errorMessage ?: "Ошибка загрузки", text = uiState.errorMessage ?: "Ошибка загрузки",
@@ -157,7 +162,6 @@ fun BookContentScreen(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
// Кнопки: Забронировать и Назад
if (!uiState.isError && placesForDate.isNotEmpty()) { if (!uiState.isError && placesForDate.isNotEmpty()) {
Button( Button(
onClick = onBook, onClick = onBook,

View File

@@ -1,5 +1,6 @@
package ru.myitschool.work.ui.screen.book package ru.myitschool.work.ui.screen.book
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -121,11 +122,22 @@ class BookViewModel : ViewModel() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
createBookingUseCase (date, placeId).fold( createBookingUseCase (date, placeId).fold(
onSuccess = { onSuccess = {
Log.d("AnnaKonda", "method is calling")
_actionFlow.emit(BookAction.BookSuccess) _actionFlow.emit(BookAction.BookSuccess)
}, },
onFailure = { error -> onFailure = { error ->
Log.d("AnnaKonda", "ERROR method is calling")
error.printStackTrace() 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)))
} }
) )
} }

View File

@@ -13,6 +13,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import ru.myitschool.work.core.TestIds import ru.myitschool.work.core.TestIds
@@ -25,7 +26,7 @@ import ru.myitschool.work.ui.nav.BookScreenDestination
fun MainScreen( fun MainScreen(
navController: NavController, navController: NavController,
) { ) {
val viewModel = MainViewModel() val viewModel: MainViewModel = viewModel()
// Состояния // Состояния
val event = viewModel.actionFlow.collectAsState(initial = null) val event = viewModel.actionFlow.collectAsState(initial = null)
// Функция загрузки данных // Функция загрузки данных

View File

@@ -12,14 +12,10 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.myitschool.work.data.repo.AuthRepository import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.data.repo.MainRepository 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.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() { class MainViewModel : ViewModel() {
private val repository by lazy{ MainRepository() } private val repository by lazy { MainRepository() }
private val getUserDataUseCase by lazy { GetUserDataUseCase(repository) } private val getUserDataUseCase by lazy { GetUserDataUseCase(repository) }
private val _uiState = MutableStateFlow<MainState>(MainState.Loading) private val _uiState = MutableStateFlow<MainState>(MainState.Loading)
@@ -28,15 +24,12 @@ class MainViewModel : ViewModel() {
private val _actionFlow: MutableSharedFlow<MainAction> = MutableSharedFlow() private val _actionFlow: MutableSharedFlow<MainAction> = MutableSharedFlow()
val actionFlow: SharedFlow<MainAction> = _actionFlow val actionFlow: SharedFlow<MainAction> = _actionFlow
init {
loadData()
}
fun onIntent(intent: MainIntent) { fun onIntent(intent: MainIntent) {
when (intent) { when (intent) {
is MainIntent.LoadData -> { is MainIntent.LoadData -> {
loadData() loadData()
} }
is MainIntent.LogOut -> { is MainIntent.LogOut -> {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
_uiState.update { MainState.Data(null) } _uiState.update { MainState.Data(null) }
@@ -46,7 +39,7 @@ class MainViewModel : ViewModel() {
} }
} }
fun loadData() { private fun loadData() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
_uiState.update { MainState.Loading } _uiState.update { MainState.Loading }