edc_web_components/pages/
federated_catalog_page.rs1use 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}