Skip to main content

edc_web_components/components/
list_assets.rs

1use crate::components::DatasetCard;
2use crate::models::{AssetItem, DataspaceDataset};
3use patternfly_yew::prelude::*;
4use std::rc::Rc;
5use yew::prelude::*;
6
7#[derive(Clone, Debug, PartialEq, Properties)]
8pub struct ListAssetsProps {
9  pub asset_items: Vec<AssetItem>,
10  pub offset: usize,
11  pub limit: usize,
12  pub onoffset: Callback<usize>,
13  pub onlimit: Callback<usize>,
14  pub onshow: Callback<String>,
15  #[prop_or(AttrValue::Static("Select"))]
16  pub button_label: AttrValue,
17}
18
19#[component]
20pub fn ListAssets(props: &ListAssetsProps) -> Html {
21  let display_cards = use_state(|| true);
22  let total_entries: Option<usize> = None;
23
24  let nav_callback = use_callback(
25    (
26      props.offset,
27      props.limit,
28      total_entries,
29      props.onoffset.clone(),
30    ),
31    |page: Navigation, (offset, limit, total_entries, onoffset)| {
32      let offset = match page {
33        Navigation::First => 0,
34        Navigation::Last => (total_entries.unwrap_or_default().saturating_sub(1) / limit) * limit,
35        Navigation::Previous => *offset - limit,
36        Navigation::Next => *offset + limit,
37        Navigation::Page(n) => n * limit,
38      };
39
40      onoffset.emit(offset);
41    },
42  );
43
44  let set_display_cards = use_callback(display_cards.setter(), |value, display_cards_setter| {
45    display_cards_setter.set(value);
46  });
47
48  let items_render = if *display_cards {
49    html!(
50      <ListAssetsGallery
51        asset_items={props.asset_items.clone()}
52        onshow={props.onshow.clone()}
53        button_label={props.button_label.clone()}
54      />
55    )
56  } else {
57    html!(
58      <ListAssetsTable
59        asset_items={props.asset_items.clone()}
60        onshow={props.onshow.clone()}
61        button_label={props.button_label.clone()}
62      />
63    )
64  };
65
66  html!(
67    <>
68      <Toolbar>
69        <ToolbarContent>
70          <ToolbarItem>
71            <Button
72              variant={ButtonVariant::Tertiary}
73              icon={Icon::ThLarge}
74              disabled={*display_cards}
75              onclick={set_display_cards.reform(|_| true)}
76            />
77            <Button
78              variant={ButtonVariant::Tertiary}
79              icon={Icon::List}
80              disabled={!*display_cards}
81              onclick={set_display_cards.reform(|_| false)}
82            />
83          </ToolbarItem>
84          <ToolbarItem r#type={ToolbarItemType::Pagination}>
85            <Pagination
86              offset={props.offset}
87              entries_per_page_choices={vec![5, 10, 25, 50, 100]}
88              selected_choice={props.limit}
89              onlimit={&props.onlimit}
90              onnavigation={&nav_callback}
91            />
92          </ToolbarItem>
93        </ToolbarContent>
94      </Toolbar>
95      { items_render }
96    </>
97  )
98}
99
100#[derive(Clone, Debug, PartialEq, Properties)]
101pub struct ListAssetsTableProps {
102  pub asset_items: Vec<AssetItem>,
103  pub onshow: Callback<String>,
104  #[prop_or(AttrValue::Static("Select"))]
105  pub button_label: AttrValue,
106}
107
108#[component]
109pub fn ListAssetsGallery(props: &ListAssetsTableProps) -> Html {
110  let rows = props
111    .asset_items
112    .clone()
113    .into_iter()
114    .map(|asset_item| DataspaceDataset {
115      id: asset_item.id,
116      title: asset_item.name,
117      version: asset_item.version,
118      comment: asset_item.description,
119      thumbnail: asset_item.thumbnail,
120      creator: asset_item.creator,
121      keywords: asset_item.keywords,
122      policies: vec![],
123      dcterm_types: vec![],
124    })
125    .map(|dataset| {
126      let asset_id = dataset.id.clone();
127
128      html!(
129        <DatasetCard
130          {dataset}
131          on_offer_click={props.onshow.reform(move |_| asset_id.clone())}
132          button_label={props.button_label.clone()}
133        />
134      )
135    });
136
137  html!(<Gallery gutter=true>{ for rows }</Gallery>)
138}
139
140#[component]
141pub fn ListAssetsTable(props: &ListAssetsTableProps) -> Html {
142  let header = html_nested! {
143    <TableHeader<Columns>>
144      <TableColumn<Columns> label="Name" index={Columns::Name} />
145      <TableColumn<Columns> label="Base URL" index={Columns::BaseUrl} />
146      <TableColumn<Columns> label="Proxy Path" index={Columns::ProxyPath} />
147      <TableColumn<Columns> label="Proxy Query Parameters" index={Columns::ProxyQueryParameters} />
148      <TableColumn<Columns> label="Proxy Method" index={Columns::ProxyMethod} />
149      <TableColumn<Columns> label="Proxy Body" index={Columns::ProxyBody} />
150      <TableColumn<Columns> label="" index={Columns::Actions} />
151    </TableHeader<Columns>>
152  };
153
154  let rows = props
155    .asset_items
156    .iter()
157    .map(|asset_item| AssetRenderer {
158      asset_item: asset_item.clone(),
159      onshow: props.onshow.clone(),
160    })
161    .collect();
162
163  let (entries, _) = use_table_data(MemoizedTableModel::new(Rc::new(rows)));
164
165  html!(
166    <Table<Columns, UseTableData<Columns, MemoizedTableModel<AssetRenderer>>>
167      mode={TableMode::Compact}
168      {header}
169      {entries}
170    />
171  )
172}
173
174#[derive(Clone, Debug, Eq, PartialEq)]
175enum Columns {
176  Name,
177  BaseUrl,
178  ProxyPath,
179  ProxyQueryParameters,
180  ProxyMethod,
181  ProxyBody,
182  Actions,
183}
184
185#[derive(Clone, Debug)]
186struct AssetRenderer {
187  asset_item: AssetItem,
188  onshow: Callback<String>,
189}
190
191impl TableEntryRenderer<Columns> for AssetRenderer {
192  fn render_cell(&self, context: CellContext<'_, Columns>) -> Cell {
193    match context.column {
194      Columns::Name => html!(self.asset_item.name.to_owned()),
195      Columns::BaseUrl => html!(self.asset_item.base_url.to_owned()),
196      Columns::ProxyPath => html!(self.asset_item.proxy_path),
197      Columns::ProxyQueryParameters => html!(self.asset_item.proxy_query_params),
198      Columns::ProxyMethod => html!(self.asset_item.proxy_method),
199      Columns::ProxyBody => html!(self.asset_item.proxy_body),
200      Columns::Actions => {
201        let asset_id = self.asset_item.id.to_string();
202
203        html!(<ShowAsset {asset_id} onshow={self.onshow.clone()} />)
204      }
205    }
206    .into()
207  }
208}
209
210#[derive(Clone, PartialEq, Properties)]
211pub struct ShowAssetProps {
212  pub asset_id: String,
213  pub onshow: Callback<String>,
214}
215
216#[function_component]
217pub fn ShowAsset(props: &ShowAssetProps) -> Html {
218  let onclick = use_callback(
219    (props.asset_id.clone(), props.onshow.clone()),
220    |_, (asset_id, onshow)| {
221      onshow.emit(asset_id.to_string());
222    },
223  );
224
225  html!(<Button variant={ButtonVariant::Primary} icon={Icon::Eye} {onclick}>{ "Show" }</Button>)
226}