things3_cloud/ui/views/
upcoming.rs1use std::sync::Arc;
2
3use iocraft::prelude::*;
4
5use crate::{
6 common::{ICONS, fmt_date},
7 store::{Task, ThingsStore},
8 ui::components::{
9 empty_text::EmptyText,
10 tasks::{TaskList, TaskOptions},
11 },
12};
13
14const LIST_INDENT: u32 = 4;
15
16#[derive(Default, Props)]
17pub struct UpcomingViewProps<'a> {
18 pub items: Option<&'a Vec<Task>>,
19 pub detailed: bool,
20}
21
22#[component]
23pub fn UpcomingView<'a>(hooks: Hooks, props: &UpcomingViewProps<'a>) -> impl Into<AnyElement<'a>> {
24 let store = hooks.use_context::<Arc<ThingsStore>>().clone();
25 let Some(items) = props.items else {
26 return element! { Text(content: "") }.into_any();
27 };
28
29 if items.is_empty() {
30 return element! { EmptyText(content: "No upcoming tasks.") }.into_any();
31 }
32
33 let id_prefix_len =
34 store.unique_prefix_length(&items.iter().map(|t| t.uuid.clone()).collect::<Vec<_>>());
35
36 let mut groups: Vec<(String, Vec<&Task>)> = Vec::new();
37 for task in items {
38 let day = fmt_date(task.start_date);
39 if let Some((current_day, day_tasks)) = groups.last_mut()
40 && *current_day == day
41 {
42 day_tasks.push(task);
43 } else {
44 groups.push((day, vec![task]));
45 }
46 }
47
48 let options = TaskOptions {
49 detailed: props.detailed,
50 show_project: false,
51 show_area: false,
52 show_today_markers: true,
53 show_staged_today_marker: false,
54 };
55
56 let mut sections: Vec<AnyElement<'a>> = Vec::new();
57 for (day, day_items) in groups.into_iter() {
58 sections.push(
59 element! {
60 View(flex_direction: FlexDirection::Column) {
61 Text(content: format!(" {}", day), wrap: TextWrap::NoWrap, weight: Weight::Bold)
62 View(flex_direction: FlexDirection::Column, padding_left: LIST_INDENT) {
63 TaskList(items: day_items, id_prefix_len, options)
64 }
65 }
66 }
67 .into_any(),
68 );
69 }
70
71 element! {
72 View(flex_direction: FlexDirection::Column, gap: 1) {
73 Text(
74 content: format!("{} Upcoming ({} tasks)", ICONS.upcoming, items.len()),
75 wrap: TextWrap::NoWrap,
76 color: Color::Cyan,
77 weight: Weight::Bold,
78 )
79 View(flex_direction: FlexDirection::Column, gap: 1) {
80 #(sections)
81 }
82 }
83 }
84 .into_any()
85}