edc_web_ui/components/
list_assets.rs1use crate::contexts::use_edc_connector_context;
2use edc_connector_client::types::asset::Asset;
3use edc_connector_client::types::query::Query;
4use patternfly_yew::prelude::*;
5use std::rc::Rc;
6use yew::platform::spawn_local;
7use yew::prelude::*;
8use yew::suspense::use_future_with;
9
10#[function_component]
11pub fn ListAssets() -> Html {
12 let fallback = html!("Loading ...");
13
14 html!(
15 <Suspense {fallback}>
16 <ListAssetsInner />
17 </Suspense>
18 )
19}
20
21#[function_component]
22pub fn ListAssetsInner() -> HtmlResult {
23 let edc_connector_context = use_edc_connector_context();
24
25 let offset = use_state(|| 0usize);
26 let limit = use_state(|| 10usize);
27
28 let asset_list = use_future_with(
29 (edc_connector_context, *limit, *offset),
30 |parameters| async move {
31 let (edc_connector_context, limit, offset) = &*parameters;
32
33 let query = Query::builder()
34 .limit(*limit as u32)
35 .offset(*offset as u32)
36 .build();
38
39 if let Some(client) = edc_connector_context.get_client() {
40 client.assets().query(query).await
41 } else {
42 Ok(vec![])
43 }
44 },
45 )?;
46
47 let asset_list = &(*asset_list);
48
49 let header = html_nested! {
50 <TableHeader<Columns>>
51 <TableColumn<Columns> label="Name" index={Columns::Name} />
52 <TableColumn<Columns> label="ID" index={Columns::Id} />
53 <TableColumn<Columns> label="Base URL" index={Columns::BaseUrl} />
54 <TableColumn<Columns> label="Proxy Path" index={Columns::ProxyPath} />
55 <TableColumn<Columns> label="Proxy Query Parameters" index={Columns::ProxyQueryParameters} />
56 <TableColumn<Columns> label="Proxy Method" index={Columns::ProxyMethod} />
57 <TableColumn<Columns> label="Proxy Body" index={Columns::ProxyBody} />
58 <TableColumn<Columns> label="" index={Columns::Actions} />
59 </TableHeader<Columns>>
60 };
61
62 let limit_callback = use_callback(limit.clone(), |number, limit| limit.set(number));
63
64 let total_entries: Option<usize> = None;
65
66 let nav_callback = use_callback(
67 (offset.clone(), *limit, total_entries),
68 |page: Navigation, (offset, limit, total_entries)| {
69 let o = match page {
70 Navigation::First => 0,
71 Navigation::Last => (total_entries.unwrap_or_default().saturating_sub(1) / limit) * limit,
72 Navigation::Previous => **offset - limit,
73 Navigation::Next => **offset + limit,
74 Navigation::Page(n) => n * limit,
75 };
76 offset.set(o);
77 },
78 );
79
80 let rows = asset_list
81 .as_ref()
82 .unwrap()
83 .iter()
84 .map(|asset| AssetRenderer(asset.clone()))
85 .collect();
86
87 let (entries, _) = use_table_data(MemoizedTableModel::new(Rc::new(rows)));
88
89 let table = html!(
90 <>
91 <Toolbar>
92 <ToolbarContent>
93 <ToolbarItem r#type={ToolbarItemType::Pagination}>
94 <Pagination
95 offset={*offset}
96 entries_per_page_choices={vec![5, 10, 25, 50, 100]}
97 selected_choice={*limit}
98 onlimit={&limit_callback}
99 onnavigation={&nav_callback}
100 />
101 </ToolbarItem>
102 </ToolbarContent>
103 </Toolbar>
104 <Table<Columns, UseTableData<Columns, MemoizedTableModel<AssetRenderer>>>
105 mode={TableMode::Compact}
106 {header}
107 {entries}
108 />
109 </>
110 );
111
112 Ok(table)
113}
114
115#[derive(Clone, Debug, Eq, PartialEq)]
116enum Columns {
117 Id,
118 Name,
119 BaseUrl,
120 ProxyPath,
121 ProxyQueryParameters,
122 ProxyMethod,
123 ProxyBody,
124 Actions,
125}
126
127#[derive(Clone, Debug)]
128struct AssetRenderer(Asset);
129
130impl AssetRenderer {
131 fn get_property(&self, name: &str) -> String {
132 self
133 .0
134 .properties()
135 .get::<String>(name)
136 .unwrap_or_default()
137 .unwrap_or_default()
138 .to_string()
139 }
140
141 fn get_data_address_property(&self, name: &str) -> String {
142 self
143 .0
144 .data_address()
145 .property::<String>(name)
146 .unwrap_or_default()
147 .unwrap_or_default()
148 .to_string()
149 }
150}
151
152impl TableEntryRenderer<Columns> for AssetRenderer {
153 fn render_cell(&self, context: CellContext<'_, Columns>) -> Cell {
154 match context.column {
155 Columns::Id => html! {self.0.id().to_string()},
156 Columns::Name => html!(self.get_property("name")),
157 Columns::BaseUrl => html!(self.get_data_address_property("baseUrl")),
158 Columns::ProxyPath => html!(self.get_data_address_property("proxyPath") == "true"),
159 Columns::ProxyQueryParameters => {
160 html!(self.get_data_address_property("proxyQueryParams") == "true")
161 }
162 Columns::ProxyMethod => html!(self.get_data_address_property("proxyMethod") == "true"),
163 Columns::ProxyBody => html!(self.get_data_address_property("proxyBody") == "true"),
164 Columns::Actions => {
165 let asset_id = self.0.id().to_string();
166
167 html!(
168 <DeleteAsset {asset_id} />
169 )
170 }
171 }
172 .into()
173 }
174}
175
176#[derive(Clone, PartialEq, Properties)]
177pub struct Props {
178 pub asset_id: String,
179}
180
181#[function_component]
182pub fn DeleteAsset(props: &Props) -> Html {
183 let edc_connector_context = use_edc_connector_context();
184
185 let onclick = use_callback(
186 (edc_connector_context, props.asset_id.clone()),
187 move |_, (edc_connector_context, asset_id)| {
188 let edc_connector_context = edc_connector_context.clone();
189 let asset_id = asset_id.to_string();
190
191 spawn_local(async move {
192 if let Some(client) = edc_connector_context.get_client() {
193 let _ = client.assets().delete(&asset_id).await;
194 }
195 });
196 },
197 );
198
199 html!(
200 <Button
201 variant={ButtonVariant::Danger}
202 icon={Icon::Trash}
203 {onclick}
204 >
205 {"Delete"}
206 </Button>
207 )
208}