Ukrywaj wybrane salda w aplikacji kierowcy
This commit is contained in:
@@ -88,6 +88,13 @@ data class DriverAppSettingsDto(
|
|||||||
data class LeaveRequestsConfigDto(
|
data class LeaveRequestsConfigDto(
|
||||||
val enabled: Boolean = false,
|
val enabled: Boolean = false,
|
||||||
val types: List<String> = listOf("URLOP"),
|
val types: List<String> = listOf("URLOP"),
|
||||||
|
val usagePoolCodes: List<String> = listOf(
|
||||||
|
"ANNUAL",
|
||||||
|
"ANNUAL_DISABILITY",
|
||||||
|
"FORCE_MAJEURE",
|
||||||
|
"CHILD_CARE_14",
|
||||||
|
"CARE_LEAVE",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DispatchSheetReminderDto(
|
data class DispatchSheetReminderDto(
|
||||||
@@ -252,6 +259,13 @@ data class DriverLeaveSummaryDto(
|
|||||||
val dailyNormMinutes: Int = 480,
|
val dailyNormMinutes: Int = 480,
|
||||||
val balanceConfigured: Boolean = false,
|
val balanceConfigured: Boolean = false,
|
||||||
val overdueQuantity: Int = 0,
|
val overdueQuantity: Int = 0,
|
||||||
|
val visibleUsagePoolCodes: List<String> = listOf(
|
||||||
|
"ANNUAL",
|
||||||
|
"ANNUAL_DISABILITY",
|
||||||
|
"FORCE_MAJEURE",
|
||||||
|
"CHILD_CARE_14",
|
||||||
|
"CARE_LEAVE",
|
||||||
|
),
|
||||||
val availableTypes: List<LeaveRequestTypeDto> = emptyList(),
|
val availableTypes: List<LeaveRequestTypeDto> = emptyList(),
|
||||||
val reminders: List<DriverLeaveReminderDto> = emptyList(),
|
val reminders: List<DriverLeaveReminderDto> = emptyList(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1547,6 +1547,12 @@ private fun LeaveBalanceOverview(state: DriverUiState) {
|
|||||||
val summary = state.leaveSummary
|
val summary = state.leaveSummary
|
||||||
val totals = DriverLeaveRequestUiRules.annualTotals(summary)
|
val totals = DriverLeaveRequestUiRules.annualTotals(summary)
|
||||||
val norm = summary?.dailyNormMinutes ?: 480
|
val norm = summary?.dailyNormMinutes ?: 480
|
||||||
|
val showAnnualUsage = DriverLeaveRequestUiRules.isUsageVisible(summary, "ANNUAL")
|
||||||
|
val otherPools = DriverLeaveRequestUiRules.otherEntitlementPools(summary)
|
||||||
|
|
||||||
|
if (summary != null && !showAnnualUsage && otherPools.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
colors = CardDefaults.cardColors(containerColor = TppTheme.colors.card),
|
colors = CardDefaults.cardColors(containerColor = TppTheme.colors.card),
|
||||||
@@ -1555,99 +1561,107 @@ private fun LeaveBalanceOverview(state: DriverUiState) {
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(18.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) {
|
Column(Modifier.padding(18.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) {
|
||||||
Text("Twój urlop wypoczynkowy", color = TppTheme.colors.ink, fontWeight = FontWeight.Bold, fontSize = 20.sp)
|
Text(
|
||||||
|
if (showAnnualUsage) "Twój urlop wypoczynkowy" else "Twoje limity nieobecności",
|
||||||
|
color = TppTheme.colors.ink,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
)
|
||||||
|
|
||||||
when {
|
if (showAnnualUsage) {
|
||||||
summary == null -> {
|
when {
|
||||||
Text("Pobieramy aktualne saldo…", color = TppTheme.colors.muted)
|
summary == null -> {
|
||||||
}
|
Text("Pobieramy aktualne saldo…", color = TppTheme.colors.muted)
|
||||||
!summary.balanceConfigured -> {
|
|
||||||
Text(
|
|
||||||
"Kadry przygotowują Twoje saldo",
|
|
||||||
color = Color(0xFF7A5A00),
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
"Możesz zapisać wniosek, ale jego godziny zostaną odjęte dopiero po potwierdzeniu puli urlopu.",
|
|
||||||
color = TppTheme.colors.muted,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Text("Pozostało do wykorzystania", color = TppTheme.colors.muted, fontWeight = FontWeight.SemiBold)
|
|
||||||
Text(
|
|
||||||
DriverLeaveRequestUiRules.formatQuantity(totals.availableMinutes, "minutes", norm),
|
|
||||||
color = TppTheme.colors.forest,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 26.sp,
|
|
||||||
)
|
|
||||||
val denominator = (totals.grantedMinutes).coerceAtLeast(1)
|
|
||||||
LinearProgressIndicator(
|
|
||||||
progress = { (totals.usedMinutes.toFloat() / denominator).coerceIn(0f, 1f) },
|
|
||||||
modifier = Modifier.fillMaxWidth().height(7.dp),
|
|
||||||
color = TppTheme.colors.forest,
|
|
||||||
trackColor = TppTheme.colors.successContainer,
|
|
||||||
)
|
|
||||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
|
||||||
LeaveBalanceStat(
|
|
||||||
title = "Wykorzystano",
|
|
||||||
value = DriverLeaveRequestUiRules.formatQuantity(totals.usedMinutes, "minutes", norm),
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
)
|
|
||||||
LeaveBalanceStat(
|
|
||||||
title = "W przyszłych urlopach",
|
|
||||||
value = DriverLeaveRequestUiRules.formatQuantity(totals.reservedMinutes, "minutes", norm),
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
!summary.balanceConfigured -> {
|
||||||
}
|
|
||||||
|
|
||||||
if (totals.overdueMinutes > 0) {
|
|
||||||
Row(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(TppTheme.colors.warningContainer, RoundedCornerShape(6.dp))
|
|
||||||
.border(1.dp, TppTheme.colors.warningOutline, RoundedCornerShape(6.dp))
|
|
||||||
.padding(12.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
|
||||||
verticalAlignment = Alignment.Top,
|
|
||||||
) {
|
|
||||||
Icon(Icons.Outlined.Info, contentDescription = null, tint = Color(0xFF7A5A00))
|
|
||||||
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(3.dp)) {
|
|
||||||
Text("Masz zaległy urlop", color = Color(0xFF7A5A00), fontWeight = FontWeight.Bold)
|
|
||||||
Text(
|
Text(
|
||||||
"${DriverLeaveRequestUiRules.formatQuantity(totals.overdueMinutes, "minutes", norm)} zaplanuj możliwie szybko, najpóźniej do 30 września. Saldo nie zniknie automatycznie.",
|
"Kadry przygotowują Twoje saldo",
|
||||||
color = Color(0xFF7A5A00),
|
color = Color(0xFF7A5A00),
|
||||||
fontSize = 13.sp,
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
"Możesz zapisać wniosek, ale jego godziny zostaną odjęte dopiero po potwierdzeniu puli urlopu.",
|
||||||
|
color = TppTheme.colors.muted,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Text("Pozostało do wykorzystania", color = TppTheme.colors.muted, fontWeight = FontWeight.SemiBold)
|
||||||
|
Text(
|
||||||
|
DriverLeaveRequestUiRules.formatQuantity(totals.availableMinutes, "minutes", norm),
|
||||||
|
color = TppTheme.colors.forest,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 26.sp,
|
||||||
|
)
|
||||||
|
val denominator = (totals.grantedMinutes).coerceAtLeast(1)
|
||||||
|
LinearProgressIndicator(
|
||||||
|
progress = { (totals.usedMinutes.toFloat() / denominator).coerceIn(0f, 1f) },
|
||||||
|
modifier = Modifier.fillMaxWidth().height(7.dp),
|
||||||
|
color = TppTheme.colors.forest,
|
||||||
|
trackColor = TppTheme.colors.successContainer,
|
||||||
|
)
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
|
LeaveBalanceStat(
|
||||||
|
title = "Wykorzystano",
|
||||||
|
value = DriverLeaveRequestUiRules.formatQuantity(totals.usedMinutes, "minutes", norm),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
LeaveBalanceStat(
|
||||||
|
title = "W przyszłych urlopach",
|
||||||
|
value = DriverLeaveRequestUiRules.formatQuantity(totals.reservedMinutes, "minutes", norm),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totals.overdueMinutes > 0) {
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(TppTheme.colors.warningContainer, RoundedCornerShape(6.dp))
|
||||||
|
.border(1.dp, TppTheme.colors.warningOutline, RoundedCornerShape(6.dp))
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
verticalAlignment = Alignment.Top,
|
||||||
|
) {
|
||||||
|
Icon(Icons.Outlined.Info, contentDescription = null, tint = Color(0xFF7A5A00))
|
||||||
|
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(3.dp)) {
|
||||||
|
Text("Masz zaległy urlop", color = Color(0xFF7A5A00), fontWeight = FontWeight.Bold)
|
||||||
|
Text(
|
||||||
|
"${DriverLeaveRequestUiRules.formatQuantity(totals.overdueMinutes, "minutes", norm)} zaplanuj możliwie szybko, najpóźniej do 30 września. Saldo nie zniknie automatycznie.",
|
||||||
|
color = Color(0xFF7A5A00),
|
||||||
|
fontSize = 13.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverLeaveRequestUiRules.annualPools(summary).forEach { pool ->
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text(
|
||||||
|
if (pool.year < LocalDate.now().year) "Urlop zaległy z ${pool.year} r." else "Pula na ${pool.year} r.",
|
||||||
|
color = TppTheme.colors.muted,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
DriverLeaveRequestUiRules.formatQuantity(pool.availableQuantity, pool.unit, norm),
|
||||||
|
color = TppTheme.colors.ink,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
textAlign = TextAlign.End,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DriverLeaveRequestUiRules.annualPools(summary).forEach { pool ->
|
if (otherPools.isNotEmpty()) {
|
||||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
if (showAnnualUsage) {
|
||||||
|
HorizontalDivider(color = TppTheme.colors.outline)
|
||||||
Text(
|
Text(
|
||||||
if (pool.year < LocalDate.now().year) "Urlop zaległy z ${pool.year} r." else "Pula na ${pool.year} r.",
|
"Pozostałe dostępne limity",
|
||||||
color = TppTheme.colors.muted,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
DriverLeaveRequestUiRules.formatQuantity(pool.availableQuantity, pool.unit, norm),
|
|
||||||
color = TppTheme.colors.ink,
|
color = TppTheme.colors.ink,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
textAlign = TextAlign.End,
|
fontSize = 17.sp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val otherPools = DriverLeaveRequestUiRules.otherEntitlementPools(summary)
|
|
||||||
if (otherPools.isNotEmpty()) {
|
|
||||||
HorizontalDivider(color = TppTheme.colors.outline)
|
|
||||||
Text(
|
|
||||||
"Pozostałe dostępne limity",
|
|
||||||
color = TppTheme.colors.ink,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 17.sp,
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
"Te limity są rozliczane osobno i nie zmniejszają zwykłego urlopu wypoczynkowego.",
|
"Te limity są rozliczane osobno i nie zmniejszają zwykłego urlopu wypoczynkowego.",
|
||||||
color = TppTheme.colors.muted,
|
color = TppTheme.colors.muted,
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ object DriverLeaveRequestUiRules {
|
|||||||
.filter { it.poolCode == "ANNUAL" && it.status == "active" }
|
.filter { it.poolCode == "ANNUAL" && it.status == "active" }
|
||||||
.sortedByDescending { it.year }
|
.sortedByDescending { it.year }
|
||||||
|
|
||||||
|
fun isUsageVisible(summary: DriverLeaveSummaryDto?, poolCode: String): Boolean =
|
||||||
|
summary?.visibleUsagePoolCodes?.contains(poolCode) ?: true
|
||||||
|
|
||||||
fun otherEntitlementPools(summary: DriverLeaveSummaryDto?): List<DriverLeavePoolDto> =
|
fun otherEntitlementPools(summary: DriverLeaveSummaryDto?): List<DriverLeavePoolDto> =
|
||||||
summary?.pools.orEmpty()
|
summary?.pools.orEmpty()
|
||||||
.filter { it.poolCode != "ANNUAL" && it.status == "active" }
|
.filter { it.poolCode != "ANNUAL" && it.status == "active" }
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class DriverLeaveApiContractTest {
|
|||||||
"dailyNormMinutes": 480,
|
"dailyNormMinutes": 480,
|
||||||
"balanceConfigured": true,
|
"balanceConfigured": true,
|
||||||
"overdueQuantity": 480,
|
"overdueQuantity": 480,
|
||||||
|
"visibleUsagePoolCodes": ["ANNUAL", "CHILD_CARE_14"],
|
||||||
"pools": [{
|
"pools": [{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"poolCode": "ANNUAL",
|
"poolCode": "ANNUAL",
|
||||||
@@ -49,6 +50,7 @@ class DriverLeaveApiContractTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(480, response.data.dailyNormMinutes)
|
assertEquals(480, response.data.dailyNormMinutes)
|
||||||
|
assertEquals(listOf("ANNUAL", "CHILD_CARE_14"), response.data.visibleUsagePoolCodes)
|
||||||
assertEquals(7680, response.data.pools.single().availableQuantity)
|
assertEquals(7680, response.data.pools.single().availableQuantity)
|
||||||
assertEquals("URLOP", response.data.availableTypes.single().value)
|
assertEquals("URLOP", response.data.availableTypes.single().value)
|
||||||
assertEquals("ANNUAL", response.data.availableTypes.single().code)
|
assertEquals("ANNUAL", response.data.availableTypes.single().code)
|
||||||
|
|||||||
@@ -124,6 +124,13 @@ class DriverLeaveRequestUiRulesTest {
|
|||||||
assertEquals(listOf("CHILD_CARE_14", "CARE_LEAVE"), pools.map { it.poolCode })
|
assertEquals(listOf("CHILD_CARE_14", "CARE_LEAVE"), pools.map { it.poolCode })
|
||||||
assertEquals("Urlop opiekuńczy", DriverLeaveRequestUiRules.poolLabel("CARE_LEAVE"))
|
assertEquals("Urlop opiekuńczy", DriverLeaveRequestUiRules.poolLabel("CARE_LEAVE"))
|
||||||
assertEquals("Rodzina i opieka", DriverLeaveRequestUiRules.categoryLabel("family"))
|
assertEquals("Rodzina i opieka", DriverLeaveRequestUiRules.categoryLabel("family"))
|
||||||
|
assertTrue(DriverLeaveRequestUiRules.isUsageVisible(summary, "ANNUAL"))
|
||||||
|
assertFalse(
|
||||||
|
DriverLeaveRequestUiRules.isUsageVisible(
|
||||||
|
summary.copy(visibleUsagePoolCodes = emptyList()),
|
||||||
|
"ANNUAL",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user