48.8 KiB · text History 28ef654
use crate::data::{
    ContactOps, PplOps, RelationOps, SigDateOps, TierDefaultOps, TierOps, TraitDefaultOps, TraitOps,
};
use crate::do_init::lcolor;
use crate::entities::ppl::Model;
use crate::entities::prelude::{TierDefaults, Traits};
use crate::entities::{
    contact, ppl, relation, sig_date, tier, tier_defaults, trait_defaults, traits,
};
use crate::PplError;
use chrono::{Datelike, NaiveDate};
use color_eyre::owo_colors::OwoColorize;
use crossterm::event;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
use enum_iterator::{next, Sequence};
use itertools::Itertools;
use ratatui::prelude::{Constraint, Direction, Layout, Line, Modifier, Span, Style, Stylize, Text};
use ratatui::style::palette::material::AMBER;
use ratatui::style::palette::tailwind::{GREEN, ORANGE, PINK, SLATE, TEAL, WHITE};
use ratatui::style::{Color, Styled};
use ratatui::widgets::calendar::{CalendarEventStore, Monthly};
use ratatui::widgets::{Block, Borders, List, ListDirection, ListItem, ListState, Paragraph};
use ratatui::{DefaultTerminal, Frame};
use sea_orm::DatabaseConnection;
use std::ops::Index;
use time::{Date, Month, OffsetDateTime};
use tui_input::backend::crossterm::EventHandler;
use tui_input::Input;

struct Tui {
    current_tab: Tabs,
    sigdate_list: Vec<sig_date::Model>,
    sigdate_cal_state: ListState,
    trait_list: Vec<traits::Model>,
    tier_list: Vec<tier::Model>,
    rel_list: Vec<relation::Model>,
    contact_list: Vec<contact::Model>,
    ppl_list: Vec<Model>,
    ppl_state: ListState,
    ppl_editing: bool,
    ppl_field_editing: bool,
    ppl_field_idx: u8,
    ppl_editables: Vec<Editable>,
    ppl_input_a: Input,
    ppl_input_b: Input,
    ppl_input_c: Input,
    ppl_detail_state: ListState,
    def_tier_list: Vec<tier_defaults::Model>,
    def_tier_state: ListState,
    def_trait_list: Vec<trait_defaults::Model>,
    def_trait_state: ListState,
}

#[derive(Debug, PartialEq, Sequence, Clone)]
enum Tabs {
    Ppl,
    Calendar,
    TierSettings,
    TraitSettings,
}

#[derive(Clone, Debug)]
pub enum Editablez {
    Trait,
    Tier,
    Contact,
    Relation,
    SigDate,
}

#[derive(Clone, Debug)]
pub struct Editable {
    tgt: Editablez,
    pub id: i32,
    pub first: String,
    pub second: Option<String>,
    pub third: Option<String>,
}

fn key_in_tier(key: String, tiers: &Vec<tier_defaults::Model>) -> bool {
    let t = tiers
        .iter()
        .map(|t| t.key.to_string())
        .collect::<Vec<String>>();
    t.contains(&key)
}

fn get_key_in_tier(key: String, tiers: &Vec<tier_defaults::Model>) -> bool {
    let t = tiers
        .iter()
        .map(|t| t.key.to_string())
        .collect::<Vec<String>>();

    tiers.iter().find(|t| t.key == key);
    t.contains(&key)
}

impl Tui {
    async fn reload(&mut self, db: &DatabaseConnection) -> Result<(), PplError> {
        self.ppl_list = PplOps::list(db).await?;
        self.def_trait_list = TraitDefaultOps::list(db).await?;
        self.def_tier_list = TierDefaultOps::list(db).await?;
        self.tier_list = TierOps::list(db).await?;
        self.trait_list = TraitOps::list(db).await?;
        self.rel_list = RelationOps::list(db).await?;
        self.contact_list = ContactOps::list(db).await?;
        self.sigdate_list = SigDateOps::list(db).await?;

        Ok(())
    }
}

impl Default for Tui {
    fn default() -> Self {
        Tui {
            current_tab: Tabs::Ppl,
            sigdate_list: vec![],
            sigdate_cal_state: ListState::default(),
            trait_list: vec![],
            tier_list: vec![],
            rel_list: vec![],
            contact_list: vec![],
            ppl_list: vec![],
            ppl_state: ListState::default(),
            ppl_editing: false,
            ppl_field_editing: false,
            ppl_field_idx: 0,
            ppl_editables: vec![],
            ppl_input_a: Input::default(),
            ppl_input_b: Input::default(),
            ppl_input_c: Input::default(),
            ppl_detail_state: ListState::default(),
            def_tier_list: vec![],
            def_tier_state: ListState::default(),
            def_trait_list: vec![],
            def_trait_state: ListState::default(),
        }
    }
}

