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