main #6
@@ -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"
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
// Функция загрузки данных
|
// Функция загрузки данных
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ 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() }
|
||||||
@@ -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 }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user