fn get_style(mine: u8, cursor: u8, total: u8) -> Color {
    if cursor % total == mine {
        WHITE
    } else {
        SLATE.c500
    }
}

fn get_cal<'a>(
    month: Month,
    year: i32,
    events: &CalendarEventStore,
) -> Monthly<'a, &CalendarEventStore> {
    let default_style = Style::default()
        .add_modifier(Modifier::BOLD)
        .fg(AMBER.c500)
        .bg(SLATE.c800);

    Monthly::new(Date::from_calendar_date(year, month, 1).unwrap(), events)
        .show_month_header(Style::default())
        .default_style(default_style)
}

pub async fn run_tui(
    mut terminal: DefaultTerminal,
    db: DatabaseConnection,
) -> Result<(), PplError> {
    let mut app = Tui::default();
    app.reload(&db).await?;
    loop {
        let _ = terminal.draw(|f| render(f, &mut app));
        match event::read()? {
            Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
                match key_event.code {
                    KeyCode::Up => match app.current_tab {
                        Tabs::Ppl => match app.ppl_editing {
                            true => match app.ppl_field_editing {
                                true => {}
                                false => app.ppl_detail_state.scroll_up_by(1),
                            },

                            false => app.ppl_state.scroll_up_by(1),
                        },
                        Tabs::TierSettings => app.def_tier_state.scroll_up_by(1),
                        Tabs::TraitSettings => app.def_trait_state.scroll_up_by(1),
                        _ => {}
                    },
                    KeyCode::Down => match app.current_tab {
                        Tabs::Ppl => match app.ppl_editing {
                            true => match app.ppl_field_editing {
                                true => {}
                                false => app.ppl_detail_state.scroll_down_by(1),
                            },
                            false => app.ppl_state.scroll_down_by(1),
                        },
                        Tabs::TierSettings => app.def_tier_state.scroll_down_by(1),
                        Tabs::TraitSettings => app.def_trait_state.scroll_down_by(1),
                        _ => {}
                    },
                    KeyCode::Tab => match app.ppl_field_editing {
                        true => {
                            app.ppl_field_idx += 1;
                        }
                        false => app.current_tab = next(&app.current_tab).unwrap_or(Tabs::Ppl),
                    },
                    KeyCode::Esc => break Ok(()),
                    KeyCode::Char('e') => {
                        if app.current_tab == Tabs::Ppl {
                            match app.ppl_field_editing {
                                true => {
                                    default_editing_handler(&mut app, &key_event);
                                }
                                false => {
                                    app.ppl_editing = !app.ppl_editing;
                                }
                            }
                        }
                    }
                    KeyCode::Char('f') => {
                        default_editing_handler(&mut app, &key_event);
                    }
                    KeyCode::Enter => {
                        if app.current_tab == Tabs::Ppl {
                            if app.ppl_editing && app.ppl_detail_state.selected().is_some() {
                                let idx = &app.ppl_detail_state.selected().unwrap();
                                let e = app.ppl_editables.get(*idx).unwrap();
                                if !app.ppl_field_editing {
                                    // let k
                                    app.ppl_input_a = e.first.clone().into();
                                    app.ppl_input_b =
                                        e.second.clone().unwrap_or("".to_string()).into();
                                    app.ppl_input_c =
                                        e.third.clone().unwrap_or("".to_string()).into();
                                } else {
                                    let new_editable = Editable {
                                        tgt: e.tgt.clone(),
                                        id: e.id,
                                        first: app.ppl_input_a.value().parse().unwrap(),
                                        second: Option::from(app.ppl_input_b.value().to_string()),
                                        third: Option::from(app.ppl_input_c.value().to_string()),
                                    };

                                    match e.tgt {
                                        Editablez::Trait => {
                                            TraitOps::updatee(&db, new_editable).await;
                                        }
                                        Editablez::Tier => {
                                            TierOps::updatee(&db, new_editable).await;
                                        }
                                        Editablez::Contact => {
                                            ContactOps::updatee(&db, new_editable).await;
                                        }
                                        Editablez::Relation => {
                                            RelationOps::updatee(&db, new_editable).await;
                                        }
                                        Editablez::SigDate => {
                                            SigDateOps::updatee(&db, new_editable).await;
                                        }
                                    };
                                    app.reload(&db).await?;
                                }
                                app.ppl_field_editing = !app.ppl_field_editing;
                            }
                        }
                    }
                    _ => {
                        default_editing_handler(&mut app, &key_event);
                    }
                }
            }

            _ => {}
        }
    }
}

