1use crate::app::App;
2use crate::common::{render_pagination_text, ColumnId, InputFocus, SortDirection};
3use crate::keymap::Mode;
4use crate::table::{TableState, TableWindow};
5use crate::ui::filter::{render_simple_filter, SimpleFilterConfig};
6use crate::ui::table::{expanded_from_columns, render_table, TableConfig};
7use crate::ui::{format_title, titled_block};
8use crate::vpc::columns::Column;
9use ratatui::{prelude::*, widgets::Paragraph};
10use rusticity_core::vpc::Vpc;
11
12pub struct State {
13 pub vpcs: TableState<Vpc>,
14 pub input_focus: InputFocus,
15 pub column_ids: Vec<ColumnId>,
16 pub visible_column_ids: Vec<ColumnId>,
17}
18impl Default for State {
19 fn default() -> Self {
20 Self {
21 vpcs: TableState::new(),
22 input_focus: InputFocus::Filter,
23 column_ids: Column::ids(),
24 visible_column_ids: Column::default_visible_ids(),
25 }
26 }
27}
28
29pub fn filtered_vpcs(app: &App) -> Vec<&Vpc> {
30 let query = app.vpc_state.vpcs.filter.to_lowercase();
31 let mut vpcs = app.vpc_state.vpcs.filtered(|vpc| {
32 [
33 &vpc.name,
34 &vpc.vpc_id,
35 &vpc.state,
36 &vpc.ipv4_cidrs,
37 &vpc.ipv6_cidrs,
38 &vpc.owner_id,
39 &vpc.region,
40 &vpc.arn,
41 &vpc.dhcp_options_id,
42 &vpc.tenancy,
43 &vpc.encryption_control_id,
44 &vpc.encryption_control_mode,
45 &vpc.block_public_access,
46 &vpc.main_route_table,
47 &vpc.main_network_acl,
48 ]
49 .iter()
50 .any(|value| value.to_lowercase().contains(&query))
51 });
52 vpcs.sort_by(|a, b| a.vpc_id.cmp(&b.vpc_id));
53 vpcs
54}
55
56pub fn render_vpcs(frame: &mut Frame, app: &App, area: Rect) {
57 let chunks = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]).split(area);
58 let state = &app.vpc_state;
59 let view = state
60 .vpcs
61 .prepare_view(filtered_vpcs(app), TableWindow::SelectedPage);
62 let pagination = render_pagination_text(view.current_page, view.total_pages);
63 render_simple_filter(
64 frame,
65 chunks[0],
66 SimpleFilterConfig {
67 filter_text: &state.vpcs.filter,
68 placeholder: "Search by name, VPC ID, CIDR, or owner",
69 pagination: &pagination,
70 mode: app.mode,
71 is_input_focused: state.input_focus == InputFocus::Filter,
72 is_pagination_focused: state.input_focus == InputFocus::Pagination,
73 },
74 );
75 if state.vpcs.loading && state.vpcs.items.is_empty() {
76 frame.render_widget(
77 Paragraph::new("Loading VPCs...")
78 .block(titled_block("VPCs"))
79 .alignment(Alignment::Center),
80 chunks[1],
81 );
82 return;
83 }
84 let columns: Vec<_> = state
85 .visible_column_ids
86 .iter()
87 .filter_map(|id| Column::from_id(id))
88 .map(Column::into_table_column)
89 .collect();
90 let expanded_columns: Vec<_> = Column::ALL
91 .into_iter()
92 .map(Column::into_table_column)
93 .collect();
94 render_table(
95 frame,
96 TableConfig {
97 items: view.items,
98 selected_index: view.selected_index,
99 expanded_index: view.expanded_index,
100 columns: &columns,
101 sort_column: &Column::VpcId.name(),
102 sort_direction: SortDirection::Asc,
103 title: format_title(&format!("VPCs ({})", view.total_items)),
104 area: chunks[1],
105 get_expanded_content: Some(Box::new(|vpc| {
106 expanded_from_columns(&expanded_columns, vpc)
107 })),
108 is_active: app.mode == Mode::Normal,
109 },
110 );
111}