Skip to main content

edc_web_components/pages/
contract_negotiation_page.rs

1use crate::components::ListContractNegotiations;
2use crate::contexts::use_edc_connector_context;
3use crate::models::{ConsumerProvider, ContractNegotiationItem};
4use edc_connector_client::EdcConnectorApiVersion;
5use edc_connector_client::types::query::{Query, SortOrder};
6use patternfly_yew::prelude::*;
7use std::collections::HashSet;
8use yew::prelude::*;
9use yew::suspense::use_future_with;
10
11#[derive(Clone, Debug, PartialEq, Properties)]
12pub struct ContractNegotiationPageProps {
13  pub on_show_contract_negotiation: Callback<String>,
14  #[prop_or("List Contract Negotiations".to_string())]
15  pub title: String,
16  #[prop_or(Some("A contract negotiation represents the active, multi-step process between two parties to agree on data sharing terms.".to_string()))]
17  pub tag_line: Option<String>,
18}
19
20#[component]
21pub fn ContractNegotiationPage(props: &ContractNegotiationPageProps) -> Html {
22  let refresh = use_state(|| 0usize);
23  let offset = use_state(|| 0usize);
24  let limit = use_state(|| 10usize);
25  let consumer_provider = use_state(HashSet::default);
26  let statuses = use_state(|| {
27    vec![
28      ("Initial".to_string(), false),
29      ("Requesting".to_string(), false),
30      ("Requested".to_string(), false),
31      ("Offering".to_string(), false),
32      ("Offered".to_string(), false),
33      ("Accepting".to_string(), false),
34      ("Accepted".to_string(), false),
35      ("Agreeing".to_string(), false),
36      ("Agreed".to_string(), false),
37      ("Verifying".to_string(), false),
38      ("Verified".to_string(), false),
39      ("Finalizing".to_string(), false),
40      ("Finalized".to_string(), false),
41      ("Terminating".to_string(), false),
42      ("Terminated".to_string(), false),
43    ]
44  });
45
46  let on_offset = use_callback(
47    (refresh.clone(), offset.setter()),
48    |offset, (refresh, offset_setter)| {
49      offset_setter.set(offset);
50      refresh.set(**refresh + 1);
51    },
52  );
53
54  let on_limit = use_callback(
55    (refresh.clone(), limit.setter()),
56    |limit, (refresh, limit_setter)| {
57      limit_setter.set(limit);
58      refresh.set(**refresh + 1);
59    },
60  );
61
62  let on_consumer_provider = use_callback(
63    (refresh.clone(), consumer_provider.setter()),
64    |consumer_provider, (refresh, consumer_provider_setter)| {
65      consumer_provider_setter.set(consumer_provider);
66      refresh.set(**refresh + 1);
67    },
68  );
69
70  let on_statuses = use_callback(
71    (refresh.clone(), statuses.setter()),
72    |statuses, (refresh, statuses_setter)| {
73      statuses_setter.set(statuses);
74      refresh.set(**refresh + 1);
75    },
76  );
77
78  let fallback = html! {
79    <Bullseye>
80      <Spinner size={SpinnerSize::Lg} />
81    </Bullseye>
82  };
83
84  let tag_line = props
85    .tag_line
86    .as_ref()
87    .map(|tag_line| html!(<p>{ tag_line }</p>))
88    .unwrap_or_default();
89
90  html!(
91    <Stack gutter=true>
92      <StackItem>
93        <Title level={Level::H3} size={Size::XXLarge}>{ &props.title }</Title>
94        { tag_line }
95      </StackItem>
96      <StackItem>
97        <Suspense {fallback}>
98          <ContractNegotiationPageInner
99            offset={*offset}
100            limit={*limit}
101            statuses={(*statuses).clone()}
102            {on_offset}
103            {on_limit}
104            {on_statuses}
105            force_refresh={*refresh}
106            on_show_contract_negotiation={props.on_show_contract_negotiation.clone()}
107            consumer_provider={(*consumer_provider).clone()}
108            {on_consumer_provider}
109          />
110        </Suspense>
111      </StackItem>
112    </Stack>
113  )
114}
115
116#[derive(Clone, Debug, PartialEq, Properties)]
117pub struct ContractNegotiationPageInnerProps {
118  pub offset: usize,
119  pub limit: usize,
120  #[prop_or_default]
121  pub statuses: Vec<(String, bool)>,
122  pub on_offset: Callback<usize>,
123  pub on_limit: Callback<usize>,
124  pub on_statuses: Callback<Vec<(String, bool)>>,
125  pub force_refresh: usize,
126  pub on_show_contract_negotiation: Callback<String>,
127  #[prop_or(true)]
128  pub show_status_selector: bool,
129  #[prop_or_default]
130  pub consumer_provider: HashSet<ConsumerProvider>,
131  pub on_consumer_provider: Callback<HashSet<ConsumerProvider>>,
132}
133
134#[component]
135pub fn ContractNegotiationPageInner(props: &ContractNegotiationPageInnerProps) -> HtmlResult {
136  let edc_connector_context = use_edc_connector_context();
137
138  let contract_negotiation_items = use_future_with(
139    (
140      edc_connector_context,
141      props.limit,
142      props.offset,
143      props.consumer_provider.clone(),
144      props.statuses.clone(),
145      props.force_refresh,
146    ),
147    |parameters| async move {
148      let (edc_connector_context, limit, offset, consumer_provider, statuses, _) =
149        (*parameters).clone();
150
151      let query_builder = Query::builder().limit(limit as u32).offset(offset as u32);
152
153      let query_builder = match (
154        consumer_provider.contains(&ConsumerProvider::Consumer),
155        consumer_provider.contains(&ConsumerProvider::Provider),
156      ) {
157        (true, true) => query_builder,
158        (true, false) => query_builder.filter("type", "=", "CONSUMER"),
159        (false, true) => query_builder.filter("type", "=", "PROVIDER"),
160        (false, false) => query_builder,
161      };
162
163      let query_builder = if statuses.iter().all(|(_, selected)| *selected)
164        || statuses.iter().all(|(_, selected)| !*selected)
165      {
166        query_builder
167      } else {
168        let list = statuses
169          .iter()
170          .filter_map(|(label, selected)| {
171            if *selected {
172              Some(label.clone().to_uppercase())
173            } else {
174              None
175            }
176          })
177          .collect::<Vec<_>>();
178
179        query_builder.filter("state", "IN", list)
180      };
181
182      let query = query_builder.sort("createdAt", SortOrder::Desc).build();
183
184      if let Some(client) = edc_connector_context.get_client() {
185        client
186          .contract_negotiations(EdcConnectorApiVersion::V4)
187          .query(query)
188          .await
189          .unwrap_or_default()
190          .into_iter()
191          .map(ContractNegotiationItem::from)
192          .collect()
193      } else {
194        vec![]
195      }
196    },
197  )?;
198
199  let contract_negotiation_items = (*contract_negotiation_items).clone();
200
201  Ok(html!(
202    <ListContractNegotiations
203      contract_negotiation_items={contract_negotiation_items}
204      offset={props.offset}
205      limit={props.limit}
206      statuses={props.statuses.clone()}
207      on_offset={props.on_offset.clone()}
208      on_limit={props.on_limit.clone()}
209      on_show_contract_negotiation={props.on_show_contract_negotiation.clone()}
210      on_statuses={props.on_statuses.clone()}
211      show_status_selector={props.show_status_selector}
212      consumer_provider={props.consumer_provider.clone()}
213      on_consumer_provider={props.on_consumer_provider.clone()}
214    />
215  ))
216}