diff --git a/app/src/main/java/com/matedroid/ui/screens/charges/ChargesViewModel.kt b/app/src/main/java/com/matedroid/ui/screens/charges/ChargesViewModel.kt index 1827ce2..7e7ce2b 100644 --- a/app/src/main/java/com/matedroid/ui/screens/charges/ChargesViewModel.kt +++ b/app/src/main/java/com/matedroid/ui/screens/charges/ChargesViewModel.kt @@ -290,7 +290,7 @@ class ChargesViewModel @Inject constructor( val formatter = DateTimeFormatter.ISO_DATE_TIME val weekFields = WeekFields.of(Locale.getDefault()) - // Group the charges by day + // Group the charges by day val chargesByDay = charges.mapNotNull { charge -> charge.startDate?.let { try { @@ -301,56 +301,111 @@ class ChargesViewModel @Inject constructor( } }.groupBy({ it.first }, { it.second }) - return if (granularity == ChartGranularity.DAILY) { - // DAILY ranges (today, last 7 and last 30 days - // If not startDate (All Time), get the first trip, or today - val start = startDate ?: (chargesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) - val end = LocalDate.now() - val result = mutableListOf() - var current = start - while (!current.isAfter(end)) { - val key = current.toEpochDay() - val itemsInDay = chargesByDay[key] ?: emptyList() - result.add( - createChargeChartPoint( - label = current.format(DateTimeFormatter.ofPattern("d/M")), - sortKey = key, - charges = itemsInDay, - dcChargeIds = _uiState.value.dcChargeIds + return when (granularity) { + ChartGranularity.DAILY -> { + // DAILY ranges (today, last 7 and last 30 days) + // If not startDate (All Time), get the first trip, or today + val start = startDate ?: (chargesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) + val end = LocalDate.now() + val result = mutableListOf() + var current = start + while (!current.isAfter(end)) { + val key = current.toEpochDay() + val itemsInDay = chargesByDay[key] ?: emptyList() + result.add( + createChargeChartPoint( + label = current.format(DateTimeFormatter.ofPattern("d/M")), + sortKey = key, + charges = itemsInDay, + dcChargeIds = _uiState.value.dcChargeIds + ) ) - ) - current = current.plusDays(1) + current = current.plusDays(1) + } + result } - result - } else { - // WEEKLY and MONTHLY ranges - charges.mapNotNull { charge -> - charge.startDate?.let { dateStr -> - try { - val date = LocalDateTime.parse(dateStr, formatter).toLocalDate() - val (label, sortKey) = when (granularity) { - ChartGranularity.WEEKLY -> { - val firstDay = date.with(weekFields.dayOfWeek(), 1) - "W${date.get(weekFields.weekOfYear())}" to firstDay.toEpochDay() - } - else -> { // MONTHLY - date.format(DateTimeFormatter.ofPattern("MMM yy")) to YearMonth.from(date).atDay(1).toEpochDay() - } - } - Triple(label, sortKey, charge) - } catch (e: Exception) { null } + ChartGranularity.WEEKLY -> { + // WEEKLY range (last 90 days = ~13 weeks) + val start = startDate ?: (chargesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) + val end = LocalDate.now() + + // Get first day of the week for start date + var weekStart = start.with(weekFields.dayOfWeek(), 1) + // If weekStart is before start, advance to the next week + if (weekStart.isBefore(start)) { + weekStart = weekStart.plusWeeks(1) + } + + // Group charges by week + val chargesByWeek = charges.mapNotNull { charge -> + charge.startDate?.let { dateStr -> + try { + val date = LocalDateTime.parse(dateStr, formatter).toLocalDate() + val firstDayOfWeek = date.with(weekFields.dayOfWeek(), 1) + firstDayOfWeek.toEpochDay() to charge + } catch (e: Exception) { null } + } + }.groupBy({ it.first }, { it.second }) + + // Generate all weeks in range + val result = mutableListOf() + var currentWeek = weekStart + while (!currentWeek.isAfter(end)) { + val key = currentWeek.toEpochDay() + val chargesInWeek = chargesByWeek[key] ?: emptyList() + val weekOfYear = currentWeek.get(weekFields.weekOfYear()) + result.add( + createChargeChartPoint( + label = "W$weekOfYear", + sortKey = key, + charges = chargesInWeek, + dcChargeIds = _uiState.value.dcChargeIds + ) + ) + currentWeek = currentWeek.plusWeeks(1) } + result } - .groupBy { it.first to it.second } - .map { (key, list) -> - createChargeChartPoint( - key.first, - key.second, - list.map { it.third }, - dcChargeIds = _uiState.value.dcChargeIds + + ChartGranularity.MONTHLY -> { + // MONTHLY range (last year = 12 months) + val start = startDate ?: (chargesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) + val end = LocalDate.now() + + // Get first day of month for start date + val monthStart = YearMonth.from(start).atDay(1) + val monthEnd = YearMonth.from(end) + + // Group charges by month + val chargesByMonth = charges.mapNotNull { charge -> + charge.startDate?.let { dateStr -> + try { + val date = LocalDateTime.parse(dateStr, formatter).toLocalDate() + val firstDayOfMonth = YearMonth.from(date).atDay(1) + firstDayOfMonth.toEpochDay() to charge + } catch (e: Exception) { null } + } + }.groupBy({ it.first }, { it.second }) + + // Generate all months in range + val result = mutableListOf() + var currentMonth = YearMonth.from(monthStart) + while (!currentMonth.isAfter(monthEnd)) { + val firstDay = currentMonth.atDay(1) + val key = firstDay.toEpochDay() + val chargesInMonth = chargesByMonth[key] ?: emptyList() + result.add( + createChargeChartPoint( + label = firstDay.format(DateTimeFormatter.ofPattern("MMM yy")), + sortKey = key, + charges = chargesInMonth, + dcChargeIds = _uiState.value.dcChargeIds + ) ) + currentMonth = currentMonth.plusMonths(1) } - .sortedBy { it.sortKey } + result + } } } diff --git a/app/src/main/java/com/matedroid/ui/screens/drives/DrivesViewModel.kt b/app/src/main/java/com/matedroid/ui/screens/drives/DrivesViewModel.kt index 99784eb..40f8fa0 100644 --- a/app/src/main/java/com/matedroid/ui/screens/drives/DrivesViewModel.kt +++ b/app/src/main/java/com/matedroid/ui/screens/drives/DrivesViewModel.kt @@ -278,7 +278,7 @@ class DrivesViewModel @Inject constructor( val formatter = DateTimeFormatter.ISO_DATE_TIME val weekFields = WeekFields.of(Locale.getDefault()) - // Group the drives by day + // Group the drives by day val drivesByDay = drives.mapNotNull { drive -> drive.startDate?.let { try { @@ -288,48 +288,109 @@ class DrivesViewModel @Inject constructor( } }.groupBy({ it.first }, { it.second }) - return if (granularity == DriveChartGranularity.DAILY) { - // DAILY ranges (today, last 7 and last 30 days - // If not startDate (All Time), get the first trip, or today - val start = startDate ?: (drivesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) - val end = LocalDate.now() - val result = mutableListOf() - var current = start - while (!current.isAfter(end)) { - val key = current.toEpochDay() - val drivesInDay = drivesByDay[key] ?: emptyList() - result.add( - createChartPoint( - label = current.format(DateTimeFormatter.ofPattern("d/M")), - sortKey = key, - drives = drivesInDay + return when (granularity) { + DriveChartGranularity.DAILY -> { + // DAILY ranges (today, last 7 and last 30 days) + // If not startDate (All Time), get the first trip, or today + val start = startDate ?: (drivesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) + val end = LocalDate.now() + val result = mutableListOf() + var current = start + while (!current.isAfter(end)) { + val key = current.toEpochDay() + val drivesInDay = drivesByDay[key] ?: emptyList() + result.add( + createChartPoint( + label = current.format(DateTimeFormatter.ofPattern("d/M")), + sortKey = key, + drives = drivesInDay + ) ) - ) - current = current.plusDays(1) + current = current.plusDays(1) + } + result } - result - } else { - // WEEKLY and MONTHLY ranges - drives.mapNotNull { drive -> - drive.startDate?.let { dateStr -> - try { - val date = LocalDateTime.parse(dateStr, formatter).toLocalDate() - val (label, sortKey) = when (granularity) { - DriveChartGranularity.WEEKLY -> { - val firstDay = date.with(weekFields.dayOfWeek(), 1) - "W${date.get(weekFields.weekOfYear())}" to firstDay.toEpochDay() - } - else -> { // MONTHLY - date.format(DateTimeFormatter.ofPattern("MMM yy")) to YearMonth.from(date).atDay(1).toEpochDay() - } - } - Triple(label, sortKey, drive) - } catch (e: Exception) { null } + + DriveChartGranularity.WEEKLY -> { + // WEEKLY range (last 90 days = ~13 weeks) + val start = startDate ?: (drivesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) + val end = LocalDate.now() + + // Get first day of the week for start date + var weekStart = start.with(weekFields.dayOfWeek(), 1) + // If weekStart is before start, advance to the next week + if (weekStart.isBefore(start)) { + weekStart = weekStart.plusWeeks(1) + } + + // Group drives by week + val drivesByWeek = drives.mapNotNull { drive -> + drive.startDate?.let { dateStr -> + try { + val date = LocalDateTime.parse(dateStr, formatter).toLocalDate() + val firstDayOfWeek = date.with(weekFields.dayOfWeek(), 1) + firstDayOfWeek.toEpochDay() to drive + } catch (e: Exception) { null } + } + }.groupBy({ it.first }, { it.second }) + + // Generate all weeks in range + val result = mutableListOf() + var currentWeek = weekStart + while (!currentWeek.isAfter(end)) { + val key = currentWeek.toEpochDay() + val drivesInWeek = drivesByWeek[key] ?: emptyList() + val weekOfYear = currentWeek.get(weekFields.weekOfYear()) + result.add( + createChartPoint( + label = "W$weekOfYear", + sortKey = key, + drives = drivesInWeek + ) + ) + currentWeek = currentWeek.plusWeeks(1) + } + result + } + + DriveChartGranularity.MONTHLY -> { + // MONTHLY range (last year = 12 months) + val start = startDate ?: (drivesByDay.keys.minOrNull()?.let { LocalDate.ofEpochDay(it) } ?: LocalDate.now()) + val end = LocalDate.now() + + // Get first day of month for start date + val monthStart = YearMonth.from(start).atDay(1) + val monthEnd = YearMonth.from(end) + + // Group drives by month + val drivesByMonth = drives.mapNotNull { drive -> + drive.startDate?.let { dateStr -> + try { + val date = LocalDateTime.parse(dateStr, formatter).toLocalDate() + val firstDayOfMonth = YearMonth.from(date).atDay(1) + firstDayOfMonth.toEpochDay() to drive + } catch (e: Exception) { null } + } + }.groupBy({ it.first }, { it.second }) + + // Generate all months in range + val result = mutableListOf() + var currentMonth = YearMonth.from(monthStart) + while (!currentMonth.isAfter(monthEnd)) { + val firstDay = currentMonth.atDay(1) + val key = firstDay.toEpochDay() + val drivesInMonth = drivesByMonth[key] ?: emptyList() + result.add( + createChartPoint( + label = firstDay.format(DateTimeFormatter.ofPattern("MMM yy")), + sortKey = key, + drives = drivesInMonth + ) + ) + currentMonth = currentMonth.plusMonths(1) } + result } - .groupBy { it.first to it.second } - .map { (key, list) -> createChartPoint(key.first, key.second, list.map { it.third }) } - .sortedBy { it.sortKey } } }