feat: [#22] vue Scheduled (tâches planifiées groupées par date : aujourd'hui, demain, cette semaine, plus tard)

This commit is contained in:
2026-06-06 06:39:00 +02:00
parent 8827c85c82
commit 86aab6c3da
2 changed files with 116 additions and 0 deletions
@@ -0,0 +1,56 @@
package com.planify.mobile.ui.scheduled
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.planify.mobile.domain.model.Task
import com.planify.mobile.ui.components.EmptyState
import com.planify.mobile.ui.components.TaskRow
@Composable
fun ScheduledScreen(
onTaskClick: (Task) -> Unit,
viewModel: ScheduledViewModel = hiltViewModel(),
) {
val groups by viewModel.groups.collectAsState()
if (groups.isEmpty()) {
EmptyState(
icon = Icons.Outlined.CalendarMonth,
title = "Aucune tâche planifiée",
subtitle = "Les tâches avec une date d'échéance apparaîtront ici",
)
return
}
LazyColumn(modifier = Modifier.fillMaxSize()) {
groups.forEach { group ->
item(key = group.label) {
Text(
text = group.label,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
)
}
items(group.tasks, key = { it.id }) { task ->
TaskRow(
task = task,
onClick = { onTaskClick(task) },
onCheckedChange = { viewModel.toggleTask(task) },
)
}
}
}
}
@@ -0,0 +1,60 @@
package com.planify.mobile.ui.scheduled
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.planify.mobile.domain.model.Task
import com.planify.mobile.domain.repository.TaskRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import java.time.LocalDate
import javax.inject.Inject
data class ScheduledGroup(val label: String, val tasks: List<Task>)
@HiltViewModel
class ScheduledViewModel @Inject constructor(
private val taskRepository: TaskRepository,
) : ViewModel() {
val groups = taskRepository.getScheduledTasks()
.map { tasks -> groupByDate(tasks) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
private fun groupByDate(tasks: List<Task>): List<ScheduledGroup> {
val today = LocalDate.now()
val tomorrow = today.plusDays(1)
val endOfWeek = today.plusDays(7)
val buckets = linkedMapOf(
"Aujourd'hui" to mutableListOf<Task>(),
"Demain" to mutableListOf(),
"Cette semaine" to mutableListOf(),
"Plus tard" to mutableListOf(),
)
for (task in tasks) {
val date = runCatching { LocalDate.parse(task.dueDate?.date ?: "") }.getOrNull() ?: continue
when {
date == today -> buckets["Aujourd'hui"]!!.add(task)
date == tomorrow -> buckets["Demain"]!!.add(task)
date <= endOfWeek -> buckets["Cette semaine"]!!.add(task)
else -> buckets["Plus tard"]!!.add(task)
}
}
return buckets.entries
.filter { it.value.isNotEmpty() }
.map { ScheduledGroup(it.key, it.value) }
}
fun toggleTask(task: Task) {
viewModelScope.launch { taskRepository.checkTask(task.id, !task.checked) }
}
fun deleteTask(task: Task) {
viewModelScope.launch { taskRepository.deleteTask(task.id) }
}
}