Коммит bf6326f5 создал по автору Radch-enko's avatar Radch-enko
Просмотр файлов

Enhance error handling and improve booking editor state management

- Added error state tracking (`isLoadUpdate`, `isErrorUpdate`, `isLoadCreate`, `isErrorCreate`) to the booking editor.
- Refactored update and create event logic to handle errors and loading states properly.
- Updated UI components to display loader or error messages for create and update operations.
- Improved organizer selection logic with fallback handling for non-matching input.
- Adjusted date and duration validation to ensure proper time range for events.
владелец 111af54d
...@@ -5,4 +5,5 @@ ...@@ -5,4 +5,5 @@
<string name="booking_time_button">Занять c %1$s до %2$s</string> <string name="booking_time_button">Занять c %1$s до %2$s</string>
<string name="update_button">Изменить</string> <string name="update_button">Изменить</string>
<string name="delete_button">Удалить бронь</string> <string name="delete_button">Удалить бронь</string>
<string name="error">Произошла ошибка</string>
</resources> </resources>
\ No newline at end of file
...@@ -37,6 +37,7 @@ import band.effective.office.tablet.feature.bookingEditor.booking_time_button ...@@ -37,6 +37,7 @@ import band.effective.office.tablet.feature.bookingEditor.booking_time_button
import band.effective.office.tablet.feature.bookingEditor.booking_view_title import band.effective.office.tablet.feature.bookingEditor.booking_view_title
import band.effective.office.tablet.feature.bookingEditor.create_view_title import band.effective.office.tablet.feature.bookingEditor.create_view_title
import band.effective.office.tablet.feature.bookingEditor.delete_button import band.effective.office.tablet.feature.bookingEditor.delete_button
import band.effective.office.tablet.feature.bookingEditor.error
import band.effective.office.tablet.feature.bookingEditor.presentation.datetimepicker.DateTimePickerModalView import band.effective.office.tablet.feature.bookingEditor.presentation.datetimepicker.DateTimePickerModalView
import band.effective.office.tablet.feature.bookingEditor.update_button import band.effective.office.tablet.feature.bookingEditor.update_button
import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.decompose.extensions.compose.stack.Children
...@@ -67,7 +68,7 @@ fun BookingEditor( ...@@ -67,7 +68,7 @@ fun BookingEditor(
onDismissRequest = { component.sendIntent(Intent.OnClose) }) onDismissRequest = { component.sendIntent(Intent.OnClose) })
BookingEditorComponent.ModalConfig.SuccessModal -> SuccessSelectRoomView( BookingEditorComponent.ModalConfig.SuccessModal -> SuccessSelectRoomView(
roomName = component.room, roomName = component.roomName,
organizerName = state.selectOrganizer.fullName, organizerName = state.selectOrganizer.fullName,
startTime = state.event.startTime, startTime = state.event.startTime,
finishTime = state.event.finishTime, finishTime = state.event.finishTime,
...@@ -88,7 +89,7 @@ fun BookingEditor( ...@@ -88,7 +89,7 @@ fun BookingEditor(
selectOrganizer = state.selectOrganizer, selectOrganizer = state.selectOrganizer,
organizers = state.selectOrganizers, organizers = state.selectOrganizers,
expended = state.expanded, expended = state.expanded,
onUpdateEvent = { component.sendIntent(Intent.OnUpdateEvent(component.room)) }, onUpdateEvent = { component.sendIntent(Intent.OnUpdateEvent(component.roomName)) },
onDeleteEvent = { component.sendIntent(Intent.OnDeleteEvent) }, onDeleteEvent = { component.sendIntent(Intent.OnDeleteEvent) },
inputText = state.inputText, inputText = state.inputText,
onInput = { component.sendIntent(Intent.OnInput(it)) }, onInput = { component.sendIntent(Intent.OnInput(it)) },
...@@ -96,12 +97,16 @@ fun BookingEditor( ...@@ -96,12 +97,16 @@ fun BookingEditor(
onDoneInput = { component.sendIntent(Intent.OnDoneInput) }, onDoneInput = { component.sendIntent(Intent.OnDoneInput) },
isDeleteError = state.isErrorDelete, isDeleteError = state.isErrorDelete,
isDeleteLoad = state.isLoadDelete, isDeleteLoad = state.isLoadDelete,
isUpdateError = state.isErrorUpdate,
isUpdateLoad = state.isLoadUpdate,
isCreateError = state.isErrorCreate,
isCreateLoad = state.isLoadCreate,
enableUpdateButton = state.enableUpdateButton, enableUpdateButton = state.enableUpdateButton,
isNewEvent = !state.isCreatedEvent(), isNewEvent = !state.isCreatedEvent(),
onCreateEvent = { component.sendIntent(Intent.OnBooking) }, onCreateEvent = { component.sendIntent(Intent.OnBooking) },
start = state.event.startTime.format(timeFormatter), start = state.event.startTime.format(timeFormatter),
finish = state.event.finishTime.format(timeFormatter), finish = state.event.finishTime.format(timeFormatter),
room = component.room, room = component.roomName,
) )
} }
} }
...@@ -134,6 +139,10 @@ private fun BookingEditor( ...@@ -134,6 +139,10 @@ private fun BookingEditor(
onDoneInput: (String) -> Unit, onDoneInput: (String) -> Unit,
isDeleteError: Boolean, isDeleteError: Boolean,
isDeleteLoad: Boolean, isDeleteLoad: Boolean,
isUpdateError: Boolean = false,
isUpdateLoad: Boolean = false,
isCreateError: Boolean = false,
isCreateLoad: Boolean = false,
enableUpdateButton: Boolean, enableUpdateButton: Boolean,
isNewEvent: Boolean, isNewEvent: Boolean,
start: String, start: String,
...@@ -176,7 +185,15 @@ private fun BookingEditor( ...@@ -176,7 +185,15 @@ private fun BookingEditor(
selectOrganizers = organizers.map { it.fullName }, selectOrganizers = organizers.map { it.fullName },
expanded = expended, expanded = expended,
onExpandedChange = onExpandedChange, onExpandedChange = onExpandedChange,
onSelectItem = { org -> onSelectOrganizer(organizers.find { it.fullName == org }!!) }, onSelectItem = { org ->
organizers.find { it.fullName == org }?.let { organizer ->
onSelectOrganizer(organizer)
} ?: run {
// If organizer not found, use the first one or default
val fallbackOrganizer = organizers.firstOrNull() ?: Organizer.default
onSelectOrganizer(fallbackOrganizer)
}
},
onInput = onInput, onInput = onInput,
isInputError = isInputError, isInputError = isInputError,
onDoneInput = onDoneInput, onDoneInput = onDoneInput,
...@@ -187,23 +204,37 @@ private fun BookingEditor( ...@@ -187,23 +204,37 @@ private fun BookingEditor(
SuccessButton( SuccessButton(
modifier = Modifier.fillMaxWidth().height(60.dp), modifier = Modifier.fillMaxWidth().height(60.dp),
onClick = onCreateEvent, onClick = onCreateEvent,
enable = enableUpdateButton enable = enableUpdateButton && !isCreateLoad
) { ) {
Text( when {
text = stringResource(Res.string.booking_time_button, start, finish), isCreateLoad -> Loader()
style = MaterialTheme.typography.h6 isCreateError -> Text(
) text = "Error creating event", // Ideally, this should be a string resource
style = MaterialTheme.typography.h6
)
else -> Text(
text = stringResource(Res.string.booking_time_button, start, finish),
style = MaterialTheme.typography.h6
)
}
} }
} else { } else {
SuccessButton( SuccessButton(
modifier = Modifier.fillMaxWidth().height(60.dp), modifier = Modifier.fillMaxWidth().height(60.dp),
onClick = onUpdateEvent, onClick = onUpdateEvent,
enable = enableUpdateButton enable = enableUpdateButton && !isUpdateLoad
) { ) {
Text( when {
text = stringResource(Res.string.update_button), isUpdateLoad -> Loader()
style = MaterialTheme.typography.h6 isUpdateError -> Text(
) text = stringResource(Res.string.error),
style = MaterialTheme.typography.h6
)
else -> Text(
text = stringResource(Res.string.update_button),
style = MaterialTheme.typography.h6
)
}
} }
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
AlertButton( AlertButton(
...@@ -213,7 +244,7 @@ private fun BookingEditor( ...@@ -213,7 +244,7 @@ private fun BookingEditor(
when { when {
isDeleteLoad -> Loader() isDeleteLoad -> Loader()
isDeleteError -> Text( isDeleteError -> Text(
text = stringResource(Res.string.update_button), text = "Error deleting event", // Ideally, this should be a string resource
style = MaterialTheme.typography.h6 style = MaterialTheme.typography.h6
) )
......
...@@ -21,54 +21,63 @@ import band.effective.office.tablet.feature.bookingEditor.presentation.mapper.Up ...@@ -21,54 +21,63 @@ import band.effective.office.tablet.feature.bookingEditor.presentation.mapper.Up
import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.childStack
import io.github.aakira.napier.Napier
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.atStartOfDayIn import kotlinx.datetime.atStartOfDayIn
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
/**
* Component responsible for editing booking events.
* Handles creating new bookings and updating existing ones.
*/
class BookingEditorComponent( class BookingEditorComponent(
componentContext: ComponentContext, componentContext: ComponentContext,
event: EventInfo, initialEvent: EventInfo,
val room: String, val roomName: String,
private val onDelete: (Slot) -> Unit, private val onDeleteEvent: (Slot) -> Unit,
private val onCloseRequest: () -> Unit, private val onCloseRequest: () -> Unit,
) : ComponentContext by componentContext, KoinComponent, ModalWindow { ) : ComponentContext by componentContext, KoinComponent, ModalWindow {
val dateTimePickerComponent: DateTimePickerComponent by lazy { val dateTimePickerComponent: DateTimePickerComponent by lazy {
DateTimePickerComponent( DateTimePickerComponent(
componentContext = componentContext, componentContext = componentContext,
onSelectDate = { newDate -> setDay(newDate) }, onSelectDate = { newDate -> updateEventDate(newDate) },
onCloseRequest = { mutableState.update { it.copy(showSelectDate = false) } }, onCloseRequest = { mutableState.update { it.copy(showSelectDate = false) } },
event = event, event = initialEvent,
room = room, room = roomName,
duration = state.value.duration, duration = state.value.duration,
initDate = { state.value.date } initDate = { state.value.date }
) )
} }
private val scope = componentCoroutineScope() private val coroutineScope = componentCoroutineScope()
val organizersInfoUseCase: OrganizersInfoUseCase by inject() // Use cases
val checkBookingUseCase: CheckBookingUseCase by inject() private val organizersInfoUseCase: OrganizersInfoUseCase by inject()
val updateBookingUseCase: UpdateBookingUseCase by inject() private val checkBookingUseCase: CheckBookingUseCase by inject()
val createBookingUseCase: CreateBookingUseCase by inject() private val updateBookingUseCase: UpdateBookingUseCase by inject()
private val createBookingUseCase: CreateBookingUseCase by inject()
val eventInfoMapper: EventInfoMapper by inject() // Mappers
val stateToEventInfoMapper: UpdateEventComponentStateToEventInfoMapper by inject() private val eventInfoMapper: EventInfoMapper by inject()
private val stateToEventInfoMapper: UpdateEventComponentStateToEventInfoMapper by inject()
private val mutableState = MutableStateFlow(eventInfoMapper.mapToUpdateBookingState(event)) // State management
private val mutableState = MutableStateFlow(eventInfoMapper.mapToUpdateBookingState(initialEvent))
val state = mutableState.asStateFlow() val state = mutableState.asStateFlow()
// Navigation
private val navigation = StackNavigation<ModalConfig>() private val navigation = StackNavigation<ModalConfig>()
val childStack = childStack( val childStack = childStack(
...@@ -82,44 +91,58 @@ class BookingEditorComponent( ...@@ -82,44 +91,58 @@ class BookingEditorComponent(
loadOrganizers() loadOrganizers()
} }
/**
* Handles intents from the UI
*/
fun sendIntent(intent: Intent) { fun sendIntent(intent: Intent) {
when (intent) { when (intent) {
Intent.OnBooking -> createEvent() Intent.OnBooking -> createNewEvent()
Intent.OnClose -> onCloseRequest() Intent.OnClose -> onCloseRequest()
Intent.OnCloseSelectDateDialog -> mutableState.update { it.copy(showSelectDate = false) } Intent.OnCloseSelectDateDialog -> closeSelectDateDialog()
Intent.OnDeleteEvent -> cancel() Intent.OnDeleteEvent -> deleteEvent()
Intent.OnDoneInput -> onDoneInput() Intent.OnDoneInput -> finalizeOrganizerSelection()
Intent.OnExpandedChange -> mutableState.update { it.copy(expanded = !it.expanded) } Intent.OnExpandedChange -> toggleExpandedState()
is Intent.OnInput -> onInput(intent.input) is Intent.OnInput -> handleOrganizerInput(intent.input)
Intent.OnOpenSelectDateDialog -> mutableState.update { it.copy(showSelectDate = true) } Intent.OnOpenSelectDateDialog -> openSelectDateDialog()
is Intent.OnSelectOrganizer -> { is Intent.OnSelectOrganizer -> selectOrganizer(intent.newOrganizer)
mutableState.update { is Intent.OnSetDate -> updateEventDate(intent.calendar)
it.copy( is Intent.OnUpdateDate -> updateEventDetails(daysToAdd = intent.updateInDays)
selectOrganizer = intent.newOrganizer, is Intent.OnUpdateEvent -> updateExistingEvent()
inputText = intent.newOrganizer.fullName, is Intent.OnUpdateLength -> updateEventDetails(durationChange = intent.update)
)
}
checkEnableButton(
inputError = false,
busyEvent = state.value.isBusyEvent,
)
}
is Intent.OnSetDate -> setDay(intent.calendar)
is Intent.OnUpdateDate -> updateInfo(changeData = intent.updateInDays)
is Intent.OnUpdateEvent -> updateEvent()
is Intent.OnUpdateLength -> updateInfo(changeDuration = intent.update)
} }
} }
private fun updateEvent() = scope.launch { /**
CoroutineScope(Dispatchers.IO).launch { * Updates an existing event in the database
updateBookingUseCase(roomName = room, eventInfo = stateToEventInfoMapper.map(state.value)) */
private fun updateExistingEvent() = coroutineScope.launch {
mutableState.update { it.copy(isLoadUpdate = true) }
withContext(Dispatchers.IO) {
updateBookingUseCase(
roomName = roomName,
eventInfo = stateToEventInfoMapper.map(state.value)
).unbox(
errorHandler = {
Napier.d { "Update booking failed: ${it.description}" }
mutableState.update {
it.copy(
isLoadUpdate = false,
isErrorUpdate = true
)
}
},
successHandler = {
mutableState.update { it.copy(isLoadUpdate = false) }
onCloseRequest()
}
)
} }
onCloseRequest()
} }
private fun loadOrganizers() = scope.launch { /**
* Loads the list of organizers from the database
*/
private fun loadOrganizers() = coroutineScope.launch {
val organizers = organizersInfoUseCase().unbox(errorHandler = { emptyList() }) val organizers = organizersInfoUseCase().unbox(errorHandler = { emptyList() })
mutableState.update { mutableState.update {
it.copy( it.copy(
...@@ -129,14 +152,22 @@ class BookingEditorComponent( ...@@ -129,14 +152,22 @@ class BookingEditorComponent(
} }
} }
private fun cancel() { /**
onDelete(eventInfoMapper.mapToSlot(state.value.event)) * Deletes the current event
*/
private fun deleteEvent() = coroutineScope.launch {
mutableState.update { it.copy(isLoadDelete = true) }
onDeleteEvent(eventInfoMapper.mapToSlot(state.value.event))
mutableState.update { it.copy(isLoadDelete = false) }
onCloseRequest() onCloseRequest()
} }
private fun onDoneInput() = with(state.value) { /**
* Finalizes the organizer selection based on the input text
*/
private fun finalizeOrganizerSelection() = with(state.value) {
val input = inputText.lowercase() val input = inputText.lowercase()
val organizer = selectOrganizers.firstOrNull { it.fullName.lowercase().contains(input) } ?: event.organizer val organizer = findOrganizerByName(input) ?: event.organizer
val isOrganizerIncorrect = !organizers.contains(organizer) val isOrganizerIncorrect = !organizers.contains(organizer)
mutableState.update { mutableState.update {
...@@ -146,48 +177,67 @@ class BookingEditorComponent( ...@@ -146,48 +177,67 @@ class BookingEditorComponent(
isInputError = isOrganizerIncorrect, isInputError = isOrganizerIncorrect,
) )
} }
checkEnableButton( updateButtonState(
inputError = isOrganizerIncorrect, inputError = isOrganizerIncorrect,
busyEvent = isBusyEvent busyEvent = isBusyEvent
) )
} }
private fun onInput(input: String) { /**
val newList = state.value.organizers * Finds an organizer by name (partial match)
*/
private fun findOrganizerByName(name: String): Organizer? {
return state.value.selectOrganizers.firstOrNull {
it.fullName.lowercase().contains(name.lowercase())
}
}
/**
* Handles input in the organizer field
*/
private fun handleOrganizerInput(input: String) {
val filteredOrganizers = state.value.organizers
.filter { it.fullName.lowercase().contains(input.lowercase()) } .filter { it.fullName.lowercase().contains(input.lowercase()) }
.sortedBy { it.fullName.lowercase().indexOf(input.lowercase()) } .sortedBy { it.fullName.lowercase().indexOf(input.lowercase()) }
mutableState.update { it.copy(inputText = input, selectOrganizers = newList) }
mutableState.update {
it.copy(
inputText = input,
selectOrganizers = filteredOrganizers
)
}
} }
private fun checkEnableButton( /**
* Updates the button state based on validation
*/
private fun updateButtonState(
inputError: Boolean, inputError: Boolean,
busyEvent: Boolean busyEvent: Boolean
) = mutableState.update { it.copy(enableUpdateButton = !inputError && !busyEvent) } ) = mutableState.update {
it.copy(enableUpdateButton = !inputError && !busyEvent)
}
private fun setDay(newDate: LocalDateTime) = scope.launch { /**
* Updates the event date
*/
private fun updateEventDate(newDate: LocalDateTime) = coroutineScope.launch {
with(state.value) { with(state.value) {
val busyEvents: List<EventInfo> = checkBookingUseCase.busyEvents( val busyEvents = checkForBusyEvents(
event = copy(date = newDate).let(stateToEventInfoMapper::map), date = newDate,
room = room duration = duration,
).filter { it.startTime != date } organizer = selectOrganizer
)
mutableState.update {
it.copy( updateStateWithNewEventDetails(
date = newDate, newDate = newDate,
duration = duration, newDuration = duration,
selectOrganizer = selectOrganizer, newOrganizer = selectOrganizer,
event = event( busyEvents = busyEvents
id = event.id, )
newDate = newDate,
newDuration = duration,
organizer = selectOrganizer,
),
isBusyEvent = busyEvents.isNotEmpty()
)
}
if (selectOrganizer != Organizer.default) { if (selectOrganizer != Organizer.default) {
checkEnableButton( updateButtonState(
inputError = isInputError, inputError = isInputError,
busyEvent = busyEvents.isNotEmpty() busyEvent = busyEvents.isNotEmpty()
) )
...@@ -195,79 +245,198 @@ class BookingEditorComponent( ...@@ -195,79 +245,198 @@ class BookingEditorComponent(
} }
} }
private fun updateInfo( /**
changeData: Int = 0, * Updates event details (date, duration, organizer)
changeDuration: Int = 0, */
newOrg: Organizer = state.value.selectOrganizer private fun updateEventDetails(
) = scope.launch { daysToAdd: Int = 0,
durationChange: Int = 0,
newOrganizer: Organizer = state.value.selectOrganizer
) = coroutineScope.launch {
with(state.value) { with(state.value) {
val newDate = date.asInstant.plus(changeData.days).asLocalDateTime val newDate = date.asInstant.plus(daysToAdd.days).asLocalDateTime
val newDuration = duration + changeDuration val newDuration = duration + durationChange
val newOrganizer = organizers.firstOrNull { it.fullName == newOrg.fullName } ?: event.organizer val resolvedOrganizer = organizers.firstOrNull {
val busyEvent: List<EventInfo> = checkBookingUseCase.busyEvents( it.fullName == newOrganizer.fullName
event = copy( } ?: event.organizer
date = newDate,
duration = newDuration,
selectOrganizer = newOrganizer
).let(stateToEventInfoMapper::map),
room = room
).filter { it.startTime != date }
fun today(): LocalDateTime {
val now = currentLocalDateTime
return now.date.atStartOfDayIn(defaultTimeZone).asLocalDateTime
}
val officeEndTime = OfficeTime.finishWorkTime(newDate.date) val busyEvents = checkForBusyEvents(
val newEventFinish = newDate.asInstant.plus(newDuration.minutes).asLocalDateTime date = newDate,
duration = newDuration,
if (newDuration > 0 && newDate > today() && newEventFinish < officeEndTime) { organizer = resolvedOrganizer
mutableState.update { )
it.copy(
date = newDate, if (isValidEventTime(newDate, newDuration)) {
duration = newDuration, updateStateWithNewEventDetails(
selectOrganizer = newOrganizer, newDate = newDate,
event = event( newDuration = newDuration,
id = event.id, newOrganizer = resolvedOrganizer,
newDate = newDate, busyEvents = busyEvents
newDuration = newDuration, )
organizer = newOrganizer,
), updateButtonState(
isBusyEvent = busyEvent.isNotEmpty() inputError = !organizers.contains(resolvedOrganizer),
) busyEvent = busyEvents.isNotEmpty()
}
checkEnableButton(
inputError = !organizers.contains(newOrganizer),
busyEvent = busyEvent.isNotEmpty()
) )
} }
} }
} }
private fun createEvent() = scope.launch { /**
val event = stateToEventInfoMapper.map(state.value) * Checks if the event time is valid
CoroutineScope(Dispatchers.IO).launch { */
createBookingUseCase(roomName = room, eventInfo = event) private fun isValidEventTime(date: LocalDateTime, duration: Int): Boolean {
val today = getTodayStartTime()
val officeEndTime = OfficeTime.finishWorkTime(date.date)
val eventEndTime = date.asInstant.plus(duration.minutes).asLocalDateTime
return duration > 0 && date > today && eventEndTime < officeEndTime
}
/**
* Gets the start time of today
*/
private fun getTodayStartTime(): LocalDateTime =
currentLocalDateTime.date.atStartOfDayIn(defaultTimeZone).asLocalDateTime
/**
* Checks for busy events that conflict with the given parameters
*/
private suspend fun checkForBusyEvents(
date: LocalDateTime,
duration: Int,
organizer: Organizer
): List<EventInfo> {
val eventToCheck = createEventInfo(
id = state.value.event.id,
startTime = date,
duration = duration,
organizer = organizer
)
val currentEventId = state.value.event.id
return checkBookingUseCase.busyEvents(
event = eventToCheck,
room = roomName
).filter { busyEvent ->
// Exclude the current event being edited (if it's an update)
if (busyEvent.id == currentEventId && !currentEventId.isBlank()) {
return@filter false
}
// Check for any overlap between events
val newEventStart = eventToCheck.startTime.asInstant
val newEventEnd = eventToCheck.finishTime.asInstant
val busyEventStart = busyEvent.startTime.asInstant
val busyEventEnd = busyEvent.finishTime.asInstant
// Events overlap if one starts before the other ends
(newEventStart < busyEventEnd && newEventEnd > busyEventStart)
} }
onCloseRequest()
} }
// TODO refactor /**
private fun event( * Updates the state with new event details
id: String, */
private fun updateStateWithNewEventDetails(
newDate: LocalDateTime, newDate: LocalDateTime,
newDuration: Int, newDuration: Int,
newOrganizer: Organizer,
busyEvents: List<EventInfo>
) {
val updatedEvent = createEventInfo(
id = state.value.event.id,
startTime = newDate,
duration = newDuration,
organizer = newOrganizer
)
mutableState.update {
it.copy(
date = newDate,
duration = newDuration,
selectOrganizer = newOrganizer,
event = updatedEvent,
isBusyEvent = busyEvents.isNotEmpty()
)
}
}
/**
* Creates a new event in the database
*/
private fun createNewEvent() = coroutineScope.launch {
mutableState.update { it.copy(isLoadCreate = true) }
val eventToCreate = stateToEventInfoMapper.map(state.value)
withContext(Dispatchers.IO) {
createBookingUseCase(roomName = roomName, eventInfo = eventToCreate).unbox(
errorHandler = {
mutableState.update {
it.copy(
isLoadCreate = false,
isErrorCreate = true,
)
}
},
successHandler = {
mutableState.update { it.copy(isLoadCreate = false) }
onCloseRequest()
}
)
}
}
/**
* Creates an EventInfo object with the given parameters
*/
private fun createEventInfo(
id: String,
startTime: LocalDateTime,
duration: Int,
organizer: Organizer, organizer: Organizer,
): EventInfo { ): EventInfo {
return EventInfo( return EventInfo(
startTime = newDate, startTime = startTime,
finishTime = newDate.asInstant.plus(newDuration.minutes).asLocalDateTime, finishTime = startTime.asInstant.plus(duration.minutes).asLocalDateTime,
organizer = organizer, organizer = organizer,
id = id, id = id,
isLoading = false, isLoading = false,
) )
} }
/**
* Selects an organizer
*/
private fun selectOrganizer(organizer: Organizer) {
mutableState.update {
it.copy(
selectOrganizer = organizer,
inputText = organizer.fullName,
)
}
updateButtonState(
inputError = false,
busyEvent = state.value.isBusyEvent,
)
}
/**
* Toggles the expanded state
*/
private fun toggleExpandedState() = mutableState.update { it.copy(expanded = !it.expanded) }
/**
* Opens the select date dialog
*/
private fun openSelectDateDialog() = mutableState.update { it.copy(showSelectDate = true) }
/**
* Closes the select date dialog
*/
private fun closeSelectDateDialog() = mutableState.update { it.copy(showSelectDate = false) }
@Serializable @Serializable
sealed interface ModalConfig { sealed interface ModalConfig {
@Serializable @Serializable
......
...@@ -17,6 +17,10 @@ data class State( ...@@ -17,6 +17,10 @@ data class State(
val isInputError: Boolean, val isInputError: Boolean,
val isLoadDelete: Boolean, val isLoadDelete: Boolean,
val isErrorDelete: Boolean, val isErrorDelete: Boolean,
val isLoadUpdate: Boolean,
val isErrorUpdate: Boolean,
val isLoadCreate: Boolean,
val isErrorCreate: Boolean,
val showSelectDate: Boolean, val showSelectDate: Boolean,
val enableUpdateButton: Boolean, val enableUpdateButton: Boolean,
val isBusyEvent: Boolean val isBusyEvent: Boolean
...@@ -34,6 +38,10 @@ data class State( ...@@ -34,6 +38,10 @@ data class State(
isInputError = false, isInputError = false,
isLoadDelete = false, isLoadDelete = false,
isErrorDelete = false, isErrorDelete = false,
isLoadUpdate = false,
isErrorUpdate = false,
isLoadCreate = false,
isErrorCreate = false,
showSelectDate = false, showSelectDate = false,
enableUpdateButton = false, enableUpdateButton = false,
isBusyEvent = false isBusyEvent = false
...@@ -41,4 +49,4 @@ data class State( ...@@ -41,4 +49,4 @@ data class State(
} }
fun isCreatedEvent() = !event.isNotCreated() fun isCreatedEvent() = !event.isNotCreated()
} }
\ No newline at end of file
...@@ -137,20 +137,20 @@ class MainComponent( ...@@ -137,20 +137,20 @@ class MainComponent(
} }
} }
// reset selected room // reset selected room
currentRoomTimer.start(delay = 1.minutes) { /*currentRoomTimer.start(delay = 1.minutes) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
loadRooms() loadRooms()
} }
} }*/
// update cache when get error // update cache when get error
errorTimer.init(15.minutes) { /*errorTimer.init(15.minutes) {
roomInfoUseCase.updateCache() roomInfoUseCase.updateCache()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
loadRooms() loadRooms()
} }
} }*/
// reset select date // reset select date
currentTimeTimer.start(1.minutes) { /*currentTimeTimer.start(1.minutes) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
mutableState.update { mutableState.update {
it.copy( it.copy(
...@@ -160,7 +160,7 @@ class MainComponent( ...@@ -160,7 +160,7 @@ class MainComponent(
} }
slotComponent.sendIntent(SlotIntent.UpdateDate(currentLocalDateTime)) slotComponent.sendIntent(SlotIntent.UpdateDate(currentLocalDateTime))
} }
} }*/
} }
fun sendIntent(intent: Intent) { fun sendIntent(intent: Intent) {
...@@ -207,10 +207,9 @@ class MainComponent( ...@@ -207,10 +207,9 @@ class MainComponent(
is ModalWindowsConfig.UpdateEvent -> BookingEditorComponent( is ModalWindowsConfig.UpdateEvent -> BookingEditorComponent(
componentContext = componentContext, componentContext = componentContext,
event = modalWindows.event, initialEvent = modalWindows.event,
room = state.value.run { roomList[indexSelectRoom].name }, roomName = state.value.run { roomList[indexSelectRoom].name },
onDeleteEvent = { slot ->
onDelete = { slot ->
slotComponent.sendIntent( slotComponent.sendIntent(
SlotIntent.Delete( SlotIntent.Delete(
slot = slot, slot = slot,
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать