From 2807368adba470136d627a38a162fd7264646b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC?= Date: Sun, 30 Nov 2025 17:52:39 +0300 Subject: [PATCH 1/2] Added appearance and basic booking functionality --- .../work/domain/entities/BookingEntities.kt | 6 + .../work/domain/entities/UserEntities.kt | 7 + .../work/ui/screen/NavigationGraph.kt | 7 +- .../work/ui/screen/book/BookIntent.kt | 6 + .../work/ui/screen/book/BookScreen.kt | 215 ++++++++++++++++++ .../work/ui/screen/book/BookState.kt | 8 + .../work/ui/screen/book/BookViewModel.kt | 31 +++ app/src/main/res/values/strings.xml | 2 + 8 files changed, 277 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt b/app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt new file mode 100644 index 0000000..9cc4cad --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt @@ -0,0 +1,6 @@ +package ru.myitschool.work.domain.entities + +data class BookingEntities ( + var roomName : String, + var time: String +) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt new file mode 100644 index 0000000..23d2aba --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.domain.entities + +data class UserEntities( + val name : String, + var image : Int, + var booking : ArrayList +) diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt index 01b0f32..9dad87b 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt @@ -15,6 +15,7 @@ import ru.myitschool.work.ui.nav.AuthScreenDestination import ru.myitschool.work.ui.nav.BookScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.screen.auth.AuthScreen +import ru.myitschool.work.ui.screen.book.BookScreen @Composable fun AppNavHost( @@ -39,11 +40,7 @@ fun AppNavHost( } } composable { - Box( - contentAlignment = Alignment.Center - ) { - Text(text = "Hello") - } + BookScreen(navController = navController) } } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt new file mode 100644 index 0000000..506f720 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt @@ -0,0 +1,6 @@ +package ru.myitschool.work.ui.screen.book + +sealed interface BookIntent { + data class Send(val text: String): BookIntent + data class BookingSelect(val text: String): BookIntent +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt new file mode 100644 index 0000000..7dc159f --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt @@ -0,0 +1,215 @@ +package ru.myitschool.work.ui.screen.book; + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.RadioButton +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.runtime.mutableStateOf +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController; +import ru.myitschool.work.R +import ru.myitschool.work.core.TestIds +import ru.myitschool.work.domain.entities.BookingEntities +import ru.myitschool.work.ui.nav.BookScreenDestination +import ru.myitschool.work.ui.nav.MainScreenDestination +import ru.myitschool.work.ui.screen.auth.AuthViewModel + +var selectedTime = mutableStateOf(0) +var selectedBooking = mutableStateOf(0) +var currentTime = mutableStateOf(0) +@Composable +fun BookScreen( + viewModel: BookViewModel = viewModel(), + navController: NavController, + modifier: Modifier = Modifier +) { + val state by viewModel.uiState.collectAsState() + + LaunchedEffect(Unit) { + viewModel.actionFlow.collect { + navController.navigate(MainScreenDestination) + } + } + + // TODO брать данные с сервера + // Иммитация того, что мы взяли данные с сервера + val bookings = arrayListOf( + BookingEntities( + "Рабочее место у окна", + "19.04" + ), + BookingEntities( + "Переговорная комната № 1", + "19.04" + ), + BookingEntities( + "Коворкинг А", + "19.04" + ), + BookingEntities( + "Кабинет № 33", + "20.04" + ), + ) + val options = toMap(bookings) + + Column( + modifier = modifier.fillMaxSize() + ) { +// Text("" + selectedTime.value + "," + currentTime.value + "," + selectedBooking.value) + when (val currentState = state) { + BookState.Data -> { + TabGroup(options.keys) + + var i = 0 + options.keys.forEach { + if (i == currentTime.value) + SelectBooking(options[it]!!) + i ++; + } + + Button( + onClick = { + // TODO Добавить бронирование + viewModel.onIntent(BookIntent.Send("Данные" )) + }, + modifier = Modifier + .testTag(TestIds.Book.BOOK_BUTTON) + ) { + Text(stringResource(R.string.to_book)) + } + } + + BookState.Error -> { + Text( + text = "", + modifier = Modifier + .testTag(TestIds.Book.ERROR) + ) + + Button( + onClick = { }, + modifier = Modifier + .testTag(TestIds.Book.REFRESH_BUTTON) + ) { + Text("") + } + } + + BookState.Loading -> { + CircularProgressIndicator( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .size(64.dp) + ) + } + + BookState.NotData -> { + Text( + text = "", + modifier = Modifier + .testTag(TestIds.Book.EMPTY) + ) + } + } + Button( + onClick = { + navController.navigate(MainScreenDestination) + }, + modifier = Modifier + .testTag(TestIds.Book.BACK_BUTTON) + ) { + Text(stringResource(R.string.back)) + } + } +} + + +@Composable +fun TabGroup(options: Set) { + NavigationBar( + Modifier.fillMaxWidth() + ) { + options.forEachIndexed { index, label -> + NavigationBarItem( + selected = currentTime.value == index, + onClick = { + currentTime.value = index + }, + icon = { + Text( + text = label, + modifier = Modifier + .testTag(TestIds.Book.ITEM_DATE) + ) + }, + modifier = Modifier + .testTag(TestIds.Book.getIdDateItemByPosition(index)) + ) + } + } +} + +@Composable +fun SelectBooking(options: List) { + LazyColumn( + Modifier + .fillMaxWidth() + ) { + options.forEachIndexed { index, label -> + item { + Row( + Modifier + .fillMaxWidth() + .selectableGroup() + .testTag(TestIds.Book.getIdPlaceItemByPosition(index)), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = label, + modifier = Modifier + .testTag(TestIds.Book.ITEM_PLACE_TEXT) + ) + RadioButton( + selected = index == selectedBooking.value && currentTime.value == selectedTime.value, + onClick = { + selectedBooking.value = index + selectedTime.value = currentTime.value + }, + modifier = Modifier + .testTag(TestIds.Book.ITEM_PLACE_SELECTOR) + ) + } + } + } + } +} + +fun toMap(options: List) : Map> { + val map : MutableMap> = mutableMapOf() + options.forEach { + if (map[it.time] == null) map[it.time] = mutableListOf(it.roomName) + else map[it.time]?.add(it.roomName) + } + return map +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt new file mode 100644 index 0000000..8b87d87 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt @@ -0,0 +1,8 @@ +package ru.myitschool.work.ui.screen.book + +sealed interface BookState { + object Loading: BookState + object Data: BookState + object Error: BookState + object NotData : BookState +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt new file mode 100644 index 0000000..c713202 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt @@ -0,0 +1,31 @@ +package ru.myitschool.work.ui.screen.book + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class BookViewModel : ViewModel() { + private val _uiState = MutableStateFlow(BookState.Data) + val uiState: StateFlow = _uiState.asStateFlow(); + + private val _actionFlow: MutableSharedFlow = MutableSharedFlow() + val actionFlow: SharedFlow = _actionFlow + + fun onIntent(intent: BookIntent) { + when (intent) { + is BookIntent.Send -> { + viewModelScope.launch(Dispatchers.Default) { + _uiState.update { BookState.Loading } + } + } + is BookIntent.BookingSelect -> Unit + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa8bda6..5979b52 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,4 +4,6 @@ Привет! Введи код для авторизации Код Войти + забронировать + Назад \ No newline at end of file From 09e97f9d67d932249d63cf01a899de3e36d0a6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC?= Date: Thu, 4 Dec 2025 23:19:42 +0300 Subject: [PATCH 2/2] I configured the server connection and improved the logic --- .../java/ru/myitschool/work/core/Constants.kt | 2 +- .../work/data/repo/BookRepository.kt | 15 +++ .../work/data/source/NetworkDataSource.kt | 41 ++++++ .../work/domain/entities/BookingEntities.kt | 6 - .../work/domain/entities/UserEntities.kt | 7 - .../work/ui/screen/BookingUserInfo.kt | 16 +++ .../work/ui/screen/NavigationGraph.kt | 7 +- .../work/ui/screen/book/BookIntent.kt | 8 +- .../work/ui/screen/book/BookScreen.kt | 124 +++++++++--------- .../work/ui/screen/book/BookState.kt | 10 +- .../work/ui/screen/book/BookViewModel.kt | 98 +++++++++++++- app/src/main/res/values/strings.xml | 2 + 12 files changed, 250 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt delete mode 100644 app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt delete mode 100644 app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/BookingUserInfo.kt diff --git a/app/src/main/java/ru/myitschool/work/core/Constants.kt b/app/src/main/java/ru/myitschool/work/core/Constants.kt index a8b7cc5..1aae4fd 100644 --- a/app/src/main/java/ru/myitschool/work/core/Constants.kt +++ b/app/src/main/java/ru/myitschool/work/core/Constants.kt @@ -1,7 +1,7 @@ package ru.myitschool.work.core object Constants { - const val HOST = "http://10.0.2.2:8080" + const val HOST = "http://127.0.0.1:8080" const val AUTH_URL = "/auth" const val INFO_URL = "/info" const val BOOKING_URL = "/booking" diff --git a/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt new file mode 100644 index 0000000..2887f8e --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt @@ -0,0 +1,15 @@ +package ru.myitschool.work.data.repo + +import ru.myitschool.work.data.source.NetworkDataSource +import ru.myitschool.work.ui.screen.Booking +import ru.myitschool.work.ui.screen.UserInfo + +object BookRepository { + suspend fun loadBooks(code: String): Result> { + return NetworkDataSource.getFreeBooking(code) + } + + suspend fun sendData(code: String, booking: Booking) : Result { + return NetworkDataSource.createNewBooking(code, booking) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt index fbdfef5..62c7504 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt @@ -1,16 +1,23 @@ package ru.myitschool.work.data.source import io.ktor.client.HttpClient +import io.ktor.client.call.body import io.ktor.client.engine.cio.CIO import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.get +import io.ktor.client.request.post import io.ktor.client.statement.bodyAsText +import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType import io.ktor.serialization.kotlinx.json.json +import io.ktor.utils.io.InternalAPI import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import ru.myitschool.work.core.Constants +import ru.myitschool.work.ui.screen.Booking +import ru.myitschool.work.ui.screen.UserInfo object NetworkDataSource { private val client by lazy { @@ -31,6 +38,40 @@ object NetworkDataSource { suspend fun checkAuth(code: String): Result = withContext(Dispatchers.IO) { return@withContext runCatching { val response = client.get(getUrl(code, Constants.AUTH_URL)) + when (response.status) { + HttpStatusCode.OK -> true + else -> error("Неверный код для авторизации") + } + } + } + + suspend fun getInfo(code: String): Result = withContext(Dispatchers.IO){ + return@withContext runCatching { + val response = client.get(getUrl(code, Constants.INFO_URL)) + when(response.status){ + HttpStatusCode.OK -> response.body() + else -> error("Ошибка получения данных") + } + } + } + + suspend fun getFreeBooking(code: String) : Result> = withContext(Dispatchers.IO) { + return@withContext runCatching { + val response = client.get(getUrl(code, Constants.BOOKING_URL)) + when (response.status) { + HttpStatusCode.OK -> response.body() + else -> error(response.bodyAsText()) + } + } + } + + @OptIn(InternalAPI::class) + suspend fun createNewBooking(code: String, booking: Booking) : Result = withContext(Dispatchers.IO) { + return@withContext runCatching { + val response = client.post(getUrl(code, Constants.BOOK_URL)) { + contentType(ContentType.Application.Json) + body = booking + } when (response.status) { HttpStatusCode.OK -> true else -> error(response.bodyAsText()) diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt b/app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt deleted file mode 100644 index 9cc4cad..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/entities/BookingEntities.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.myitschool.work.domain.entities - -data class BookingEntities ( - var roomName : String, - var time: String -) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt deleted file mode 100644 index 23d2aba..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntities.kt +++ /dev/null @@ -1,7 +0,0 @@ -package ru.myitschool.work.domain.entities - -data class UserEntities( - val name : String, - var image : Int, - var booking : ArrayList -) diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/BookingUserInfo.kt b/app/src/main/java/ru/myitschool/work/ui/screen/BookingUserInfo.kt new file mode 100644 index 0000000..39c3e4b --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/BookingUserInfo.kt @@ -0,0 +1,16 @@ +package ru.myitschool.work.ui.screen + +import kotlinx.serialization.Serializable + +@Serializable +data class UserInfo( + val name: String, + val photo: String? = null, + val bookings: List +) + +@Serializable +data class Booking( + val date: String, + val place: String +) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt index 9dad87b..b70bbc0 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -16,6 +17,7 @@ import ru.myitschool.work.ui.nav.BookScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.screen.auth.AuthScreen import ru.myitschool.work.ui.screen.book.BookScreen +import ru.myitschool.work.ui.screen.book.BookViewModel @Composable fun AppNavHost( @@ -40,7 +42,10 @@ fun AppNavHost( } } composable { - BookScreen(navController = navController) + BookScreen( + viewModel = BookViewModel(), + navController = navController + ) } } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt index 506f720..8d77d08 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt @@ -1,6 +1,10 @@ package ru.myitschool.work.ui.screen.book +import ru.myitschool.work.ui.screen.Booking + sealed interface BookIntent { - data class Send(val text: String): BookIntent - data class BookingSelect(val text: String): BookIntent + data class Send(val code : String, val booking: Booking) : BookIntent + data object BackToMainScreen : BookIntent + data object ToAuthScreen : BookIntent + data object LoadData : BookIntent } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt index 7dc159f..47dd7ad 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt @@ -1,5 +1,6 @@ package ru.myitschool.work.ui.screen.book; +import android.util.Log import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -10,12 +11,14 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.selection.selectableGroup import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.RadioButton import androidx.compose.material3.Text -import androidx.compose.runtime.Composable; +import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -25,17 +28,17 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavController; +import androidx.navigation.NavController import ru.myitschool.work.R import ru.myitschool.work.core.TestIds -import ru.myitschool.work.domain.entities.BookingEntities -import ru.myitschool.work.ui.nav.BookScreenDestination +import ru.myitschool.work.data.repo.AuthRepository +import ru.myitschool.work.ui.nav.AuthScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination -import ru.myitschool.work.ui.screen.auth.AuthViewModel +import ru.myitschool.work.ui.screen.Booking -var selectedTime = mutableStateOf(0) -var selectedBooking = mutableStateOf(0) -var currentTime = mutableStateOf(0) +var selectedTime: MutableState = mutableStateOf(null) +var selectedBooking: MutableState = mutableStateOf(null) +var currentTime: MutableState = mutableStateOf(null) @Composable fun BookScreen( viewModel: BookViewModel = viewModel(), @@ -45,73 +48,72 @@ fun BookScreen( val state by viewModel.uiState.collectAsState() LaunchedEffect(Unit) { - viewModel.actionFlow.collect { - navController.navigate(MainScreenDestination) + Log.d("BookScreen", "1") + viewModel.navigationFlow.collect { + // TODO настроить наконец это переход между экранами +// Log.d("BookScreen", "2") + when (it) { + BookNavigationEvent.NavigateToMain -> { + Log.d("BookScreen", "3") + navController.navigate(MainScreenDestination) + } + BookNavigationEvent.NavigateToAuth -> { + Log.d("BookScreen", "4") + navController.navigate(AuthScreenDestination) { + Log.d("BookScreen", "5") + popUpTo(navController.graph.startDestinationId) { + Log.d("BookScreen", "6") + inclusive = true + } + } + } + } } } - // TODO брать данные с сервера - // Иммитация того, что мы взяли данные с сервера - val bookings = arrayListOf( - BookingEntities( - "Рабочее место у окна", - "19.04" - ), - BookingEntities( - "Переговорная комната № 1", - "19.04" - ), - BookingEntities( - "Коворкинг А", - "19.04" - ), - BookingEntities( - "Кабинет № 33", - "20.04" - ), - ) - val options = toMap(bookings) - Column( modifier = modifier.fillMaxSize() ) { // Text("" + selectedTime.value + "," + currentTime.value + "," + selectedBooking.value) - when (val currentState = state) { - BookState.Data -> { + when (state) { + is BookState.Data -> { + val options = (state as BookState.Data).booking + if (currentTime.value == null) + for (el in options) { + currentTime.value = el.key + break + } TabGroup(options.keys) - - var i = 0 - options.keys.forEach { - if (i == currentTime.value) - SelectBooking(options[it]!!) - i ++; - } + options[currentTime.value]?.let { SelectBooking(it) } Button( onClick = { - // TODO Добавить бронирование - viewModel.onIntent(BookIntent.Send("Данные" )) + viewModel.onIntent(BookIntent.Send(AuthRepository.getSavedCode()!!, selectedBooking.value!!)) }, modifier = Modifier - .testTag(TestIds.Book.BOOK_BUTTON) + .testTag(TestIds.Book.BOOK_BUTTON), + enabled = selectedBooking.value != null ) { Text(stringResource(R.string.to_book)) } } - BookState.Error -> { + is BookState.Error -> { Text( - text = "", + text = (state as BookState.Error).error, + color = MaterialTheme.colorScheme.error, modifier = Modifier .testTag(TestIds.Book.ERROR) ) Button( - onClick = { }, + onClick = { + viewModel.onIntent(BookIntent.LoadData) + }, modifier = Modifier .testTag(TestIds.Book.REFRESH_BUTTON) ) { - Text("") + Text(stringResource(R.string.upadate)) // А что сюда писать? } } @@ -125,7 +127,7 @@ fun BookScreen( BookState.NotData -> { Text( - text = "", + text = stringResource(R.string.not_book), modifier = Modifier .testTag(TestIds.Book.EMPTY) ) @@ -133,7 +135,7 @@ fun BookScreen( } Button( onClick = { - navController.navigate(MainScreenDestination) + viewModel.onIntent(BookIntent.BackToMainScreen) }, modifier = Modifier .testTag(TestIds.Book.BACK_BUTTON) @@ -151,9 +153,9 @@ fun TabGroup(options: Set) { ) { options.forEachIndexed { index, label -> NavigationBarItem( - selected = currentTime.value == index, + selected = currentTime.value == label, onClick = { - currentTime.value = index + currentTime.value = label }, icon = { Text( @@ -170,12 +172,12 @@ fun TabGroup(options: Set) { } @Composable -fun SelectBooking(options: List) { +fun SelectBooking(options: List) { LazyColumn( Modifier .fillMaxWidth() ) { - options.forEachIndexed { index, label -> + options.forEachIndexed { index, book -> item { Row( Modifier @@ -186,14 +188,14 @@ fun SelectBooking(options: List) { verticalAlignment = Alignment.CenterVertically ) { Text( - text = label, + text = book.place, modifier = Modifier .testTag(TestIds.Book.ITEM_PLACE_TEXT) ) RadioButton( - selected = index == selectedBooking.value && currentTime.value == selectedTime.value, + selected = book == selectedBooking.value && currentTime.value == selectedTime.value, onClick = { - selectedBooking.value = index + selectedBooking.value = book selectedTime.value = currentTime.value }, modifier = Modifier @@ -205,11 +207,3 @@ fun SelectBooking(options: List) { } } -fun toMap(options: List) : Map> { - val map : MutableMap> = mutableMapOf() - options.forEach { - if (map[it.time] == null) map[it.time] = mutableListOf(it.roomName) - else map[it.time]?.add(it.roomName) - } - return map -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt index 8b87d87..1b32a2f 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt @@ -1,8 +1,14 @@ package ru.myitschool.work.ui.screen.book +import ru.myitschool.work.ui.screen.Booking + sealed interface BookState { object Loading: BookState - object Data: BookState - object Error: BookState + data class Data( + val booking : Map> = mapOf() + ): BookState + data class Error( + val error : String + ): BookState object NotData : BookState } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt index c713202..ce5a423 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt @@ -1,5 +1,6 @@ package ru.myitschool.work.ui.screen.book +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers @@ -10,22 +11,115 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import ru.myitschool.work.data.repo.AuthRepository +import ru.myitschool.work.data.repo.BookRepository +import ru.myitschool.work.ui.screen.Booking class BookViewModel : ViewModel() { - private val _uiState = MutableStateFlow(BookState.Data) + private val _uiState = MutableStateFlow(BookState.Loading) val uiState: StateFlow = _uiState.asStateFlow(); private val _actionFlow: MutableSharedFlow = MutableSharedFlow() val actionFlow: SharedFlow = _actionFlow + private val _navigationFlow = MutableSharedFlow() + val navigationFlow: SharedFlow = _navigationFlow + + init { + loadData() + } + + private fun loadData() { + _uiState.update { BookState.Loading } + viewModelScope.launch(Dispatchers.IO) { + val code = AuthRepository.getSavedCode() ?: run { + onIntent(BookIntent.ToAuthScreen) + Log.d("", "Go to AuthScreen") + return@launch + } + Log.d("", "Проверка") +// _uiState.update { +// BookState.Data( +// listOf( +// Booking( +// "19.04", +// "Рабочее место у окна" +// ), +// Booking( +// "19.04", +// "Переговорная комната № 1" +// ), +// Booking( +// "19.04", +// "Коворкинг А" +// ), +// Booking( +// "20.04", +// "Кабинет № 33" +// ), +// ).toMap() +// ) +// } + BookRepository.loadBooks(code).fold( + onSuccess = { + it.let { bookings -> + _uiState.update { + when (bookings.isEmpty()) { + true -> BookState.Data( + booking = bookings.toMap() + ) + false -> BookState.NotData + } + } + } + }, + onFailure = { error -> + _uiState.update { + BookState.Error( + error = error.message ?: "Не удалось загрузить данные" + ) + } + } + ) + } + } fun onIntent(intent: BookIntent) { when (intent) { is BookIntent.Send -> { viewModelScope.launch(Dispatchers.Default) { _uiState.update { BookState.Loading } + BookRepository.sendData(intent.code, intent.booking).fold( + onSuccess = { + _actionFlow.emit(Unit) + }, + onFailure = { error -> + BookState.Error(error.message ?: "Неизвестная ошибка") + } + ) } } - is BookIntent.BookingSelect -> Unit + BookIntent.BackToMainScreen -> { + _navigationFlow.tryEmit(BookNavigationEvent.NavigateToMain) + } + BookIntent.LoadData -> loadData() + BookIntent.ToAuthScreen -> { + _navigationFlow.tryEmit(BookNavigationEvent.NavigateToAuth) + } } } +} + +fun List.toMap() : Map> { + val options = this + val map : MutableMap> = mutableMapOf() + options.forEach { + if (map[it.date] == null) map[it.date] = mutableListOf(it) + else map[it.date]?.add(it) + } + return map +} + +sealed interface BookNavigationEvent { + object NavigateToAuth : BookNavigationEvent + object NavigateToMain : BookNavigationEvent } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5979b52..fa27628 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,4 +6,6 @@ Войти забронировать Назад + Всё забранировано + Обновить \ No newline at end of file