fn default_editing_handler(app: &mut Tui, key_event: &KeyEvent) {
    if app.ppl_editing {
        let idx = &app.ppl_detail_state.selected().unwrap();
        let e = app.ppl_editables.get(*idx).unwrap();
        match e.tgt {
            Editablez::Trait => match app.ppl_field_idx % 2 {
                0 => {
                    app.ppl_input_a.handle_event(&Event::Key(*key_event));
                }
                1 => {
                    app.ppl_input_b.handle_event(&Event::Key(*key_event));
                }
                _ => {}
            },
            Editablez::Tier => match app.ppl_field_idx % 3 {
                0 => {
                    app.ppl_input_a.handle_event(&Event::Key(*key_event));
                }
                1 => {
                    app.ppl_input_b.handle_event(&Event::Key(*key_event));
                }
                2 => {
                    app.ppl_input_c.handle_event(&Event::Key(*key_event));
                }
                _ => {}
            },
            Editablez::Contact => match app.ppl_field_idx % 3 {
                0 => {
                    app.ppl_input_a.handle_event(&Event::Key(*key_event));
                }
                1 => {
                    app.ppl_input_b.handle_event(&Event::Key(*key_event));
                }
                2 => {
                    app.ppl_input_c.handle_event(&Event::Key(*key_event));
                }
                _ => {}
            },
            Editablez::Relation => match app.ppl_field_idx % 3 {
                0 => {
                    app.ppl_input_a.handle_event(&Event::Key(*key_event));
                }
                1 => {
                    app.ppl_input_b.handle_event(&Event::Key(*key_event));
                }
                2 => {
                    app.ppl_input_c.handle_event(&Event::Key(*key_event));
                }
                _ => {}
            },
            Editablez::SigDate => match app.ppl_field_idx % 3 {
                0 => {
                    app.ppl_input_a.handle_event(&Event::Key(*key_event));
                }
                1 => {
                    app.ppl_input_b.handle_event(&Event::Key(*key_event));
                }
                2 => {
                    app.ppl_input_c.handle_event(&Event::Key(*key_event));
                }
                _ => {}
            },
        }
        {}
    }
}

