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