edc_web_ui/components/
list_assets.rs

1use 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        // .filter("https://w3id.org/edc/v0.0.1/ns/master-catalog-company-id", "=", "424F9F7A-BBC8-4BAD-B128-C3D0A693ABBA")
37        .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}