fn render(f: &mut Frame, app: &mut Tui) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(2)
        .constraints(
            [
                Constraint::Length(1),
                Constraint::Percentage(100),
                Constraint::Length(1),
            ]
            .as_ref(),
        )
        .split(f.area());

    let msgw = vec![
        Span::raw("welcome to "),
        Span::styled(
            "ppl",
            Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
        ),
        Span::raw(" :)"),
    ];
    let textw = Text::from(Line::from(msgw)).style(Style::default());
    let welcome = Paragraph::new(textw);
    f.render_widget(welcome, chunks[0]);

    match app.current_tab {
        Tabs::Ppl => {
            match app.ppl_editing {
                true => {
                    let msgw = vec![
                        Span::styled(
                            "Enter",
                            Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                        ),
                        Span::raw(" to modify a ppl entry. "),
                        Span::styled(
                            "Shift+Enter",
                            Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                        ),
                        Span::raw(" to add a ppl entry"),
                    ];
                    let textw = Text::from(Line::from(msgw)).style(Style::default());
                    let welcome = Paragraph::new(textw);
                    f.render_widget(welcome, chunks[2]);
                }
                false => {
                    let msgw = vec![
                        Span::styled(
                            "Tab",
                            Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                        ),
                        Span::raw(" to switch views"),
                        Span::styled(
                            "Enter",
                            Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                        ),
                        Span::raw(" to add ppl. "),
                        Span::styled("E", Style::default().add_modifier(Modifier::BOLD).fg(WHITE)),
                        Span::raw(" to edit the selected ppl. "),
                    ];
                    let textw = Text::from(Line::from(msgw)).style(Style::default());
                    let welcome = Paragraph::new(textw);
                    f.render_widget(welcome, chunks[2]);
                }
            }

            let layout_ppl = Layout::default()
                .direction(Direction::Horizontal)
                .margin(0)
                .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
                .split(chunks[1]);
            let layout_edit = Layout::default()
                .direction(Direction::Vertical)
                .margin(0)
                .constraints([
                    Constraint::Length(1),
                    Constraint::Length(3),
                    Constraint::Length(3),
                    Constraint::Length(3),
                    Constraint::Length(3),
                ])
                .split(layout_ppl[1]);
            let ppls: Vec<PplAndProps> = app
                .ppl_list
                .iter()
                .map(|p| PplAndProps {
                    ppl: p.clone(),
                    tiers: app
                        .tier_list
                        .iter()
                        .filter(|t| t.ppl_id == p.id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<tier::Model>>(),
                    tier_defaults: app.def_tier_list.clone(),
                })
                .collect::<Vec<PplAndProps>>();

            let ppl = List::new(ppls)
                .block(Block::bordered().title("ppl"))
                .style(Style::new().white())
                .highlight_style(Style::new().italic())
                .highlight_symbol(">>")
                .repeat_highlight_symbol(true)
                .direction(ListDirection::TopToBottom);

            f.render_stateful_widget(&ppl, layout_ppl[0], &mut app.ppl_state);

            let idx = app.ppl_state.selected();
            match idx {
                None => {}
                Some(val) => {
                    let curr = app.ppl_list.index(val);

                    let mut msgs: Vec<Line> = vec![];
                    let mut editables: Vec<Editable> = vec![];

                    app.trait_list.sort_by_key(|k| {
                        (
                            key_in_tier(k.key.clone(), &app.def_tier_list),
                            k.key.clone(),
                            k.value.clone(),
                        )
                    });
                    let curr_traits = app
                        .trait_list
                        .iter()
                        .filter(|t| t.ppl_id == curr.id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<traits::Model>>();
                    let trait_defaults = app
                        .def_trait_list
                        .iter()
                        .filter(|t| !t.is_date && !t.is_contact)
                        .collect::<Vec<&trait_defaults::Model>>();
                    for t in &curr_traits {
                        editables.push(Editable {
                            tgt: Editablez::Trait,
                            id: t.id,
                            first: t.key.clone(),
                            second: Option::from(t.value.clone()),
                            third: t.hidden.clone().to_string().into(),
                        });
                        match trait_defaults.iter().find(|f| f.key == t.key) {
                            None => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("ℹ️ {}: ", t.key.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.value.clone()),
                            ])),
                            Some(td) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("{} {}: ", td.symbol, t.key.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.value.clone()),
                            ])),
                        }
                    }

                    let curr_tiers = app
                        .tier_list
                        .iter()
                        .filter(|t| t.ppl_id == curr.id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<tier::Model>>();

                    for t in &curr_tiers {
                        let othersym = app.def_tier_list.iter().find(|dt| dt.key == t.name);
                        editables.push(Editable {
                            id: t.id,
                            tgt: Editablez::Tier,
                            first: t.name.clone(),
                            second: t.color.clone(),
                            third: t.symbol.clone(),
                        });
                        match (&t.symbol, othersym) {
                            (None, Some(other)) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!(
                                        "{} {}: ",
                                        &other.symbol.clone().unwrap(),
                                        t.name.clone()
                                    ),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.name.clone()),
                            ])),
                            (Some(main), None) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("{} {}: ", main, t.name.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.name.clone()),
                            ])),
                            (None, None) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("ℹ️ {}: ", t.name.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.name.clone()),
                            ])),
                            // _ => {}
                            (Some(main), Some(other)) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("{} {}: ", main, t.name.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.name.clone()),
                            ])),
                        }
                    }

                    let curr_rels = app
                        .rel_list
                        .iter()
                        .filter(|t| t.ppl_id_b == curr.id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<relation::Model>>();
                    for t in &curr_rels {
                        let i2 = t.date_entered.map(|d| d.to_string());
                        let i3 = t.date_ended.map(|d| d.to_string());
                        editables.push(Editable {
                            id: t.id,
                            tgt: Editablez::Relation,
                            first: t.r#type.clone(),
                            second: i2,
                            third: i3,
                        });

                        msgs.push(Line::from(vec![
                            Span::styled(
                                format!("🫂 {}: ", t.r#type.clone()),
                                Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                            ),
                            Span::raw(format!("{:?} - {:?}", t.date_entered, t.date_ended)),
                        ]))
                    }

                    let curr_dates = app
                        .sigdate_list
                        .iter()
                        .filter(|t| t.ppl_id == curr.id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<sig_date::Model>>();
                    let date_traits = app
                        .def_trait_list
                        .iter()
                        .filter(|t| t.is_date && t.enabled)
                        .collect::<Vec<&trait_defaults::Model>>();
                    for t in &curr_dates {
                        editables.push(Editable {
                            id: t.id,
                            tgt: Editablez::SigDate,
                            first: t.event.clone(),
                            second: Option::from(t.date.to_string()),
                            third: Option::from(t.do_remind.to_string()),
                        });
                        match (date_traits.iter().find(|f| f.key == t.event), t.do_remind) {
                            (Some(dt), _) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("{} {}: ", dt.symbol, t.event.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.date.clone().to_string()),
                            ])),
                            (None, true) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("⏰ {}: ", t.event.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.date.clone().to_string()),
                            ])),
                            (None, false) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("📅 {}: ", t.event.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.date.clone().to_string()),
                            ])),
                        }
                    }

                    let contacts = app
                        .contact_list
                        .iter()
                        .filter(|t| t.ppl_id == curr.id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<contact::Model>>();
                    let contact_traits = app
                        .def_trait_list
                        .iter()
                        .filter(|t| t.is_contact && t.enabled)
                        .collect::<Vec<&trait_defaults::Model>>();
                    for t in &contacts {
                        editables.push(Editable {
                            id: t.id,
                            tgt: Editablez::Contact,
                            first: t.r#type.clone(),
                            second: Option::from(t.designator.clone().unwrap_or("".to_string())),
                            third: Option::from(t.value.clone()),
                        });

                        match contact_traits.iter().find(|f| f.key == t.r#type) {
                            Some(dt) => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("{} {}: ", dt.symbol, t.r#type.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.value.clone().to_string()),
                            ])),
                            None => msgs.push(Line::from(vec![
                                Span::styled(
                                    format!("📇 {}: ", t.r#type.clone()),
                                    Style::default().add_modifier(Modifier::BOLD).fg(WHITE),
                                ),
                                Span::raw(t.value.clone().to_string()),
                            ])),
                        }
                    }

                    app.ppl_editables = editables.clone();
                    let list_contact = List::new(msgs.clone())
                        .block(Block::bordered().title("Circles"))
                        .style(Style::new().white())
                        .highlight_style(Style::new().italic())
                        .highlight_symbol(">>")
                        .repeat_highlight_symbol(true)
                        .direction(ListDirection::TopToBottom);

                    let textw = Text::from(msgs.clone()).style(Style::default());
                    let paragraph = Paragraph::new(textw);
                    match app.ppl_editing {
                        true => match app.ppl_field_editing {
                            true => {
                                let ced = editables
                                    .get(app.ppl_detail_state.selected().unwrap())
                                    .unwrap();
                                match ced.tgt.clone() {
                                    Editablez::Trait => {
                                        let input_a = Paragraph::new(app.ppl_input_a.value())
                                            .block(
                                                Block::default().borders(Borders::ALL).title("key"),
                                            )
                                            .style(get_style(0, app.ppl_field_idx, 2));
                                        f.render_widget(input_a, layout_edit[1]);

                                        let input_b = Paragraph::new(app.ppl_input_b.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("value"),
                                            )
                                            .style(get_style(1, app.ppl_field_idx, 2));
                                        f.render_widget(input_b, layout_edit[2]);
                                    }
                                    Editablez::Tier => {
                                        let input_b = Paragraph::new(app.ppl_input_a.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("name"),
                                            )
                                            .style(get_style(0, app.ppl_field_idx, 3));
                                        f.render_widget(input_b, layout_edit[1]);
                                        let input_b = Paragraph::new(app.ppl_input_b.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("color"),
                                            )
                                            .style(get_style(1, app.ppl_field_idx, 3));
                                        f.render_widget(input_b, layout_edit[2]);

                                        let input_c = Paragraph::new(app.ppl_input_c.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("symbol"),
                                            )
                                            .style(get_style(2, app.ppl_field_idx, 3));
                                        f.render_widget(input_c, layout_edit[3]);
                                    }
                                    Editablez::Contact => {
                                        let input_a = Paragraph::new(app.ppl_input_a.value())
                                            .block(
                                                Block::default().borders(Borders::ALL).title("typ"),
                                            )
                                            .style(get_style(0, app.ppl_field_idx, 3));
                                        f.render_widget(input_a, layout_edit[1]);

                                        let input_b = Paragraph::new(app.ppl_input_b.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("designator"),
                                            )
                                            .style(get_style(1, app.ppl_field_idx, 3));
                                        f.render_widget(input_b, layout_edit[2]);
                                        let input_c = Paragraph::new(app.ppl_input_c.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("value"),
                                            )
                                            .style(get_style(2, app.ppl_field_idx, 3));
                                        f.render_widget(input_c, layout_edit[3]);
                                    }
                                    Editablez::Relation => {
                                        let input_a = Paragraph::new(app.ppl_input_a.value())
                                            .block(
                                                Block::default().borders(Borders::ALL).title("key"),
                                            )
                                            .style(get_style(0, app.ppl_field_idx, 3));
                                        f.render_widget(input_a, layout_edit[1]);

                                        let input_b = Paragraph::new(app.ppl_input_b.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("start"),
                                            )
                                            .style(get_style(1, app.ppl_field_idx, 3));
                                        f.render_widget(input_b, layout_edit[2]);

                                        let input_c = Paragraph::new(app.ppl_input_c.value())
                                            .block(
                                                Block::default().borders(Borders::ALL).title("end"),
                                            )
                                            .style(get_style(2, app.ppl_field_idx, 3));
                                        f.render_widget(input_c, layout_edit[3]);
                                    }
                                    Editablez::SigDate => {
                                        let input_a = Paragraph::new(app.ppl_input_a.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("date"),
                                            )
                                            .style(get_style(0, app.ppl_field_idx, 3));
                                        f.render_widget(input_a, layout_edit[1]);

                                        let input_b = Paragraph::new(app.ppl_input_b.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("date"),
                                            )
                                            .style(get_style(1, app.ppl_field_idx, 3));
                                        f.render_widget(input_b, layout_edit[2]);

                                        let input_c = Paragraph::new(app.ppl_input_c.value())
                                            .block(
                                                Block::default()
                                                    .borders(Borders::ALL)
                                                    .title("do_remind"),
                                            )
                                            .style(get_style(2, app.ppl_field_idx, 3));
                                        f.render_widget(input_c, layout_edit[3]);
                                    }
                                }
                                let title = match &curr.nick {
                                    None => {
                                        format!("Editing {}", curr.name.clone())
                                    }
                                    Some(n) => {
                                        format!("Editing {} ({})", curr.name.clone(), n)
                                    }
                                };
                                let block = Block::new()
                                    .borders(Borders::LEFT)
                                    .title(title)
                                    .style(Style::default().fg(ORANGE.c500));
                                f.render_widget(paragraph.clone().block(block), layout_edit[0])
                            }
                            false => {
                                let title = match &curr.nick {
                                    None => {
                                        format!("Editing {}", curr.name.clone())
                                    }
                                    Some(n) => {
                                        format!("Editing {} ({})", curr.name.clone(), n)
                                    }
                                };
                                let block = Block::new()
                                    .borders(Borders::LEFT)
                                    .title(title)
                                    .style(Style::default().fg(AMBER.c500));
                                f.render_stateful_widget(
                                    list_contact.block(block),
                                    layout_ppl[1],
                                    &mut app.ppl_detail_state,
                                );
                            }
                        },
                        false => {
                            let title = match &curr.nick {
                                None => {
                                    format!("{}", curr.name.clone())
                                }
                                Some(n) => {
                                    format!("{} ({})", curr.name.clone(), n)
                                }
                            };
                            let block = Block::new().borders(Borders::ALL).title(title);
                            f.render_widget(paragraph.clone().block(block), layout_ppl[1]);
                        }
                    };
                }
            }
        }
        Tabs::Calendar => {
            let layout_cal = Layout::default()
                .direction(Direction::Horizontal)
                .margin(0)
                .constraints([Constraint::Percentage(26), Constraint::Percentage(74)])
                .split(chunks[1]);

            let mut start = OffsetDateTime::now_local()
                .unwrap()
                .date()
                .replace_month(Month::January)
                .unwrap()
                .replace_day(1)
                .unwrap();
            let as_upcoming = &app
                .sigdate_list
                .iter()
                .map(|mut d| {
                    let mut n = d.clone();
                    n.date = NaiveDate::from_ymd(start.year(), n.date.month(), n.date.day());
                    let p = app
                        .ppl_list
                        .iter()
                        .find(|p| p.id == n.ppl_id)
                        .unwrap()
                        .clone();
                    let tr = app
                        .trait_list
                        .iter()
                        .filter(|p| p.id == n.ppl_id)
                        .map(|t| t.to_owned())
                        .collect::<Vec<traits::Model>>();
                    crate::do_tui::SigDateAndProps {
                        date: n,
                        ppl: p,
                        traits: tr,
                        trait_defaults: app.def_trait_list.clone(),
                    }
                })
                .sorted_by(|a, b| a.date.date.cmp(&b.date.date))
                .collect::<Vec<SigDateAndProps>>();
            let mut event_list = CalendarEventStore::today(
                Style::default()
                    .add_modifier(Modifier::BOLD)
                    .bg(Color::Blue),
            );

            for u in as_upcoming {
                event_list.add(
                    // Date::from_calendar_date(start.year(), Month::try_from(4 as u8).expect("oops"), 15 as u8).expect("doubleoops"),
                    Date::from_calendar_date(
                        start.year(),
                        Month::try_from(u.date.date.month() as u8).expect("oops"),
                        u.date.date.day() as u8,
                    )
                    .expect("doubleoops"),
                    Style::default()
                        .add_modifier(Modifier::BOLD)
                        .fg(GREEN.c300)
                        .bg(PINK.c500),
                );
            }

            // let _ = as_upcoming.iter().map(|u| {
            //     let _ = event_list.add(
            //         Date::from_calendar_date(start.year(), Month::try_from(4 as u8).expect("oops"), 15 as u8).expect("doubleoops"),
            //         // Date::from_calendar_date(start.year(), Month::try_from(u.date.month() as u8).expect("oops"), u.date.day() as u8).expect("doubleoops"),
            //         Style::default()
            //             .add_modifier(Modifier::BOLD)
            //             .fg(GREEN.c300)
            //             .bg(PINK.c500),
            //     );
            // });

            let rows = Layout::vertical([Constraint::Ratio(1, 2); 2]).split(layout_cal[1]);
            let cols = rows.iter().flat_map(|row| {
                Layout::horizontal([Constraint::Ratio(1, 6); 6])
                    .split(*row)
                    .to_vec()
            });
            for col in cols {
                let cal = get_cal(start.month(), start.year(), &event_list);
                f.render_widget(cal, col);
                start = start.replace_month(start.month().next()).unwrap();
            }

            let dates = List::new(as_upcoming)
                .block(Block::bordered().title("upcoming"))
                .style(Style::new().white())
                .highlight_style(Style::new().italic())
                .highlight_symbol(">>")
                .repeat_highlight_symbol(true)
                .direction(ListDirection::TopToBottom);

            f.render_stateful_widget(dates, layout_cal[0], &mut app.sigdate_cal_state);

            // let layout_cal_detail = Layout::default()
            //     .direction(Direction::Horizontal)
            //     .margin(0)
            //     .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
            //     .split(chunks[1]);
        }
        Tabs::TierSettings => {
            let ppl = List::new(&app.def_tier_list)
                .block(Block::bordered().title("tiers"))
                .style(Style::new().white())
                .highlight_style(Style::new().italic())
                .highlight_symbol(">>")
                .repeat_highlight_symbol(true)
                .direction(ListDirection::TopToBottom);

            f.render_stateful_widget(ppl, chunks[1], &mut app.def_tier_state);
        }
        Tabs::TraitSettings => {
            let ppl = List::new(&app.def_trait_list)
                .block(Block::bordered().title("traits"))
                .style(Style::new().white())
                .highlight_style(Style::new().italic())
                .highlight_symbol(">>")
                .repeat_highlight_symbol(true)
                .direction(ListDirection::TopToBottom);

            f.render_stateful_widget(ppl, chunks[1], &mut app.def_trait_state);
        }
        _ => {}
    }
}

