Usprawnij wybór urlopu przy konfliktach

This commit is contained in:
admin
2026-07-09 00:31:06 +02:00
parent 8a0651e578
commit 3267fee439
4 changed files with 91 additions and 17 deletions
@@ -1053,10 +1053,23 @@ private fun LeaveCalendarScreen(
val context = LocalContext.current
val listState = rememberLazyListState()
val months = remember(state.leaveCalendarLoadedUntil) { leaveCalendarMonths(state.leaveCalendarLoadedUntil) }
var detailsDate by remember { mutableStateOf<LocalDate?>(null) }
val detailsEntries = detailsDate
?.let { DriverLeaveRequestUiRules.calendarEntriesForDate(state.leaveCalendarEntries, it) }
.orEmpty()
var showConflictDetails by remember { mutableStateOf(false) }
val selectedConflictEntries = remember(
state.leaveCalendarEntries,
state.leaveCalendarHasSelection,
state.leaveRequestDateFrom,
state.leaveRequestDateTo,
) {
if (state.leaveCalendarHasSelection) {
DriverLeaveRequestUiRules.calendarEntriesForRange(
entries = state.leaveCalendarEntries,
dateFrom = state.leaveRequestDateFrom,
dateTo = state.leaveRequestDateTo,
)
} else {
emptyList()
}
}
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
LaunchedEffect(months.size, state.leaveCalendarLoading) {
@@ -1074,6 +1087,8 @@ private fun LeaveCalendarScreen(
bottomBar = {
LeaveCalendarContinueBar(
state = state,
conflictCount = selectedConflictEntries.size,
onShowConflicts = { showConflictDetails = true },
onContinue = onContinue,
)
},
@@ -1093,11 +1108,8 @@ private fun LeaveCalendarScreen(
selectedFrom = state.leaveRequestDateFrom,
selectedTo = state.leaveRequestDateTo,
hasSelection = state.leaveCalendarHasSelection,
onDayClick = { date, entries ->
onDayClick = { date, _ ->
onDateSelected(date.toString())
if (entries.isNotEmpty()) {
detailsDate = date
}
},
)
}
@@ -1121,15 +1133,15 @@ private fun LeaveCalendarScreen(
}
}
if (detailsDate != null && detailsEntries.isNotEmpty()) {
if (showConflictDetails && selectedConflictEntries.isNotEmpty()) {
ModalBottomSheet(
onDismissRequest = { detailsDate = null },
onDismissRequest = { showConflictDetails = false },
sheetState = sheetState,
containerColor = TppTheme.colors.card,
) {
LeaveCalendarDetailsSheet(
date = detailsDate,
entries = detailsEntries,
title = "Urlopy w wybranym terminie",
entries = selectedConflictEntries,
onCall = { dialUri ->
context.startActivity(Intent(Intent.ACTION_DIAL, Uri.parse(dialUri)))
},
@@ -1139,7 +1151,12 @@ private fun LeaveCalendarScreen(
}
@Composable
private fun LeaveCalendarContinueBar(state: DriverUiState, onContinue: () -> Unit) {
private fun LeaveCalendarContinueBar(
state: DriverUiState,
conflictCount: Int,
onShowConflicts: () -> Unit,
onContinue: () -> Unit,
) {
val selectionText = when {
!state.leaveCalendarHasSelection -> "Wybierz pierwszy dzień urlopu"
else -> leaveDateRange(state.leaveRequestDateFrom, state.leaveRequestDateTo)
@@ -1178,6 +1195,28 @@ private fun LeaveCalendarContinueBar(state: DriverUiState, onContinue: () -> Uni
Text("Dalej", fontWeight = FontWeight.Bold)
}
}
if (state.leaveCalendarHasSelection && conflictCount > 0) {
Row(
Modifier
.fillMaxWidth()
.background(TppTheme.colors.warningContainer, RoundedCornerShape(4.dp))
.border(1.dp, TppTheme.colors.warningOutline, RoundedCornerShape(4.dp))
.padding(horizontal = 12.dp, vertical = 10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(Icons.Outlined.Info, contentDescription = null, tint = Color(0xFF7A5A00), modifier = Modifier.size(20.dp))
Text(
"Nakłada się z ${leaveConflictCountLabel(conflictCount)}",
color = Color(0xFF7A5A00),
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f),
)
TextButton(onClick = onShowConflicts) {
Text("Zobacz", color = TppTheme.colors.forest, fontWeight = FontWeight.Bold)
}
}
}
}
}
@@ -1307,7 +1346,7 @@ private fun LeaveCalendarDayCell(
@Composable
private fun LeaveCalendarDetailsSheet(
date: LocalDate?,
title: String,
entries: List<DriverLeaveCalendarEntryDto>,
onCall: (String) -> Unit,
) {
@@ -1316,7 +1355,7 @@ private fun LeaveCalendarDetailsSheet(
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
date?.let { DateTimeFormatter.ofPattern("d MMMM yyyy", Locale("pl", "PL")).format(it) } ?: "Urlopy w tym dniu",
title,
color = TppTheme.colors.ink,
fontWeight = FontWeight.Bold,
fontSize = 22.sp,
@@ -2926,6 +2965,9 @@ private fun photoCountLabel(count: Int): String =
else -> "zdjęć"
}
private fun leaveConflictCountLabel(count: Int): String =
if (count == 1) "1 urlopem" else "$count urlopami"
@Composable
private fun ManifestSection(route: DriverRouteDto, selectedDate: String, onNavigate: (NavigationPointDto) -> Unit) {
val stripColor = routeStatusColor(route.status)
@@ -61,6 +61,19 @@ object DriverLeaveRequestUiRules {
from != null && to != null && !date.isBefore(from) && !date.isAfter(to)
}
fun calendarEntriesForRange(entries: List<DriverLeaveCalendarEntryDto>, dateFrom: String, dateTo: String): List<DriverLeaveCalendarEntryDto> {
val selectedFrom = runCatching { LocalDate.parse(dateFrom) }.getOrNull() ?: return emptyList()
val selectedTo = runCatching { LocalDate.parse(dateTo) }.getOrNull() ?: return emptyList()
val from = minOf(selectedFrom, selectedTo)
val to = maxOf(selectedFrom, selectedTo)
return entries.filter { entry ->
val entryFrom = entry.dateFrom?.let { runCatching { LocalDate.parse(it) }.getOrNull() }
val entryTo = entry.dateTo?.let { runCatching { LocalDate.parse(it) }.getOrNull() }
entryFrom != null && entryTo != null && !entryTo.isBefore(from) && !entryFrom.isAfter(to)
}
}
fun phoneDialUri(phoneNumber: String?): String? {
val raw = phoneNumber?.trim().orEmpty()
if (raw.isBlank()) return null