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