struct PplAndProps {
    ppl: ppl::Model,
    tier_defaults: Vec<tier_defaults::Model>,
    tiers: Vec<tier::Model>,
}

struct SigDateAndProps {
    date: sig_date::Model,
    ppl: ppl::Model,
    traits: Vec<traits::Model>,
    trait_defaults: Vec<trait_defaults::Model>,
}

impl From<PplAndProps> for ListItem<'_> {
    fn from(value: PplAndProps) -> Self {
        // value.date_up;
        let line = match value.ppl.me {
            true => Line::styled(
                format!(" 🌟 {}", value.ppl.nick.unwrap_or(value.ppl.name)),
                WHITE,
            ),
            false => match value.tiers.len() {
                0 => Line::styled(
                    format!("   {}", value.ppl.nick.unwrap_or(value.ppl.name)),
                    WHITE,
                ),
                _ => {
                    let t = value.tiers.first().unwrap();
                    match value.tier_defaults.iter().find(|q| q.key == t.name) {
                        None => Line::styled(
                            format!("   {}", value.ppl.nick.unwrap_or(value.ppl.name)),
                            WHITE,
                        ),
                        Some(tierd) => match t.symbol.clone().unwrap_or("".to_string()).as_str() {
                            "" => Line::styled(
                                format!(
                                    " {} {}",
                                    tierd.symbol.clone().unwrap_or("".to_string()),
                                    value.ppl.nick.unwrap_or(value.ppl.name)
                                ),
                                lcolor(
                                    &t.color
                                        .clone()
                                        .unwrap_or(tierd.color.clone().unwrap_or("".to_string())),
                                ),
                            ),
                            otherwise => Line::styled(
                                format!(
                                    " {} {}",
                                    otherwise,
                                    value.ppl.nick.unwrap_or(value.ppl.name)
                                ),
                                lcolor(
                                    &t.color
                                        .clone()
                                        .unwrap_or(tierd.color.clone().unwrap_or("".to_string())),
                                ),
                            ),
                        },
                    }
                }
            },
        };
        ListItem::new(line)
    }
}

