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            <p>
31              { "Explore and access available data offers from your list of trusted and followed participants." }
32            </p>
33          </SplitItem>
34        </Split>
35      </StackItem>
36      <StackItem>
37        <Suspense {fallback}>
38          <CatalogPageInner
39            force_refresh={*refresh}
40            onselectedoffer={props.onselectedoffer.clone()}
41            on_manage_catalog={props.on_manage_catalog.clone()}
42          />
43        </Suspense>
44      </StackItem>
45    </Stack>
46  )
47}
48
49#[derive(Clone, Debug, PartialEq, Properties)]
50pub struct CatalogPageInnerProps {
51  pub force_refresh: usize,
52  pub onselectedoffer: Callback<SelectedFederatedCatalogOffer>,
53  pub on_manage_catalog: Callback<()>,
54}
55
56#[component]
57pub fn CatalogPageInner(props: &CatalogPageInnerProps) -> HtmlResult {
58  let latest_access_token_context = use_latest_access_token().unwrap();
59
60  let asset_items = use_future_with(
61    (latest_access_token_context.clone(), props.force_refresh),
62    |parameters| async move {
63      let (latest_access_token_context, _) = (*parameters).clone();
64
65      let server_url = web_sys::window().unwrap().location().origin().unwrap();
66      let federated_catalog_client = FederatedCatalogClient::new(
67        reqwest::Client::new(),
68        format!("{server_url}/federated-catalog"),
69        latest_access_token_context.access_token(),
70        FederatedCatalogClientVersion::V4,
71      );
72
73      federated_catalog_client
74        .list_offers()
75        .await
76        .unwrap_or_default()
77        .iter()
78        .flat_map(|federated_catalog_offer| {
79          federated_catalog_offer
80            .dataset
81            .clone()
82            .into_iter()
83            .map(|dataset| {
84              let dataset_id = dataset.id.clone();
85              let asset_item = AssetItem::from(dataset);
86
87              let selected_offer = SelectedFederatedCatalogOffer {
88                originator: federated_catalog_offer.originator.clone(),
89                provider_id: federated_catalog_offer.participant_id.id.clone(),
90                dataset_id,
91              };
92
93              (asset_item, selected_offer)
94            })
95            .collect::<Vec<_>>()
96        })
97        .unzip()
98    },
99  )?;
100
101  let (asset_items, selected_offers): (Vec<AssetItem>, Vec<SelectedFederatedCatalogOffer>) =
102    (*asset_items).clone();
103
104  let onshow = use_callback(
105    (props.onselectedoffer.clone(), selected_offers),
106    |dataset_id, (onselectedoffer, selected_offers)| {
107      if let Some(selected_offer) = selected_offers
108        .iter()
109        .find(|selected_offer| selected_offer.dataset_id == dataset_id)
110      {
111        onselectedoffer.emit(selected_offer.clone());
112      }
113    },
114  );
115
116  if asset_items.is_empty() {
117    Ok(html! {
118      <EmptyState
119        title="Empty state"
120        icon={Icon::Cubes}
121        primary={Action::new("Manage my catalog subscriptions", props.on_manage_catalog.reform(|_| ()))}
122      >
123        <div>
124          <p>{ "You do not have any registered catalogs yet." }</p>
125        </div>
126      </EmptyState>
127    })
128  } else {
129    Ok(html!(<ListAssetsGallery {asset_items} {onshow} />))
130  }
131}