diff --git a/composeApp/src/androidMain/kotlin/band/effective/office/elevator/App.android.kt b/composeApp/src/androidMain/kotlin/band/effective/office/elevator/App.android.kt index f837651115fcb7ef44348479658392fd93a6e971..b92246abde355f1c49efcc7151204384a87d8584 100644 --- a/composeApp/src/androidMain/kotlin/band/effective/office/elevator/App.android.kt +++ b/composeApp/src/androidMain/kotlin/band/effective/office/elevator/App.android.kt @@ -4,6 +4,9 @@ import android.app.Application import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.core.os.BuildCompat.PrereleaseSdkCheck import band.effective.office.elevator.di.androidModuleDI import band.effective.office.elevator.di.appModuleDI import band.effective.office.elevator.domain.AppActivityLifecycleObserver @@ -59,3 +62,4 @@ class AppActivity : ComponentActivity() { } } } + diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Color.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Color.kt index adcf6e078baabc07a6a3243eed72ef72c91c1fe2..d13ecaf23d206bc59f09e4d4a3830f6bbbf7cdba 100644 --- a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Color.kt +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Color.kt @@ -76,9 +76,11 @@ internal val md_theme_dark_scrim = Color(0xFF000000) internal val theme_light_primary = Color(0xFFE85B0F) internal val theme_light_onPrimary = Color(0xFFFFFFFF) -internal val theme_light_secondary = Color(0xFFC0430E) +internal val theme_light_secondary = Color(0xFFC2410C) internal val theme_light_onSecondary = Color(0xFFFFFFFF) +internal val theme_light_secondary_variant = Color(0xFF6F01FF) + internal val theme_light_tertiary = Color(0xFFDB6242) internal val theme_light_onTertiary = Color(0xFFFFFFFF) @@ -141,4 +143,4 @@ internal val seed = Color(0xFF2C3639) // Non Material specified colors internal val lightGray = Color(0xFFEBEBEB) internal val successGreen = Color(0xFF4BB543) -internal val textGrayColor = Color(0x80000000) +internal val textGrayColor = Color(0x80000000) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Theme.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Theme.kt index 4e4c5c24182b6dd5c0b0a6c7c72641ef122f0e78..7c59d713098709e7dcc5a2d689a3a33f61ab6821 100644 --- a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Theme.kt +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/Theme.kt @@ -12,6 +12,7 @@ private val LightColors = lightColors( primary = theme_light_primary, onPrimary = theme_light_onPrimary, secondary = theme_light_secondary, + secondaryVariant = theme_light_secondary_variant, onSecondary = theme_light_onSecondary, error = theme_light_error, onError = theme_light_onError, diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/components/EffectiveButton.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/components/EffectiveButton.kt new file mode 100644 index 0000000000000000000000000000000000000000..af55640e46a98d9124ed6b4e3eb837dfe87b10bd --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/components/EffectiveButton.kt @@ -0,0 +1,39 @@ +package band.effective.office.elevator.components + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import band.effective.office.elevator.MainRes + +@Composable +fun EffectiveButton( + buttonText: String, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Button( + onClick = onClick, + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.primary, + contentColor = MaterialTheme.colors.background + ), + modifier = modifier + .clip(RoundedCornerShape(48.dp)) + ) { + Text( + text = buttonText, + fontFamily = MaterialTheme.typography.button.fontFamily, + fontSize = 15.sp, + modifier = Modifier.padding(vertical = 10.dp) + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/components/TitlePage.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/components/TitlePage.kt new file mode 100644 index 0000000000000000000000000000000000000000..fc36038baba9070dc6640182aa292bab09631a60 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/components/TitlePage.kt @@ -0,0 +1,19 @@ +package band.effective.office.elevator.components + +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.sp + +@Composable +fun TitlePage(title: String, modifier: Modifier = Modifier) { + Text( + text = title, + modifier = modifier, + fontSize = 20.sp, //TODO (Gruzdev) поменять на MaterialTheme.typography.h1.fontSize, + fontFamily = MaterialTheme.typography.h1.fontFamily, + color = Color.Black // TODO(Gruzdev) поменять на цвет из темы + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainComponent.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainComponent.kt index e70c0e906aa0272d670ec0157cd61c04f4450297..e43e992475f25b0bd4f160bfadb6cf5e0deea4cd 100644 --- a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainComponent.kt +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainComponent.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.StateFlow class MainComponent(componentContext: ComponentContext, storeFactory: StoreFactory) : ComponentContext by componentContext { - private val elevatorStore = + private val mainStore = instanceKeeper.getStore { MainStoreFactory( storeFactory = storeFactory, @@ -22,11 +22,25 @@ class MainComponent(componentContext: ComponentContext, storeFactory: StoreFacto } @OptIn(ExperimentalCoroutinesApi::class) - val state: StateFlow = elevatorStore.stateFlow + val state: StateFlow = mainStore.stateFlow - val label: Flow = elevatorStore.labels + val label: Flow = mainStore.labels fun onEvent(event: MainStore.Intent) { - elevatorStore.accept(event) + mainStore.accept(event) + } + + fun onOutput(output: Output) { + when(output) { + is Output.OpenBookingScreen -> TODO() + is Output.OpenMap -> TODO() + } + } + + sealed interface Output { + object OpenMap : Output + + object OpenBookingScreen : Output + } } diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainScreen.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainScreen.kt index d9ed752c8ae33bdf077e4f9d1b722f4d98e4bdb1..e4c8eabf3f9eb6bfd7517cadfa97bae016420e8c 100644 --- a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainScreen.kt +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/MainScreen.kt @@ -1,8 +1,12 @@ package band.effective.office.elevator.ui.main import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.material.Snackbar @@ -16,11 +20,17 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import band.effective.office.elevator.MainRes import band.effective.office.elevator.components.ElevatorButton +import band.effective.office.elevator.components.TitlePage import band.effective.office.elevator.successGreen +import band.effective.office.elevator.ui.main.components.BookingInformation +import band.effective.office.elevator.ui.main.components.ElevatorUIComponent import band.effective.office.elevator.ui.main.store.MainStore +import band.effective.office.elevator.ui.models.ElevatorState +import band.effective.office.elevator.ui.models.ReservedSeat import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.delay @@ -48,14 +58,22 @@ fun MainScreen(component: MainComponent) { delay(3000) isSuccessMessageVisible = false } + MainStore.Label.ShowOptions -> {} } } } - Box(modifier = Modifier.fillMaxSize()) { - ElevatorScreenContent( - isButtonActive = state.buttonActive, - onElevatorButtonClick = component::onEvent + Box( + modifier = Modifier + .background(Color.White) + .fillMaxSize() + ) { + MainScreenContent( + elevatorState = state.elevatorState, + reservedSeats = state.reservedSeats, + onClickBook = { component.onOutput(MainComponent.Output.OpenBookingScreen) }, + onClickShowOptions = { component.onEvent(MainStore.Intent.OnClickShowOption) }, + onClickShowMap = { component.onOutput(MainComponent.Output.OpenMap) } ) SnackBarErrorMessage( modifier = Modifier.align(Alignment.BottomCenter), @@ -67,7 +85,6 @@ fun MainScreen(component: MainComponent) { isVisible = isSuccessMessageVisible ) } - } @Composable @@ -91,19 +108,44 @@ private fun SnackBarSuccessMessage(modifier: Modifier, isVisible: Boolean) { } } - @Composable -private fun ElevatorScreenContent( - isButtonActive: Boolean, - onElevatorButtonClick: (MainStore.Intent) -> Unit +fun MainScreenContent( + reservedSeats: List, + elevatorState: ElevatorState, + onClickBook: () -> Unit, + onClickShowMap: () -> Unit, + onClickShowOptions: () -> Unit, ) { - Box( - modifier = Modifier.fillMaxSize().padding(16.dp) + Column( + modifier = Modifier.fillMaxSize() ) { - ElevatorButton( - modifier = Modifier.align(Alignment.Center), - isActive = isButtonActive, - onButtonClick = { onElevatorButtonClick(MainStore.Intent.OnButtonClicked) } - ) + Column ( + modifier = Modifier + .padding(horizontal = 16.dp) + ) { + TitlePage( + title = stringResource(MainRes.strings.main), + modifier = Modifier.padding(top = 60.dp) + ) + Spacer(modifier = Modifier.height(24.dp)) + ElevatorUIComponent( + elevatorState = elevatorState, + onClickCallElevator = {} + ) + Spacer(modifier = Modifier.height(24.dp)) + } + Column( + modifier = Modifier + .background(MaterialTheme.colors.onBackground) + .padding(horizontal = 16.dp), + ) { + BookingInformation( + reservedSeats = reservedSeats, + onClickBook = onClickBook, + onClickShowMap = onClickShowMap, + onClickShowOptions = onClickShowOptions + ) + } } -} \ No newline at end of file +} + diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/BookingCard.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/BookingCard.kt new file mode 100644 index 0000000000000000000000000000000000000000..4af02482392d178d9c98da81d2225be5ce49c112 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/BookingCard.kt @@ -0,0 +1,125 @@ +package band.effective.office.elevator.ui.main.components + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Divider +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import band.effective.office.elevator.MainRes +import band.effective.office.elevator.ui.models.ReservedSeat +import dev.icerock.moko.resources.compose.painterResource +import dev.icerock.moko.resources.compose.stringResource + +@Composable +fun BookingCard( + seat: ReservedSeat, + onClickShowMap: () -> Unit, + onClickShowOptions: () -> Unit +) { + Column( + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .background(Color.White) + .padding(horizontal = 16.dp, vertical = 24.dp) + .fillMaxWidth() + ) { + Row { + SeatIcon() + Spacer(modifier = Modifier.width(12.dp)) + SeatTitle(seat) + } + Spacer(modifier = Modifier.height(24.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround + ) { + Button( + onClick = onClickShowMap, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .fillMaxWidth(0.7f), + border = BorderStroke(1.dp, MaterialTheme.colors.primary), + elevation = ButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.White, + contentColor = MaterialTheme.colors.primary + ) + ) { + Text( + text = stringResource(MainRes.strings.show_map), + fontSize = 15.sp, + modifier = Modifier.padding(horizontal = 8.dp) + ) + } + Spacer(modifier = Modifier.width(16.dp)) + Button( + onClick = onClickShowOptions, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)), + border = BorderStroke(1.dp, MaterialTheme.colors.primary), + elevation = ButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.White, + contentColor = MaterialTheme.colors.primary + ) + ) { + Image( + painter = painterResource(MainRes.images.mi_options_vertical), + contentDescription = null, + ) + } + } + } +} + +@Composable +private fun SeatTitle(seat: ReservedSeat) { + Column { + Text( + text = seat.seatName, + fontSize = 15.sp, + color = Color.Black + ) + Spacer(modifier = Modifier.height(12.dp)) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = seat.bookingDay, + fontSize = 15.sp, + color = Color.Black + ) + Divider( + modifier = Modifier + .width(19.dp) + .rotate(90f), + thickness = 2.dp, + ) + Text( + text = seat.bookingTime, + fontSize = 15.sp, + color = Color.Black + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/BookingInformation.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/BookingInformation.kt new file mode 100644 index 0000000000000000000000000000000000000000..de1537cd6aa7a49258f26d849d4936fc7192103f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/BookingInformation.kt @@ -0,0 +1,36 @@ +package band.effective.office.elevator.ui.main.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +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.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import band.effective.office.elevator.ui.models.ReservedSeat + +@Composable +fun BookingInformation( + reservedSeats: List, + onClickBook: () -> Unit, + onClickShowMap: () -> Unit, + onClickShowOptions: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxSize() + ) { + Spacer(modifier = Modifier.height(24.dp)) + DateSelection() + Spacer(modifier = Modifier.height(24.dp)) + SeatsReservation( + reservedSeats = reservedSeats, + onClickBook = onClickBook, + onClickShowMap = onClickShowMap, + onClickShowOptions = onClickShowOptions + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/DateSelection.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/DateSelection.kt new file mode 100644 index 0000000000000000000000000000000000000000..1f825c621e33d4aa3e68e06b97d32f63825b1da8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/DateSelection.kt @@ -0,0 +1,49 @@ +package band.effective.office.elevator.ui.main.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.width +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import band.effective.office.elevator.MainRes +import dev.icerock.moko.resources.compose.painterResource +import dev.icerock.moko.resources.compose.stringResource + +@Composable +fun DateSelection() { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround + ){ + Text( + text = stringResource(MainRes.strings.nearest_bookings), + fontSize = 15.sp, + color = Color.Black + ) + CalendarTitle() + } +} + +@Composable +fun CalendarTitle() { + Row { + Image( + painter = painterResource(MainRes.images.material_calendar_ic), + contentDescription = null + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(MainRes.strings.by_date), + color = MaterialTheme.colors.secondaryVariant, + fontSize = 15.sp + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/ElevatorUIComponent.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/ElevatorUIComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..ba7fced9a5183f30b803b81a5d9821682e08a776 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/ElevatorUIComponent.kt @@ -0,0 +1,107 @@ +package band.effective.office.elevator.ui.main.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +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.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import band.effective.office.elevator.MainRes +import band.effective.office.elevator.components.EffectiveButton +import band.effective.office.elevator.ui.models.ElevatorState +import dev.icerock.moko.resources.compose.painterResource +import dev.icerock.moko.resources.compose.stringResource + +@Composable +fun ElevatorUIComponent( + elevatorState: ElevatorState, + modifier: Modifier = Modifier, + onClickCallElevator: () -> Unit +) { + + val title = when(elevatorState) { + ElevatorState.Below -> stringResource(MainRes.strings.elevator_below) + ElevatorState.Goes -> stringResource(MainRes.strings.elevator_goes) + ElevatorState.Raised -> stringResource(MainRes.strings.elevator_raised) + } + + Column( + modifier = modifier + .clip(RoundedCornerShape(16.dp)) + .background(MaterialTheme.colors.background) + .padding(horizontal = 24.dp, vertical = 16.dp), + ) { + Text( + text = title, + fontSize = 15.sp, // TODO использовать тему + color = Color.Black + ) + Spacer(Modifier.height(30.dp)) + when(elevatorState) { + ElevatorState.Below -> ActionCall(onClickCallElevator) + ElevatorState.Goes -> ActionWait() + ElevatorState.Raised -> ActionText( + action = stringResource(MainRes.strings.elevator_ready) + ) + } + } +} + +@Composable +private fun ActionCall(onClickCallElevator: () -> Unit) { + EffectiveButton( + buttonText = stringResource(MainRes.strings.elevator_button), + onClick = onClickCallElevator, + modifier = Modifier.fillMaxWidth() + ) +} + +@Composable +private fun ActionWait() { + Row ( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + + ){ + Image( + painter = painterResource(MainRes.images.loading_ic), + contentDescription = null, + ) + Spacer(modifier = Modifier.width(10.dp)) + Text( + text = stringResource(MainRes.strings.elevator_wait), + fontSize = 15.sp, + color = MaterialTheme.colors.secondary + ) + } +} + +@Composable +private fun ActionText(action: String) { + Text( + text = action, + modifier = Modifier.fillMaxSize(), + textAlign = TextAlign.Center, + fontSize = 15.sp, + color = MaterialTheme.colors.secondary + ) +} diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/SeatIcon.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/SeatIcon.kt new file mode 100644 index 0000000000000000000000000000000000000000..d0d729b148e4d604671191fad14c5c4ff4a2125e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/SeatIcon.kt @@ -0,0 +1,29 @@ +package band.effective.office.elevator.ui.main.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import band.effective.office.elevator.MainRes +import dev.icerock.moko.resources.compose.painterResource + +@Composable +fun SeatIcon() { + Box ( + modifier = Modifier + .clip(RoundedCornerShape(48.dp)) + .background(MaterialTheme.colors.onBackground) + ) { + Image( + painter = painterResource(MainRes.images.seat_ic), + modifier = Modifier.padding(10.dp), + contentDescription = null + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/SeatReservation.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/SeatReservation.kt new file mode 100644 index 0000000000000000000000000000000000000000..8374282dd0a7a4ae8cd4493ec1925f85f22a3b0e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/components/SeatReservation.kt @@ -0,0 +1,79 @@ +package band.effective.office.elevator.ui.main.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import band.effective.office.elevator.MainRes +import band.effective.office.elevator.components.EffectiveButton +import band.effective.office.elevator.textGrayColor +import band.effective.office.elevator.ui.models.ReservedSeat +import dev.icerock.moko.resources.compose.stringResource + +@Composable +fun SeatsReservation( + reservedSeats: List, + onClickBook: () -> Unit, + onClickShowMap: () -> Unit, + onClickShowOptions: () -> Unit +) { + when(reservedSeats.isEmpty()) { + true -> EmptyReservation(onClickBook) + false -> NonEmptyReservation( + reservedSeats = reservedSeats, + onClickShowMap = onClickShowMap, + onClickShowOptions = onClickShowOptions + ) + } +} + +@Composable +fun EmptyReservation(onClickBook: () -> Unit) { + Column ( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(MainRes.strings.none_booking_seat), + fontSize = 15.sp, + color = textGrayColor + ) + Spacer(modifier = Modifier.height(16.dp)) + EffectiveButton( + buttonText = stringResource(MainRes.strings.book_a_seat), + onClick = onClickBook, + modifier = Modifier.width(280.dp) + ) + } +} + +@Composable +fun NonEmptyReservation( + reservedSeats: List, + onClickShowMap: () -> Unit, + onClickShowOptions: () -> Unit, +) { + LazyColumn ( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(18.dp) + ) { + items(reservedSeats) { seat -> + BookingCard( + seat = seat, + onClickShowMap = onClickShowMap, + onClickShowOptions = onClickShowOptions + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStore.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStore.kt index 3b362f6fb2ec111472589ccec0d540f839470fa9..126e53ada392b27a7116f12bc20786a357ff4473 100644 --- a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStore.kt +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStore.kt @@ -1,20 +1,30 @@ package band.effective.office.elevator.ui.main.store +import band.effective.office.elevator.ui.models.ElevatorState +import band.effective.office.elevator.ui.models.ReservedSeat import com.arkivanov.mvikotlin.core.store.Store import dev.icerock.moko.resources.StringResource interface MainStore : Store { sealed interface Intent { - object OnButtonClicked : Intent + object OnClickCallElevator : Intent + + object OnClickShowOption : Intent } sealed interface Label { data class ShowError(val errorState: ErrorState) : Label object ShowSuccess : Label + + object ShowOptions : Label + } - data class State(val buttonActive: Boolean) + data class State( + val reservedSeats: List, + val elevatorState: ElevatorState + ) data class ErrorState(val message: StringResource) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStoreFactory.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStoreFactory.kt index 1d1800228e24b470e228df2b6805539f0e6bc054..10ed4ba4c847bdbd027b87d087b2c00b2e32bc34 100644 --- a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStoreFactory.kt +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/main/store/MainStoreFactory.kt @@ -3,8 +3,8 @@ package band.effective.office.elevator.ui.main.store import band.effective.office.elevator.MainRes import band.effective.office.elevator.data.ApiResponse import band.effective.office.elevator.domain.OfficeElevatorRepository -import band.effective.office.elevator.ui.main.store.MainStore.Intent -import band.effective.office.elevator.ui.main.store.MainStore.State +import band.effective.office.elevator.ui.models.ElevatorState +import band.effective.office.elevator.ui.models.ReservedSeat import com.arkivanov.mvikotlin.core.store.Reducer import com.arkivanov.mvikotlin.core.store.Store import com.arkivanov.mvikotlin.core.store.StoreFactory @@ -23,40 +23,63 @@ internal class MainStoreFactory( private val officeElevatorRepository: OfficeElevatorRepository by inject() fun create(): MainStore = - object : MainStore, Store by storeFactory.create( - name = "ElevatorStore", - initialState = State( - buttonActive = false, + object : MainStore, Store by storeFactory.create( + name = "MainStore", + initialState = MainStore.State( + elevatorState = ElevatorState.Below, + reservedSeats = listOf( + ReservedSeat( + seatName = "Рабочее масто А1", + bookingDay = "Пн, 1 июля", + bookingTime = "12:00 - 14:00" + ), + ReservedSeat( + seatName = "Рабочее масто А1", + bookingDay = "Пн, 1 июля", + bookingTime = "12:00 - 14:00" + ), + ReservedSeat( + seatName = "Рабочее масто А1", + bookingDay = "Пн, 1 июля", + bookingTime = "12:00 - 14:00" + ), + ), ), executorFactory = ::ExecutorImpl, reducer = ReducerImpl ) {} private sealed interface Msg { - data class SwitchButton(val isActive: Boolean) : Msg + data class UpdateElevatorState(val elevatorState: ElevatorState) : Msg + data class UpdateSeatsReservation(val reservedSeats: List) : Msg } private inner class ExecutorImpl : - CoroutineExecutor() { - override fun executeIntent(intent: Intent, getState: () -> State) { + CoroutineExecutor() { + override fun executeIntent(intent: MainStore.Intent, getState: () -> MainStore.State) { when (intent) { - Intent.OnButtonClicked -> { - if (!getState().buttonActive) + MainStore.Intent.OnClickCallElevator -> { + if (getState().elevatorState is ElevatorState.Below) doElevatorCall() } + MainStore.Intent.OnClickShowOption -> { + scope.launch { + publish(MainStore.Label.ShowOptions) + } + } } } private fun doElevatorCall() { scope.launch { - dispatch(Msg.SwitchButton(true)) + dispatch(Msg.UpdateElevatorState(ElevatorState.Goes)) delay(1000) publish( when (val result = officeElevatorRepository.call()) { is ApiResponse.Error.HttpError -> MainStore.Label.ShowError( MainStore.ErrorState( (MainRes.strings.server_error.format(result.code.toString())) - as StringResource + as StringResource ) ) @@ -84,15 +107,15 @@ internal class MainStoreFactory( } ) delay(1000) - dispatch(Msg.SwitchButton(false)) + dispatch(Msg.UpdateElevatorState(ElevatorState.Raised)) } } } - - private object ReducerImpl : Reducer { - override fun State.reduce(message: Msg): State = + private object ReducerImpl : Reducer { + override fun MainStore.State.reduce(message: Msg): MainStore.State = when (message) { - is Msg.SwitchButton -> copy(buttonActive = message.isActive) + is Msg.UpdateElevatorState -> copy(elevatorState = message.elevatorState) + is Msg.UpdateSeatsReservation -> copy(reservedSeats = message.reservedSeats) } } } diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/models/ElevatorState.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/models/ElevatorState.kt new file mode 100644 index 0000000000000000000000000000000000000000..845a0f5de1edaa9f73d94090415629934de58e75 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/models/ElevatorState.kt @@ -0,0 +1,7 @@ +package band.effective.office.elevator.ui.models + +sealed interface ElevatorState{ + object Below: ElevatorState + object Goes: ElevatorState + object Raised: ElevatorState +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/models/ReservedSeat.kt b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/models/ReservedSeat.kt new file mode 100644 index 0000000000000000000000000000000000000000..92ab69fed779b4a7eef444b098ceab584a42f80f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/band/effective/office/elevator/ui/models/ReservedSeat.kt @@ -0,0 +1,7 @@ +package band.effective.office.elevator.ui.models + +data class ReservedSeat( + val seatName: String, + val bookingDay: String, + val bookingTime: String +) \ No newline at end of file diff --git a/composeApp/src/commonMain/resources/MR/base/strings_ru.xml b/composeApp/src/commonMain/resources/MR/base/strings_ru.xml index de9ef8d3a9df56816fa25020a4c560d299435e56..d6161090aa0fce5225af7f4c3423fcafed777cd5 100644 --- a/composeApp/src/commonMain/resources/MR/base/strings_ru.xml +++ b/composeApp/src/commonMain/resources/MR/base/strings_ru.xml @@ -4,6 +4,19 @@ Войти в аккаунт Effective Что-то пошло не так. Пожалуйста попробуйте еще раз + + Лифт на 1-м этаже + Лифт едет + Лифт поднят + Поднять лифт + Подождите... + Вызовите лифт через кнопку + + По дате + Показать на карте + Забронировать место + Ваши ближайшие брони + У вас пока нет бронирований Профиль Главная diff --git a/composeApp/src/commonMain/resources/MR/en/strings_en.xml b/composeApp/src/commonMain/resources/MR/en/strings_en.xml index d6ac0855d148025ab360c1c0bccf77e8dbd62da6..12d489c96acfd2c37f65629069e2bc631e19f430 100644 --- a/composeApp/src/commonMain/resources/MR/en/strings_en.xml +++ b/composeApp/src/commonMain/resources/MR/en/strings_en.xml @@ -4,9 +4,15 @@ Sign in to Effective email Something went wrong. Please try again + By date + Show on map + Book a seat + Your upcoming bookings + You dont have any bookings yet + Profile - Elevator + Main Booking Employees diff --git a/composeApp/src/commonMain/resources/MR/images/loading_ic.svg b/composeApp/src/commonMain/resources/MR/images/loading_ic.svg new file mode 100644 index 0000000000000000000000000000000000000000..fe5af212cc375a39519bd988e3b9310bbcf03ef2 --- /dev/null +++ b/composeApp/src/commonMain/resources/MR/images/loading_ic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/composeApp/src/commonMain/resources/MR/images/material_calendar_ic.svg b/composeApp/src/commonMain/resources/MR/images/material_calendar_ic.svg new file mode 100644 index 0000000000000000000000000000000000000000..9216a60e8e998c49a528546f579c89728279f46c --- /dev/null +++ b/composeApp/src/commonMain/resources/MR/images/material_calendar_ic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/composeApp/src/commonMain/resources/MR/images/mi_options_vertical.svg b/composeApp/src/commonMain/resources/MR/images/mi_options_vertical.svg new file mode 100644 index 0000000000000000000000000000000000000000..0bd73f5e7f6cbbc6a5b04daa4a24dd4d5aba9cc7 --- /dev/null +++ b/composeApp/src/commonMain/resources/MR/images/mi_options_vertical.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/composeApp/src/commonMain/resources/MR/images/seat_ic.svg b/composeApp/src/commonMain/resources/MR/images/seat_ic.svg new file mode 100644 index 0000000000000000000000000000000000000000..f01b3afb4e68bf02fda0e0ab896b88217ca0bc81 --- /dev/null +++ b/composeApp/src/commonMain/resources/MR/images/seat_ic.svg @@ -0,0 +1,5 @@ + + + + +