impl From<&SigDateAndProps> for ListItem<'_> {
    fn from(value: &SigDateAndProps) -> Self {
        let n = value.ppl.nick.clone().unwrap_or(value.ppl.name.clone());
        let default = value
            .trait_defaults
            .iter()
            .find(|q| q.key == value.date.event && q.is_date == true);
        let line = match (value.date.do_remind, default) {
            (true, Some(t)) => {
                Line::styled(format!(" ✓ {} {:6} {}", t.symbol, n, value.date.date), WHITE)
            }
            (true, None) => Line::styled(
                format!(" ✓ 📅 {:6} {} ({})", n, value.date.date, value.date.event),
                WHITE,
            ),
            (false, Some(t)) => Line::styled(
                format!(" ☐ {} {:6} {}", t.symbol, n, value.date.date),
                SLATE.c500,
            ),

            (false, None) => Line::styled(
                format!(" ☐ 📅 {:6} {} ({})", n, value.date.date, value.date.event),
                SLATE.c500,
            ),
        };

        ListItem::new(line)
    }
}

impl From<&ppl::Model> for ListItem<'_> {
    fn from(value: &Model) -> Self {
        value.date_up;
        let line = match value.me {
            true => Line::styled(format!(" 🌟 {}", value.name), WHITE),
            false => Line::styled(format!("   {}", value.name), WHITE),
        };
        ListItem::new(line)
    }
}

