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 </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}