Skip to main content

edc_web_components/pages/
my_published_offer.rs

1use crate::components::{AssetCard, DatasetCard, ShowPolicy};
2use crate::contexts::use_edc_connector_context;
3use crate::models::{AssetItem, ContractDefinitionItem, DataspaceDataset};
4use edc_connector_client::EdcConnectorApiVersion;
5use edc_connector_client::types::query::Query;
6use patternfly_yew::prelude::*;
7use yew::platform::spawn_local;
8use yew::prelude::*;
9use yew::suspense::use_future_with;
10
11#[derive(Clone, Debug, PartialEq, Properties)]
12pub struct MyPublishedOfferPageProps {
13  #[prop_or("My Published Offer".to_string())]
14  pub title: String,
15  #[prop_or(None)]
16  pub tag_line: Option<String>,
17  pub asset_id: String,
18}
19
20#[component]
21pub fn MyPublishedOfferPage(props: &MyPublishedOfferPageProps) -> Html {
22  let tag_line = props
23    .tag_line
24    .as_ref()
25    .map(|tag_line| html!(<p>{ tag_line }</p>))
26    .unwrap_or_default();
27
28  html!(
29    <>
30      <Title level={Level::H2} size={Size::XXXLarge}>{ &props.title }</Title>
31      { tag_line }
32      <Suspense fallback={html!{<Bullseye><Spinner/></Bullseye>}}>
33        <MyPublishedOfferPageInner asset_id={props.asset_id.clone()} />
34      </Suspense>
35    </>
36  )
37}
38
39#[component]
40pub fn MyPublishedOfferPageInner(props: &MyPublishedOfferPageProps) -> HtmlResult {
41  let edc_connector_context = use_edc_connector_context();
42
43  let refresh = use_state(|| 0usize);
44
45  let results = use_future_with(
46    (
47      edc_connector_context.clone(),
48      props.asset_id.clone(),
49      refresh.clone(),
50    ),
51    |parameters| async move {
52      let (edc_connector_context, asset_id, _) = (*parameters).clone();
53
54      let query = Query::builder().limit(1000).build();
55
56      if let Some(client) = edc_connector_context.get_client() {
57        let asset = client
58          .assets(EdcConnectorApiVersion::V4)
59          .get(&asset_id)
60          .await
61          .ok()
62          .map(AssetItem::from);
63
64        let contract_definitions = client
65          .contract_definitions(EdcConnectorApiVersion::V4)
66          .query(query)
67          .await
68          .unwrap_or_default()
69          .into_iter()
70          .map(ContractDefinitionItem::from)
71          .filter(|contract_definition_item| contract_definition_item.asset_ids.contains(&asset_id))
72          .collect::<Vec<_>>();
73
74        let mut contract_definitions_and_policies = Vec::with_capacity(contract_definitions.len());
75
76        for contract_definition in contract_definitions.into_iter() {
77          let access_policy = client
78            .policies(EdcConnectorApiVersion::V4)
79            .get(&contract_definition.access_policy_id)
80            .await
81            .ok();
82
83          let contract_policy = client
84            .policies(EdcConnectorApiVersion::V4)
85            .get(&contract_definition.contract_policy_id)
86            .await
87            .ok();
88
89          contract_definitions_and_policies.push((
90            contract_definition,
91            access_policy,
92            contract_policy,
93          ));
94        }
95
96        (contract_definitions_and_policies, asset)
97      } else {
98        (vec![], None)
99      }
100    },
101  )?;
102
103  let (contract_definitions_and_policies, asset_item) = (*results).clone();
104
105  log::trace!("asset_item: {:?}", asset_item);
106  log::trace!(
107    "contract_definitions_and_policies: {:?}",
108    contract_definitions_and_policies
109  );
110
111  let backdropper = use_backdrop();
112
113  let unpublish_offer = use_callback(
114    (
115      edc_connector_context.clone(),
116      refresh.clone(),
117      backdropper.clone(),
118    ),
119    |contract_definition_id: String, (edc_connector_context, refresh, backdropper)| {
120      let edc_connector_context = edc_connector_context.clone();
121      let contract_definition_id = contract_definition_id.clone();
122      let refresh = refresh.clone();
123      let backdropper = backdropper.clone();
124
125      spawn_local(async move {
126        if let Some(client) = edc_connector_context.get_client() {
127          if let Err(error) = client
128            .contract_definitions(EdcConnectorApiVersion::V4)
129            .delete(&contract_definition_id)
130            .await
131          {
132            log::error!("Unable to unpublish offer: {}", error);
133          } else {
134            refresh.set(*refresh + 1);
135            if let Some(backdropper) = backdropper {
136              backdropper.close();
137            }
138          }
139        }
140      });
141    },
142  );
143
144  let unpublish = use_callback(
145    (backdropper.clone(), unpublish_offer.clone()),
146    |contract_definition_item: ContractDefinitionItem, (backdropper, unpublish_offer)| {
147      let contract_definition_id = contract_definition_item.id.clone();
148
149      let assets = contract_definition_item
150        .asset_ids
151        .iter()
152        .map(|asset_id| html!(<AssetCard asset_id={asset_id.clone()} />));
153
154      if let Some(backdropper) = backdropper {
155        backdropper.open(Backdrop::new(
156        html!(
157          <Bullseye>
158            <Modal
159              title={format!("Review Unpublish Offer: {}", contract_definition_item.name)}
160              variant={ModalVariant::Medium}
161            >
162              <Stack gutter=true>
163                <StackItem>
164                  { "By unpublishing the offer, it will remove publication of these related asset(s):" }
165                </StackItem>
166                <StackItem>
167                  <Gallery gutter=true>{ for assets }</Gallery>
168                </StackItem>
169                <StackItem>
170                  <Button
171                    variant={ButtonVariant::Danger}
172                    onclick={unpublish_offer.reform(move |_| contract_definition_id.clone())}
173                  >
174                    { "Yes, unpublish this offer" }
175                  </Button>
176                </StackItem>
177              </Stack>
178            </Modal>
179          </Bullseye>
180        )
181      ))
182      }
183    },
184  );
185
186  if let Some(asset_item) = asset_item {
187    let dataset = DataspaceDataset::from(asset_item);
188
189    let contract_definitions = contract_definitions_and_policies.iter().map(
190      |(contract_definition_item, access_policy, contract_policy)| {
191        let access_policy = access_policy.as_ref().map(|access_policy| {
192          html!(<ShowPolicy policy={access_policy.policy().clone()} hide_id=true hide_kind=true />)
193        });
194
195        let contract_policy = contract_policy.as_ref().map(|contract_policy| {
196          html!(<ShowPolicy policy={contract_policy.policy().clone()} hide_id=true hide_kind=true />)
197        });
198
199        let name = contract_definition_item.name.clone();
200        let contract_definition_item = contract_definition_item.clone();
201
202        html!(
203          <Card>
204            <CardHeader>
205              <Split>
206                <SplitItem fill=true>{ name }</SplitItem>
207                <SplitItem>
208                  <Button
209                    variant={ButtonVariant::DangerSecondary}
210                    onclick={unpublish.reform(move |_| contract_definition_item.clone())}
211                  >
212                    { "Unpublish" }
213                  </Button>
214                </SplitItem>
215              </Split>
216            </CardHeader>
217            <Divider />
218            <CardBody>
219              <Title level={Level::H6} size={Size::Large}>{ "Access Policy" }</Title>
220              { access_policy }
221            </CardBody>
222            <Divider />
223            <CardBody>
224              <Title level={Level::H6} size={Size::Large}>{ "Contract Policy" }</Title>
225              { contract_policy }
226            </CardBody>
227          </Card>
228        )
229      },
230    );
231
232    Ok(html!(
233      <Stack gutter=true>
234        <StackItem>
235          <DatasetCard {dataset} />
236        </StackItem>
237        <StackItem>
238          <Title level={Level::H4} size={Size::XLarge}>
239            { "Available Terms & Conditions for this Offer" }
240          </Title>
241          <Gallery gutter=true min_widths={AttrValue::from("400px").all()}>
242            { for contract_definitions }
243          </Gallery>
244        </StackItem>
245      </Stack>
246    ))
247  } else {
248    Ok(html!(<Bullseye>{ "Asset not found" }</Bullseye>))
249  }
250}