impl From<&tier_defaults::Model> for ListItem<'_> {
    fn from(value: &tier_defaults::Model) -> Self {
        let line = match value.enabled {
            true => Line::styled(format!(" ✓ {:?} {}", value.symbol, value.key), lcolor(&value.color.clone().unwrap_or("".to_string()))),
            false => Line::styled(format!(" ☐ {:?} {}", value.symbol, value.key), SLATE.c500),
        };

        ListItem::new(line)
    }
}
impl From<&trait_defaults::Model> for ListItem<'_> {
    fn from(value: &trait_defaults::Model) -> Self {
        let line = match value.enabled {
            true => Line::styled(format!(" ✓ {} {}", value.symbol, value.key), lcolor(&value.color)),
            false => Line::styled(format!(" ☐ {} {}", value.symbol, value.key), SLATE.c500),
        };

        ListItem::new(line)
    }
}

impl From<&sig_date::Model> for ListItem<'_> {
    fn from(value: &sig_date::Model) -> Self {
        let line = match value.do_remind {
            true => Line::styled(
                format!(" ✓ {} {} {}", value.ppl_id, value.event, value.date),
                WHITE,
            ),
            false => Line::styled(
                format!(" ☐ {} {} {}", value.ppl_id, value.event, value.date),
                SLATE.c500,
            ),
        };

        ListItem::new(line)
    }
}