Skip to main content

edc_web_components/pages/
federated_catalog_page.rs

1use crate::components::{ListAssetsGallery, SelectedFederatedCatalogOffer};
2use crate::models::AssetItem;
3use edc_federated_catalog_client::{FederatedCatalogClient, FederatedCatalogClientVersion};
4use patternfly_yew::prelude::*;
5use yew::prelude::*;
6use yew::suspense::use_future_with;
7use yew_oauth2::hook::use_latest_access_token;
8
9#[derive(Clone, Debug, PartialEq, Properties)]
10pub struct CatalogPageProps {
11  pub onselectedoffer: Callback<SelectedFederatedCatalogOffer>,
12  pub on_manage_catalog: Callback<()>,
13}
14
15#[component]
16pub fn CatalogPage(props: &CatalogPageProps) -> Html {
17  let refresh = use_state(|| 0usize);
18  let fallback = html! {
19    <Bullseye>
20      <Spinner size={SpinnerSize::Lg} />
21    </Bullseye>
22  };
23
24  html!(
25    <Stack gutter=true>
26      <StackItem>
27        <Split gutter=true>
28          <SplitItem fill=true>
29            <Title level={Level::H3} size={Size::XXLarge}>{ "Catalog" }</Title>
30          </SplitItem>
31        </Split>
32      </StackItem>
33      <StackItem>
34        <Suspense {fallback}>
35          <CatalogPageInner
36            force_refresh={*refresh}
37            onselectedoffer={props.onselectedoffer.clone()}
38            on_manage_catalog={props.on_manage_catalog.clone()}
39          />
40        </Suspense>
41      </StackItem>
42    </Stack>
43  )
44}
45
46#[derive(Clone, Debug, PartialEq, Properties)]
47pub struct CatalogPageInnerProps {
48  pub force_refresh: usize,
49  pub onselectedoffer: Callback<SelectedFederatedCatalogOffer>,
50  pub on_manage_catalog: Callback<()>,
51}
52
53#[component]
54pub fn CatalogPageInner(props: &CatalogPageInnerProps) -> HtmlResult {
55  let latest_access_token_context = use_latest_access_token().unwrap();
56
57  let asset_items = use_future_with(
58    (latest_access_token_context.clone(), props.force_refresh),
59    |parameters| async move {
60      let (latest_access_token_context, _) = (*parameters).clone();
61
62      let server_url = web_sys::window().unwrap().location().origin().unwrap();
63      let federated_catalog_client = FederatedCatalogClient::new(
64        reqwest::Client::new(),
65        format!("{server_url}/federated-catalog"),
66        latest_access_token_context.access_token(),
67        FederatedCatalogClientVersion::V4,
68      );
69
70      federated_catalog_client
71        .list_offers()
72        .await
73        .unwrap_or_default()
74        .iter()
75        .flat_map(|federated_catalog_offer| {
76          federated_catalog_offer
77            .dataset
78            .clone()
79            .into_iter()
80            .map(|dataset| {
81              let dataset_id = dataset.id.clone();
82              let asset_item = AssetItem::from(dataset);
83
84              let selected_offer = SelectedFederatedCatalogOffer {
85                originator: federated_catalog_offer.originator.clone(),
86                provider_id: federated_catalog_offer.participant_id.id.clone(),
87                dataset_id,
88              };
89
90              (asset_item, selected_offer)
91            })
92            .collect::<Vec<_>>()
93        })
94        .unzip()
95    },
96  )?;
97
98  let (asset_items, selected_offers): (Vec<AssetItem>, Vec<SelectedFederatedCatalogOffer>) =
99    (*asset_items).clone();
100
101  let onshow = use_callback(
102    (props.onselectedoffer.clone(), selected_offers),
103    |dataset_id, (onselectedoffer, selected_offers)| {
104      if let Some(selected_offer) = selected_offers
105        .iter()
106        .find(|selected_offer| selected_offer.dataset_id == dataset_id)
107      {
108        onselectedoffer.emit(selected_offer.clone());
109      }
110    },
111  );
112
113  if asset_items.is_empty() {
114    Ok(html! {
115      <EmptyState
116        title="Empty state"
117        icon={Icon::Cubes}
118        primary={Action::new("Manage my catalog subscriptions", props.on_manage_catalog.reform(|_| ()))}
119      >
120        <div>
121          <p>{ "You do not have any registered catalogs yet." }</p>
122        </div>
123      </EmptyState>
124    })
125  } else {
126    Ok(html!(<ListAssetsGallery {asset_items} {onshow} />))
127  }
128}