From 257755a25acec834dad3b8f28e405e032f2fedc7 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Dec 2025 21:08:22 +0300 Subject: [PATCH] full mainscreen --- .../java/ru/myitschool/work/core/Constants.kt | 2 +- .../ru/myitschool/work/data/entity/Booking.kt | 11 + .../myitschool/work/data/entity/Employee.kt | 9 + .../ru/myitschool/work/data/entity/Place.kt | 5 + .../work/data/repo/MainRepository.kt | 34 +++ .../work/data/source/DataStoreDataSource.kt | 16 +- .../work/data/source/NetworkDataSource.kt | 68 ++++++ .../work/domain/main/GetUserDataUseCase.kt | 13 ++ .../work/ui/screen/NavigationGraph.kt | 7 +- .../work/ui/screen/auth/AuthViewModel.kt | 2 +- .../work/ui/screen/book/BookScreen.kt | 15 +- .../work/ui/screen/book/BookingState.kt | 12 ++ .../work/ui/screen/book/BookingViewModel.kt | 4 +- .../work/ui/screen/main/MainAction.kt | 8 + .../work/ui/screen/main/MainIntent.kt | 9 + .../work/ui/screen/main/MainScreen.kt | 194 ++++++++++-------- .../work/ui/screen/main/MainState.kt | 9 + .../work/ui/screen/main/MainViewModel.kt | 66 ++++++ 18 files changed, 375 insertions(+), 109 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/data/entity/Booking.kt create mode 100644 app/src/main/java/ru/myitschool/work/data/entity/Employee.kt create mode 100644 app/src/main/java/ru/myitschool/work/data/entity/Place.kt create mode 100644 app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/main/GetUserDataUseCase.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookingState.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainAction.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.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..5fe3adf 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://192.168.1.39: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/entity/Booking.kt b/app/src/main/java/ru/myitschool/work/data/entity/Booking.kt new file mode 100644 index 0000000..e3fe124 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entity/Booking.kt @@ -0,0 +1,11 @@ +package ru.myitschool.work.data.entity + +import java.time.LocalDate + + +data class Booking ( val id: Long, + val date: LocalDate, + val place: Place, + val employeeCode: String){ + +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/entity/Employee.kt b/app/src/main/java/ru/myitschool/work/data/entity/Employee.kt new file mode 100644 index 0000000..8190ff4 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entity/Employee.kt @@ -0,0 +1,9 @@ +package ru.myitschool.work.data.entity + +data class Employee ( + val name: String, + val code: String, + val photoUrl: String, + val bookingList: MutableList) { + +} diff --git a/app/src/main/java/ru/myitschool/work/data/entity/Place.kt b/app/src/main/java/ru/myitschool/work/data/entity/Place.kt new file mode 100644 index 0000000..73e5a73 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entity/Place.kt @@ -0,0 +1,5 @@ +package ru.myitschool.work.data.entity + +data class Place( + val id: Long, + val place: String ){} diff --git a/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt new file mode 100644 index 0000000..003b689 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt @@ -0,0 +1,34 @@ +package ru.myitschool.work.data.repo + +import ru.myitschool.work.data.entity.Employee +import ru.myitschool.work.data.source.DataStoreDataSource +import ru.myitschool.work.data.source.DataStoreDataSource.createAuthCode +import ru.myitschool.work.data.source.DataStoreDataSource.getAuthCode +import ru.myitschool.work.data.source.NetworkDataSource + +class MainRepository { + private var employee: Employee? = null + + suspend fun getUserInfo(): Result { + return try { + val code = getCode() + val result = NetworkDataSource.getUserInfo(code) + result.onSuccess { success -> + employee = success + } + result + } catch (e: Exception) { + Result.failure(e) + } + } + + + + suspend fun getCode(): String { + return getAuthCode() + } + + suspend fun logOut(){ + DataStoreDataSource.logOut() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/source/DataStoreDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/DataStoreDataSource.kt index aaa03ce..5014e2f 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/DataStoreDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/DataStoreDataSource.kt @@ -8,6 +8,7 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import ru.myitschool.work.App import ru.myitschool.work.core.OurConstants.DS_AUTH_KEY @@ -28,7 +29,20 @@ object DataStoreDataSource { App.context.dataStore.updateData { it.toMutablePreferences().also { preferences -> preferences[AUTH_KEY] = code - Log.d("AnnaKonda", "Code added to ds") + } + } + } + + suspend fun getAuthCode(): String { + return App.context.dataStore.data.map { preferences -> + preferences[AUTH_KEY] ?: "" + }.first() + } + + suspend fun logOut() { + App.context.dataStore.updateData { + it.toMutablePreferences().also { preferences -> + preferences.remove(AUTH_KEY) } } } 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 f12b719..546f664 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 @@ -11,6 +11,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.entity.Employee +import kotlinx.serialization.json.* +import ru.myitschool.work.data.entity.Booking +import ru.myitschool.work.data.entity.Place +import java.time.LocalDate object NetworkDataSource { private val client by lazy { @@ -37,6 +42,69 @@ object NetworkDataSource { } } } + suspend fun getUserInfo(code: String): Result = withContext(Dispatchers.IO) { + return@withContext runCatching { + val response = client.get(getUrl(code, Constants.INFO_URL)) + when (response.status) { + HttpStatusCode.OK -> { + val json = response.bodyAsText() + if (json.isBlank()) { + error("Пустой ответ от сервера") + } + + val jsonObject = try { + Json.parseToJsonElement(json).jsonObject + } catch (e: Exception) { + error("Ошибка парсинга: ${e.message}") + } + val name = jsonObject["name"]?.jsonPrimitive?.content + ?: error("Отсутствует поле 'name'") + val photoUrl = jsonObject["photoUrl"]?.jsonPrimitive?.content + ?: error("Отсутствует поле 'photoUrl'") + + val bookingJson = jsonObject["booking"]?.jsonObject + ?: error("Отсутствует поле 'booking' в ответе") + + val employee = Employee( + name = name, + code = code, + photoUrl = photoUrl, + bookingList = mutableListOf() + ) + val bookingList = mutableListOf() + for ((dateString, bookingElement) in bookingJson) { + val date = LocalDate.parse(dateString) + val bookingObj = bookingElement.jsonObject + val bookingId = bookingObj["id"]?.jsonPrimitive?.long + ?: error("Отсутствует поле id") + val placeString = bookingObj["place"]?.jsonPrimitive?.content + ?: error("Отсутствует поле 'place' $dateString") + + if (placeString.isBlank()) { + error("Пустое поле 'place' $dateString") + } + + val placeId = bookingId + val place = Place(placeId, placeString) + + val booking = Booking( + id = bookingId, + date = date, + place = place, + employeeCode = employee.code + ) + bookingList.add(booking) + } + if (bookingList.isEmpty()) { + error("Список бронирований пуст") + } + employee.bookingList.addAll(bookingList) + employee + } + else -> error(response.bodyAsText()) + } + } + } private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/main/GetUserDataUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/main/GetUserDataUseCase.kt new file mode 100644 index 0000000..9b8c749 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/main/GetUserDataUseCase.kt @@ -0,0 +1,13 @@ +package ru.myitschool.work.domain.main + +import ru.myitschool.work.data.entity.Employee +import ru.myitschool.work.data.repo.AuthRepository +import ru.myitschool.work.data.repo.MainRepository + +class GetUserDataUseCase( + private val repository: MainRepository +) { + suspend operator fun invoke(): Result { + return repository.getUserInfo() + } +} \ 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 bc632cd..93e99c6 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 @@ -33,12 +33,7 @@ fun AppNavHost( AuthScreen(navController = navController) } composable { - MainScreen( - navController = navController, - onNavigateToBooking = { - navController.navigate(BookScreenDestination) - } - ) + MainScreen(navController = navController) } composable { BookScreen( diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt index 4900be0..0a0ac5b 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt @@ -47,7 +47,7 @@ class AuthViewModel : ViewModel() { } } - is AuthIntent.TextInput -> { + is AuthIntent.TextInput -> { viewModelScope.launch { authFlow().collect { if (CheckCodeInput(intent.text)) { 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 cdbcfb4..0953e8b 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 @@ -27,7 +27,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun BookingScreen( - uiState: BookingUiState, // состояние интерфейса + uiState: BookingState, // состояние интерфейса onSelectDate: (LocalDate) -> Unit, // callback при выборе даты onSelectPlace: (String) -> Unit, // callback при выборе места onBook: () -> Unit, // callback при бронировании @@ -136,22 +136,15 @@ fun BookingScreen( } } -// Модель состояния интерфейса -data class BookingUiState( - val dates: List = emptyList(), // список доступных дат - val places: Map> = emptyMap(), // места по датам - val selectedDate: LocalDate? = null, // выбранная дата - val selectedPlace: String? = null, // выбранное место - val isError: Boolean = false, // флаг ошибки - val errorMessage: String? = null // сообщение об ошибке -) + + @Composable fun BookScreen( onBack: () -> Unit, // callback при возврате назад onBookingSuccess: () -> Unit // callback при успешном бронировании ) { - val viewModel: BookingViewModel = viewModel() + val viewModel: BookingViewModel = BookingViewModel() val uiState by viewModel.uiState.collectAsState() BookingScreen( diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingState.kt new file mode 100644 index 0000000..b61dcc7 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingState.kt @@ -0,0 +1,12 @@ +package ru.myitschool.work.ui.screen.book + +import java.time.LocalDate + +data class BookingState( + val dates: List = emptyList(), // список доступных дат + val places: Map> = emptyMap(), // места по датам + val selectedDate: LocalDate? = null, // выбранная дата + val selectedPlace: String? = null, // выбранное место + val isError: Boolean = false, // флаг ошибки + val errorMessage: String? = null // сообщение об ошибке +) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingViewModel.kt index 928017d..96133c8 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookingViewModel.kt @@ -10,8 +10,8 @@ import java.time.LocalDate class BookingViewModel : ViewModel() { - private val _uiState = MutableStateFlow(BookingUiState()) - val uiState: StateFlow = _uiState.asStateFlow() + private val _uiState = MutableStateFlow(BookingState()) + val uiState: StateFlow = _uiState.asStateFlow() init { loadBookingData() diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainAction.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainAction.kt new file mode 100644 index 0000000..007981d --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainAction.kt @@ -0,0 +1,8 @@ +package ru.myitschool.work.ui.screen.main + +import ru.myitschool.work.ui.screen.auth.AuthAction + +sealed interface MainAction { + data class SetName(val name: String) + data class ShowError(val message: String?) : MainAction +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt new file mode 100644 index 0000000..eb58973 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt @@ -0,0 +1,9 @@ +package ru.myitschool.work.ui.screen.main + +sealed interface MainIntent { + /* data class Send(val text: String): AuthIntent + data class TextInput(val text: String): AuthIntent + object CheckLogIntent: AuthIntent*/ + object LoadData: MainIntent + object LogOut: MainIntent +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt index a851c25..5460355 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt @@ -1,100 +1,81 @@ package ru.myitschool.work.ui.screen.main import android.util.Log -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import kotlinx.coroutines.launch +import coil3.compose.AsyncImage import ru.myitschool.work.core.TestIds -import java.text.SimpleDateFormat -import java.util.* +import ru.myitschool.work.data.entity.Booking +import ru.myitschool.work.data.entity.Employee +import ru.myitschool.work.ui.nav.AuthScreenDestination import ru.myitschool.work.ui.nav.BookScreenDestination -// Модель данных для бронирования -data class BookingItem( - val date: String, // Формат "dd.MM.yyyy" - val place: String, - val id: Int -) - @Composable fun MainScreen( navController: NavController, - onNavigateToBooking: () -> Unit ) { + val viewModel = MainViewModel() // Состояния - var userName by remember { mutableStateOf("Иван Иванов") } - var isLoading by remember { mutableStateOf(false) } - var errorMessage by remember { mutableStateOf("") } - var bookingItems by remember { mutableStateOf(emptyList()) } - var hasError by remember { mutableStateOf(false) } - - // Для корутин - val coroutineScope = rememberCoroutineScope() - + val event = viewModel.actionFlow.collectAsState(initial = null) // Функция загрузки данных - fun loadData() { - isLoading = true - hasError = false - - coroutineScope.launch { - kotlinx.coroutines.delay(1000) // Имитация задержки - - // Имитация ответа от сервера - val response = listOf( - BookingItem("20.12.2023", "Конференц-зал А", 1), - BookingItem("15.12.2023", "Переговорная Б", 2), - BookingItem("25.12.2023", "Спортзал", 3) - ) - - // Сортировка по дате (увеличение) - bookingItems = response.sortedBy { - SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).parse(it.date) - } - - isLoading = false - } - } - - // Первая загрузка при открытии экрана LaunchedEffect(Unit) { - loadData() + viewModel.onIntent(MainIntent.LoadData) } + var errorMessage: String? by remember { mutableStateOf("") } + LaunchedEffect(event.value) { + if (event.value is MainAction.ShowError) { + errorMessage = (event.value as MainAction.ShowError).message + } + } + Log.d("AnnaKonda", errorMessage.toString()) // Если ошибка - показываем только ошибку и кнопку обновления - if (hasError) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - // Текстовое поле с ошибкой (main_error) - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - modifier = Modifier.testTag(TestIds.Main.ERROR) + if (errorMessage != null) { + ErrorScreen(viewModel = viewModel, navController = navController, errorMessage) + } else { + DefaultScreen(viewModel = viewModel, navController = navController) + } +} +@Composable +fun DefaultScreen(viewModel: MainViewModel, + navController: NavController){ + val state by viewModel.uiState.collectAsState() + var employee : Employee? by remember { mutableStateOf(null) } + var errorMessage by remember { mutableStateOf("") } + var bookingItems : List? by remember { mutableStateOf(emptyList()) } + var isLoading by remember { mutableStateOf(true) } - ) - - Spacer(modifier = Modifier.height(16.dp)) - - // Кнопка обновления (main_refresh_button) - Button(onClick = { loadData() }) { - Text("Обновить") + LaunchedEffect(state) { + when (state) { + is MainState.Loading -> { + errorMessage = "" + isLoading = true + } + is MainState.Data -> { + isLoading = false + employee = (state as MainState.Data).employee + if (employee == null){ + navController.navigate(AuthScreenDestination) { popUpTo(0) } + } else { + bookingItems = employee?.bookingList?.sortedBy { item -> + item?.date + } + } } } - } else { - // Нормальное состояние + } + employee?.let { Column( modifier = Modifier .fillMaxSize() @@ -106,29 +87,34 @@ fun MainScreen( verticalAlignment = Alignment.CenterVertically ) { // Фото пользователя (main_photo) - Image( - painter = painterResource(id = android.R.drawable.ic_menu_gallery), + employee?.photoUrl?.let { msg -> Log.d("AnnaKonda", msg) } + AsyncImage( + model = employee?.photoUrl ?: "", contentDescription = "Фото", - modifier = Modifier.size(64.dp).testTag(TestIds.Main.PROFILE_IMAGE) + modifier = Modifier + .size(64.dp) + .clip(CircleShape) + .testTag(TestIds.Main.PROFILE_IMAGE), + error = painterResource(id = android.R.drawable.ic_menu_gallery) ) Spacer(modifier = Modifier.width(16.dp)) // Имя пользователя (main_name) Text( - text = userName, + text = employee!!.name, style = MaterialTheme.typography.titleLarge, modifier = Modifier.weight(1f).testTag(TestIds.Main.PROFILE_NAME), color = MaterialTheme.colorScheme.onSurface ) // Кнопка выхода (main_logout_button) - Button(onClick = { - // Очистка данных и переход на авторизацию - userName = "" - bookingItems = emptyList() - navController.navigate("auth") { popUpTo(0) } - }, + Button( + onClick = { + // Очистка данных и переход на авторизацию + viewModel.onIntent(MainIntent.LogOut) + bookingItems = emptyList() + }, modifier = Modifier.testTag(TestIds.Main.LOGOUT_BUTTON) ) { Text("Выход") @@ -145,11 +131,11 @@ fun MainScreen( ) { // Кнопка обновления (main_refresh_button) Button( - onClick = { loadData() }, - enabled = !isLoading, + onClick = { viewModel.onIntent(MainIntent.LoadData) }, + enabled = state !is MainState.Loading, modifier = Modifier.testTag(TestIds.Main.REFRESH_BUTTON) ) { - if (isLoading) { + if (state is MainState.Loading) { CircularProgressIndicator( modifier = Modifier.size(16.dp), color = MaterialTheme.colorScheme.onPrimary @@ -160,7 +146,8 @@ fun MainScreen( } // кнопка бронирования Button( - onClick = { navController.navigate(BookScreenDestination) + onClick = { + navController.navigate(BookScreenDestination) }, modifier = Modifier.testTag(TestIds.Main.ADD_BUTTON) ) { @@ -171,14 +158,13 @@ fun MainScreen( Spacer(modifier = Modifier.height(16.dp)) // Список бронирований - if (bookingItems.isNotEmpty()) { + if (!bookingItems.isNullOrEmpty()) { LazyColumn( modifier = Modifier.weight(1f) ) { - itemsIndexed(bookingItems) { index, item -> + itemsIndexed(bookingItems as List) { index, item -> // Элемент списка (main_book_pos_{index}) - Log.d("Nicoly", index.toString()) Card( modifier = Modifier .fillMaxWidth() @@ -187,10 +173,13 @@ fun MainScreen( containerColor = MaterialTheme.colorScheme.surfaceVariant ) ) { - Column(modifier = Modifier.padding(16.dp).testTag(TestIds.Main.getIdItemByPosition(index))) { + Column( + modifier = Modifier.padding(16.dp) + .testTag(TestIds.Main.getIdItemByPosition(index)) + ) { // Дата бронирования (main_item_date) Text( - text = "Дата: ${item.date}", + text = "Дата: ${item?.date}", color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.testTag(TestIds.Main.ITEM_DATE) ) @@ -199,7 +188,7 @@ fun MainScreen( // Место бронирования (main_item_place) Text( - text = "Место: ${item.place}", + text = "Место: ${item?.place?.place}", color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.testTag(TestIds.Main.ITEM_PLACE) ) @@ -223,3 +212,34 @@ fun MainScreen( } } } + + + + +@Composable +fun ErrorScreen(viewModel: MainViewModel, + navController: NavController, + errorMessage: String?){ + + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // Текстовое поле с ошибкой (main_error) + + if (errorMessage != null) { + Text( + text = errorMessage, + color = MaterialTheme.colorScheme.error, + modifier = Modifier.testTag(TestIds.Main.ERROR) + + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + Button(onClick = { viewModel.onIntent(MainIntent.LoadData) }) { + Text("Обновить") + } + } +} diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt new file mode 100644 index 0000000..ea877f5 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt @@ -0,0 +1,9 @@ +package ru.myitschool.work.ui.screen.main + +import ru.myitschool.work.data.entity.Employee +import ru.myitschool.work.ui.screen.auth.AuthState + +sealed interface MainState { + object Loading: MainState + data class Data (val employee: Employee?): MainState +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt new file mode 100644 index 0000000..0b3b065 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt @@ -0,0 +1,66 @@ +package ru.myitschool.work.ui.screen.main + +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 +import ru.myitschool.work.data.repo.AuthRepository +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.ui.screen.auth.AuthAction +import ru.myitschool.work.ui.screen.auth.AuthIntent +import ru.myitschool.work.ui.screen.auth.AuthState + +class MainViewModel : ViewModel() { + init { + loadData() + } + private val repository by lazy{ MainRepository() } + private val getUserDataUseCase by lazy { GetUserDataUseCase(repository) } + + private val _uiState = MutableStateFlow(MainState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _actionFlow: MutableSharedFlow = MutableSharedFlow() + val actionFlow: SharedFlow = _actionFlow + + fun onIntent(intent: MainIntent) { + when (intent) { + is MainIntent.LoadData -> { + loadData() + } + is MainIntent.LogOut -> { + viewModelScope.launch(Dispatchers.IO) { + _uiState.update { MainState.Data(null) } + repository.logOut() + } + } + } + } + + fun loadData() { + viewModelScope.launch(Dispatchers.IO) { + _uiState.update { MainState.Loading } + + getUserDataUseCase.invoke().fold( + onSuccess = { employee -> + _uiState.update { MainState.Data(employee) } + _actionFlow.emit(MainAction.ShowError(null)) + }, + onFailure = { error -> + error.printStackTrace() + if (error.message != null) { + _actionFlow.emit(MainAction.ShowError(error.message.toString())) + } + } + ) + } + } +} \ No newline at end of file