extend checkout time improve ui
This commit is contained in:
@@ -1,35 +0,0 @@
|
|||||||
kotlin version: 2.3.0
|
|
||||||
error message: Incremental compilation failed: /home/androidlover5842/AndroidStudioProjects/TrisolarisPMS/app/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin (No such file or directory)
|
|
||||||
java.io.FileNotFoundException: /home/androidlover5842/AndroidStudioProjects/TrisolarisPMS/app/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin (No such file or directory)
|
|
||||||
at java.base/java.io.FileInputStream.open0(Native Method)
|
|
||||||
at java.base/java.io.FileInputStream.open(Unknown Source)
|
|
||||||
at java.base/java.io.FileInputStream.<init>(Unknown Source)
|
|
||||||
at org.jetbrains.kotlin.incremental.storage.ExternalizersKt.loadFromFile(externalizers.kt:184)
|
|
||||||
at org.jetbrains.kotlin.incremental.snapshots.LazyClasspathSnapshot.getSavedShrunkClasspathAgainstPreviousLookups(LazyClasspathSnapshot.kt:86)
|
|
||||||
at org.jetbrains.kotlin.incremental.classpathDiff.ClasspathSnapshotShrinkerKt.shrinkAndSaveClasspathSnapshot(ClasspathSnapshotShrinker.kt:267)
|
|
||||||
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.performWorkAfterCompilation(IncrementalJvmCompilerRunner.kt:76)
|
|
||||||
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.performWorkAfterCompilation(IncrementalJvmCompilerRunner.kt:23)
|
|
||||||
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:420)
|
|
||||||
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally$lambda$0$compile(IncrementalCompilerRunner.kt:249)
|
|
||||||
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:267)
|
|
||||||
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:119)
|
|
||||||
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:684)
|
|
||||||
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:94)
|
|
||||||
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1810)
|
|
||||||
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
|
|
||||||
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
|
|
||||||
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
|
|
||||||
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
|
|
||||||
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
|
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
|
||||||
at java.base/java.lang.Thread.run(Unknown Source)
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
# TrisolarisPMS API Usage
|
# TrisolarisPMS API Usage
|
||||||
|
|
||||||
|
## API Docs Path
|
||||||
|
|
||||||
|
- `/home/androidlover5842/IdeaProjects/TrisolarisServer/docs`
|
||||||
|
|
||||||
## 1) Booking
|
## 1) Booking
|
||||||
|
|
||||||
### Create booking
|
### Create booking
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.android.trisolarispms.ui.booking
|
package com.android.trisolarispms.ui.booking
|
||||||
|
|
||||||
import android.app.TimePickerDialog
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -15,7 +13,6 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CalendarMonth
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
import androidx.compose.material.icons.filled.Schedule
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
@@ -36,7 +33,6 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
@@ -192,7 +188,7 @@ fun BookingCreateScreen(
|
|||||||
}
|
}
|
||||||
if (state.billingMode == BookingBillingMode.CUSTOM_WINDOW) {
|
if (state.billingMode == BookingBillingMode.CUSTOM_WINDOW) {
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
TimePickerTextField(
|
BookingTimePickerTextField(
|
||||||
value = state.billingCheckoutTime,
|
value = state.billingCheckoutTime,
|
||||||
label = { Text("Billing check-out (HH:mm)") },
|
label = { Text("Billing check-out (HH:mm)") },
|
||||||
onTimeSelected = viewModel::onBillingCheckoutTimeChange,
|
onTimeSelected = viewModel::onBillingCheckoutTimeChange,
|
||||||
@@ -511,55 +507,3 @@ fun BookingCreateScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TimePickerTextField(
|
|
||||||
value: String,
|
|
||||||
label: @Composable () -> Unit,
|
|
||||||
onTimeSelected: (String) -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val parsed = runCatching {
|
|
||||||
val parts = value.split(":")
|
|
||||||
val hour = parts.getOrNull(0)?.toIntOrNull() ?: 12
|
|
||||||
val minute = parts.getOrNull(1)?.toIntOrNull() ?: 0
|
|
||||||
(hour.coerceIn(0, 23)) to (minute.coerceIn(0, 59))
|
|
||||||
}.getOrDefault(12 to 0)
|
|
||||||
|
|
||||||
OutlinedTextField(
|
|
||||||
value = value,
|
|
||||||
onValueChange = {},
|
|
||||||
readOnly = true,
|
|
||||||
label = label,
|
|
||||||
trailingIcon = {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
TimePickerDialog(
|
|
||||||
context,
|
|
||||||
{ _, hourOfDay, minute ->
|
|
||||||
onTimeSelected("%02d:%02d".format(hourOfDay, minute))
|
|
||||||
},
|
|
||||||
parsed.first,
|
|
||||||
parsed.second,
|
|
||||||
true
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.Schedule, contentDescription = "Pick time")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = modifier
|
|
||||||
.clickable {
|
|
||||||
TimePickerDialog(
|
|
||||||
context,
|
|
||||||
{ _, hourOfDay, minute ->
|
|
||||||
onTimeSelected("%02d:%02d".format(hourOfDay, minute))
|
|
||||||
},
|
|
||||||
parsed.first,
|
|
||||||
parsed.second,
|
|
||||||
true
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -37,64 +40,30 @@ internal fun BookingDateTimePickerDialog(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onConfirm: (LocalDate, String) -> Unit
|
onConfirm: (LocalDate, String) -> Unit
|
||||||
) {
|
) {
|
||||||
val today = remember { LocalDate.now() }
|
val (selectedDate, timeValue, daysOfWeek, calendarState) = rememberBookingDateTimePickerState(
|
||||||
val currentMonth = remember { YearMonth.from(today) }
|
initialDate = initialDate,
|
||||||
val startMonth = remember { currentMonth }
|
initialTime = initialTime,
|
||||||
val endMonth = remember { currentMonth.plusMonths(24) }
|
minDate = minDate
|
||||||
val daysOfWeek = remember { daysOfWeek() }
|
|
||||||
val calendarState = rememberCalendarState(
|
|
||||||
startMonth = startMonth,
|
|
||||||
endMonth = endMonth,
|
|
||||||
firstVisibleMonth = currentMonth,
|
|
||||||
firstDayOfWeek = daysOfWeek.first()
|
|
||||||
)
|
)
|
||||||
val selectedDate = remember { mutableStateOf(initialDate ?: today) }
|
|
||||||
val timeValue = remember { mutableStateOf(initialTime) }
|
|
||||||
val dateFormatter = remember { DateTimeFormatter.ISO_LOCAL_DATE }
|
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
title = { Text(title) },
|
title = { Text(title) },
|
||||||
text = {
|
text = {
|
||||||
Column {
|
BookingDateTimePickerContent(
|
||||||
CalendarDaysOfWeekHeader(daysOfWeek)
|
selectedDate = selectedDate,
|
||||||
HorizontalCalendar(
|
timeValue = timeValue,
|
||||||
state = calendarState,
|
minDate = minDate,
|
||||||
dayContent = { day ->
|
daysOfWeek = daysOfWeek,
|
||||||
val selectable = day.position == DayPosition.MonthDate && !day.date.isBefore(minDate)
|
calendarState = calendarState
|
||||||
CalendarDayCell(
|
)
|
||||||
day = day,
|
|
||||||
isSelectedStart = selectedDate.value == day.date,
|
|
||||||
isSelectedEnd = false,
|
|
||||||
isInRange = false,
|
|
||||||
hasRate = false,
|
|
||||||
isSelectable = selectable,
|
|
||||||
onClick = { selectedDate.value = day.date }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
monthHeader = { month ->
|
|
||||||
CalendarMonthHeader(month)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Text(
|
|
||||||
text = "Selected: ${selectedDate.value.format(dateFormatter)}",
|
|
||||||
style = MaterialTheme.typography.bodySmall
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
OutlinedTextField(
|
|
||||||
value = timeValue.value,
|
|
||||||
onValueChange = { timeValue.value = it },
|
|
||||||
label = { Text("Time (HH:MM)") },
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
val time = timeValue.value.ifBlank { initialTime }
|
val time = timeValue.value.ifBlank { initialTime }
|
||||||
onConfirm(selectedDate.value, time)
|
val date = if (selectedDate.value.isBefore(minDate)) minDate else selectedDate.value
|
||||||
|
onConfirm(date, time)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text("OK")
|
Text("OK")
|
||||||
@@ -108,6 +77,145 @@ internal fun BookingDateTimePickerDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun BookingDateTimePickerInline(
|
||||||
|
title: String,
|
||||||
|
initialDate: LocalDate?,
|
||||||
|
initialTime: String,
|
||||||
|
minDate: LocalDate,
|
||||||
|
onValueChange: (LocalDate, String) -> Unit
|
||||||
|
) {
|
||||||
|
val (selectedDate, timeValue, daysOfWeek, calendarState) = rememberBookingDateTimePickerState(
|
||||||
|
initialDate = initialDate,
|
||||||
|
initialTime = initialTime,
|
||||||
|
minDate = minDate
|
||||||
|
)
|
||||||
|
Card(
|
||||||
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = MaterialTheme.shapes.large
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 10.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
BookingDateTimePickerContent(
|
||||||
|
selectedDate = selectedDate,
|
||||||
|
timeValue = timeValue,
|
||||||
|
minDate = minDate,
|
||||||
|
daysOfWeek = daysOfWeek,
|
||||||
|
calendarState = calendarState,
|
||||||
|
onDateSelected = { date ->
|
||||||
|
val safeDate = if (date.isBefore(minDate)) minDate else date
|
||||||
|
onValueChange(safeDate, timeValue.value)
|
||||||
|
},
|
||||||
|
onTimeSelected = { time ->
|
||||||
|
val safeDate = if (selectedDate.value.isBefore(minDate)) minDate else selectedDate.value
|
||||||
|
onValueChange(safeDate, time)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BookingDateTimePickerContent(
|
||||||
|
selectedDate: MutableState<LocalDate>,
|
||||||
|
timeValue: MutableState<String>,
|
||||||
|
minDate: LocalDate,
|
||||||
|
daysOfWeek: List<java.time.DayOfWeek>,
|
||||||
|
calendarState: com.kizitonwose.calendar.compose.CalendarState,
|
||||||
|
onDateSelected: (LocalDate) -> Unit = {},
|
||||||
|
onTimeSelected: (String) -> Unit = {}
|
||||||
|
) {
|
||||||
|
val dateFormatter = remember { DateTimeFormatter.ISO_LOCAL_DATE }
|
||||||
|
Column {
|
||||||
|
CalendarDaysOfWeekHeader(daysOfWeek)
|
||||||
|
HorizontalCalendar(
|
||||||
|
state = calendarState,
|
||||||
|
dayContent = { day ->
|
||||||
|
val selectable = day.position == DayPosition.MonthDate && !day.date.isBefore(minDate)
|
||||||
|
CalendarDayCell(
|
||||||
|
day = day,
|
||||||
|
isSelectedStart = selectedDate.value == day.date,
|
||||||
|
isSelectedEnd = false,
|
||||||
|
isInRange = false,
|
||||||
|
hasRate = false,
|
||||||
|
isSelectable = selectable,
|
||||||
|
onClick = {
|
||||||
|
selectedDate.value = day.date
|
||||||
|
onDateSelected(day.date)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
monthHeader = { month ->
|
||||||
|
CalendarMonthHeader(month)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "Selected: ${selectedDate.value.format(dateFormatter)}",
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
BookingTimePickerTextField(
|
||||||
|
value = timeValue.value,
|
||||||
|
onTimeSelected = {
|
||||||
|
timeValue.value = it
|
||||||
|
onTimeSelected(it)
|
||||||
|
},
|
||||||
|
label = { Text("Time (HH:MM)") },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberBookingDateTimePickerState(
|
||||||
|
initialDate: LocalDate?,
|
||||||
|
initialTime: String,
|
||||||
|
minDate: LocalDate
|
||||||
|
): PickerUiState {
|
||||||
|
val now = remember { LocalDate.now() }
|
||||||
|
val initialSelectedDate = remember(initialDate, minDate, now) {
|
||||||
|
val seed = initialDate ?: now
|
||||||
|
if (seed.isBefore(minDate)) minDate else seed
|
||||||
|
}
|
||||||
|
val selectedDate = remember(initialSelectedDate) { mutableStateOf(initialSelectedDate) }
|
||||||
|
val timeValue = remember(initialTime) { mutableStateOf(initialTime) }
|
||||||
|
val startMonth = remember(minDate) { YearMonth.from(minDate) }
|
||||||
|
val firstVisibleMonth = remember(initialSelectedDate) { YearMonth.from(initialSelectedDate) }
|
||||||
|
val endMonth = remember(startMonth) { startMonth.plusMonths(24) }
|
||||||
|
val daysOfWeek = remember { daysOfWeek() }
|
||||||
|
val calendarState = rememberCalendarState(
|
||||||
|
startMonth = startMonth,
|
||||||
|
endMonth = endMonth,
|
||||||
|
firstVisibleMonth = firstVisibleMonth,
|
||||||
|
firstDayOfWeek = daysOfWeek.first()
|
||||||
|
)
|
||||||
|
return PickerUiState(
|
||||||
|
selectedDate = selectedDate,
|
||||||
|
timeValue = timeValue,
|
||||||
|
daysOfWeek = daysOfWeek,
|
||||||
|
calendarState = calendarState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class PickerUiState(
|
||||||
|
val selectedDate: MutableState<LocalDate>,
|
||||||
|
val timeValue: MutableState<String>,
|
||||||
|
val daysOfWeek: List<java.time.DayOfWeek>,
|
||||||
|
val calendarState: com.kizitonwose.calendar.compose.CalendarState
|
||||||
|
)
|
||||||
|
|
||||||
internal fun formatBookingIso(date: LocalDate, time: String): String {
|
internal fun formatBookingIso(date: LocalDate, time: String): String {
|
||||||
val parts = time.split(":")
|
val parts = time.split(":")
|
||||||
val hour = parts.getOrNull(0)?.toIntOrNull() ?: 0
|
val hour = parts.getOrNull(0)?.toIntOrNull() ?: 0
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
package com.android.trisolarispms.ui.booking
|
package com.android.trisolarispms.ui.booking
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.CalendarMonth
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -40,8 +34,6 @@ fun BookingExpectedDatesScreen(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onDone: () -> Unit
|
onDone: () -> Unit
|
||||||
) {
|
) {
|
||||||
val showCheckInPicker = remember { mutableStateOf(false) }
|
|
||||||
val showCheckOutPicker = remember { mutableStateOf(false) }
|
|
||||||
val checkInDate = remember { mutableStateOf<LocalDate?>(null) }
|
val checkInDate = remember { mutableStateOf<LocalDate?>(null) }
|
||||||
val checkOutDate = remember { mutableStateOf<LocalDate?>(null) }
|
val checkOutDate = remember { mutableStateOf<LocalDate?>(null) }
|
||||||
val checkInTime = remember { mutableStateOf("12:00") }
|
val checkInTime = remember { mutableStateOf("12:00") }
|
||||||
@@ -50,6 +42,7 @@ fun BookingExpectedDatesScreen(
|
|||||||
val error = remember { mutableStateOf<String?>(null) }
|
val error = remember { mutableStateOf<String?>(null) }
|
||||||
val displayFormatter = remember { DateTimeFormatter.ofPattern("dd-MM-yy HH:mm") }
|
val displayFormatter = remember { DateTimeFormatter.ofPattern("dd-MM-yy HH:mm") }
|
||||||
val displayZone = remember { ZoneId.of("Asia/Kolkata") }
|
val displayZone = remember { ZoneId.of("Asia/Kolkata") }
|
||||||
|
val today = LocalDate.now(displayZone)
|
||||||
val editableCheckIn = status?.uppercase() == "OPEN"
|
val editableCheckIn = status?.uppercase() == "OPEN"
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
@@ -127,13 +120,22 @@ fun BookingExpectedDatesScreen(
|
|||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text("Expected Check-in") },
|
label = { Text("Expected Check-in") },
|
||||||
trailingIcon = {
|
|
||||||
IconButton(onClick = { showCheckInPicker.value = true }) {
|
|
||||||
Icon(Icons.Default.CalendarMonth, contentDescription = "Pick date")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
BookingDateTimePickerInline(
|
||||||
|
title = "Select check-in",
|
||||||
|
initialDate = checkInDate.value,
|
||||||
|
initialTime = checkInTime.value,
|
||||||
|
minDate = today,
|
||||||
|
onValueChange = { date, time ->
|
||||||
|
checkInDate.value = date
|
||||||
|
checkInTime.value = time
|
||||||
|
if (checkOutDate.value?.isBefore(date) == true) {
|
||||||
|
checkOutDate.value = date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
}
|
}
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
@@ -147,13 +149,20 @@ fun BookingExpectedDatesScreen(
|
|||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text("Expected Check-out") },
|
label = { Text("Expected Check-out") },
|
||||||
trailingIcon = {
|
|
||||||
IconButton(onClick = { showCheckOutPicker.value = true }) {
|
|
||||||
Icon(Icons.Default.CalendarMonth, contentDescription = "Pick date")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
val checkOutMinDate = maxOf(checkInDate.value ?: today, today)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
BookingDateTimePickerInline(
|
||||||
|
title = "Select check-out",
|
||||||
|
initialDate = checkOutDate.value,
|
||||||
|
initialTime = checkOutTime.value,
|
||||||
|
minDate = checkOutMinDate,
|
||||||
|
onValueChange = { date, time ->
|
||||||
|
checkOutDate.value = date
|
||||||
|
checkOutTime.value = time
|
||||||
|
}
|
||||||
|
)
|
||||||
if (isLoading.value) {
|
if (isLoading.value) {
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
@@ -164,34 +173,4 @@ fun BookingExpectedDatesScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showCheckInPicker.value && editableCheckIn) {
|
|
||||||
BookingDateTimePickerDialog(
|
|
||||||
title = "Select check-in",
|
|
||||||
initialDate = checkInDate.value,
|
|
||||||
initialTime = checkInTime.value,
|
|
||||||
minDate = LocalDate.now(),
|
|
||||||
onDismiss = { showCheckInPicker.value = false },
|
|
||||||
onConfirm = { date, time ->
|
|
||||||
checkInDate.value = date
|
|
||||||
checkInTime.value = time
|
|
||||||
showCheckInPicker.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showCheckOutPicker.value) {
|
|
||||||
BookingDateTimePickerDialog(
|
|
||||||
title = "Select check-out",
|
|
||||||
initialDate = checkOutDate.value,
|
|
||||||
initialTime = checkOutTime.value,
|
|
||||||
minDate = checkInDate.value ?: LocalDate.now(),
|
|
||||||
onDismiss = { showCheckOutPicker.value = false },
|
|
||||||
onConfirm = { date, time ->
|
|
||||||
checkOutDate.value = date
|
|
||||||
checkOutTime.value = time
|
|
||||||
showCheckOutPicker.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.android.trisolarispms.ui.booking
|
||||||
|
|
||||||
|
import android.app.TimePickerDialog
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Schedule
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun BookingTimePickerTextField(
|
||||||
|
value: String,
|
||||||
|
label: @Composable () -> Unit,
|
||||||
|
onTimeSelected: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val parsed = runCatching {
|
||||||
|
val parts = value.split(":")
|
||||||
|
val hour = parts.getOrNull(0)?.toIntOrNull() ?: 12
|
||||||
|
val minute = parts.getOrNull(1)?.toIntOrNull() ?: 0
|
||||||
|
(hour.coerceIn(0, 23)) to (minute.coerceIn(0, 59))
|
||||||
|
}.getOrDefault(12 to 0)
|
||||||
|
|
||||||
|
fun openDialog() {
|
||||||
|
TimePickerDialog(
|
||||||
|
context,
|
||||||
|
{ _, hourOfDay, minute ->
|
||||||
|
onTimeSelected("%02d:%02d".format(hourOfDay, minute))
|
||||||
|
},
|
||||||
|
parsed.first,
|
||||||
|
parsed.second,
|
||||||
|
true
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
|
label = label,
|
||||||
|
trailingIcon = {
|
||||||
|
IconButton(onClick = ::openDialog) {
|
||||||
|
Icon(Icons.Default.Schedule, contentDescription = "Pick time")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = modifier.clickable(onClick = ::openDialog)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.kizitonwose.calendar.core.CalendarDay
|
import com.kizitonwose.calendar.core.CalendarDay
|
||||||
import com.kizitonwose.calendar.core.CalendarMonth
|
import com.kizitonwose.calendar.core.CalendarMonth
|
||||||
@@ -29,11 +30,12 @@ fun CalendarDaysOfWeekHeader(daysOfWeek: List<java.time.DayOfWeek>) {
|
|||||||
text = day.name.take(3),
|
text = day.name.take(3),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -58,14 +60,15 @@ fun CalendarDayCell(
|
|||||||
) {
|
) {
|
||||||
val isInMonth = day.position == DayPosition.MonthDate
|
val isInMonth = day.position == DayPosition.MonthDate
|
||||||
val background = when {
|
val background = when {
|
||||||
isSelectedStart || isSelectedEnd -> MaterialTheme.colorScheme.primary.copy(alpha = 0.35f)
|
isSelectedStart || isSelectedEnd -> MaterialTheme.colorScheme.primaryContainer
|
||||||
isInRange -> MaterialTheme.colorScheme.primary.copy(alpha = 0.18f)
|
isInRange -> MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.55f)
|
||||||
hasRate -> MaterialTheme.colorScheme.secondary.copy(alpha = 0.2f)
|
hasRate -> MaterialTheme.colorScheme.secondary.copy(alpha = 0.2f)
|
||||||
else -> Color.Transparent
|
else -> Color.Transparent
|
||||||
}
|
}
|
||||||
val textColor = when {
|
val textColor = when {
|
||||||
|
isSelectedStart || isSelectedEnd -> MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
!isInMonth -> MaterialTheme.colorScheme.onSurfaceVariant
|
!isInMonth -> MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
!isSelectable -> MaterialTheme.colorScheme.error
|
!isSelectable -> MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.45f)
|
||||||
else -> MaterialTheme.colorScheme.onSurface
|
else -> MaterialTheme.colorScheme.onSurface
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
Reference in New Issue
Block a user