Skip to main content

edc_web_components/components/
contract_negotiation_status.rs

1use crate::contexts::use_edc_connector_context;
2use edc_connector_client::types::contract_negotiation::{
3  ContractNegotiation, ContractNegotiationState,
4};
5use patternfly_yew::prelude::*;
6use std::time::Duration;
7use yew::platform::spawn_local;
8use yew::platform::time::sleep;
9use yew::prelude::*;
10
11const HAPPY_PATH_STATES: [(&str, ContractNegotiationState); 7] = [
12  ("Initial", ContractNegotiationState::Initial),
13  ("Requested", ContractNegotiationState::Requested),
14  ("Offered", ContractNegotiationState::Offered),
15  ("Accepted", ContractNegotiationState::Accepted),
16  ("Agreed", ContractNegotiationState::Agreed),
17  ("Verified", ContractNegotiationState::Verified),
18  ("Finalized", ContractNegotiationState::Finalized),
19];
20
21#[derive(Clone, Debug, PartialEq, Properties)]
22pub struct ContractNegotiationStatusProps {
23  pub contract_negotiation_id: String,
24  pub on_finalized: Callback<()>,
25}
26
27#[component]
28pub fn ContractNegotiationStatus(props: &ContractNegotiationStatusProps) -> Html {
29  let edc_connector_client = use_edc_connector_context();
30
31  let contract_negotiation_state = use_state(|| None);
32  let contract_negotiation_error = use_state(|| None);
33
34  use_effect_with(
35    (
36      props.contract_negotiation_id.clone(),
37      edc_connector_client.clone(),
38      contract_negotiation_state.setter(),
39      contract_negotiation_error.setter(),
40      props.on_finalized.clone(),
41    ),
42    |(
43      contract_negotiation_id,
44      edc_connector_client,
45      contract_negotiation_state_setter,
46      contract_negotiation_error_setter,
47      on_finalized,
48    )| {
49      let contract_negotiation_id = contract_negotiation_id.clone();
50      let edc_connector_client = edc_connector_client.clone();
51      let contract_negotiation_state_setter = contract_negotiation_state_setter.clone();
52      let contract_negotiation_error_setter = contract_negotiation_error_setter.clone();
53      let on_finalized = on_finalized.clone();
54
55      spawn_local(async move {
56        loop {
57          let contract_negotiation = if let Some(client) = edc_connector_client.get_client() {
58            client
59              .contract_negotiations(edc_connector_client::EdcConnectorApiVersion::V4)
60              .get(&contract_negotiation_id)
61              .await
62              .ok()
63          } else {
64            None
65          };
66
67          let state = contract_negotiation
68            .as_ref()
69            .map(|contract_negotiation| contract_negotiation.state().clone());
70
71          let error_message = contract_negotiation
72            .as_ref()
73            .and_then(|contract_negotiation| {
74              contract_negotiation
75                .error_detail()
76                .map(|error_detail| error_detail.to_string())
77            });
78
79          contract_negotiation_error_setter.set(error_message);
80
81          contract_negotiation_state_setter.set(contract_negotiation);
82
83          if state == Some(ContractNegotiationState::Finalized)
84            || state == Some(ContractNegotiationState::Terminated)
85          {
86            on_finalized.emit(());
87            break;
88          }
89
90          sleep(Duration::from_secs(1)).await;
91        }
92      });
93    },
94  );
95
96  match (
97    (*contract_negotiation_error).clone(),
98    (*contract_negotiation_state).clone(),
99  ) {
100    (Some(error_message), _) => html!(
101      <Alert title="Negotiation failed" r#type={AlertType::Danger}>
102        <div>{ error_message }</div>
103      </Alert>
104    ),
105    (None, Some(contract_negotiation)) => {
106      let contract_negotiation: ContractNegotiation = contract_negotiation;
107
108      let current_state_index = HAPPY_PATH_STATES
109        .iter()
110        .position(|(_, state)| state == contract_negotiation.state())
111        .unwrap_or_default();
112
113      let steps = HAPPY_PATH_STATES
114        .iter()
115        .enumerate()
116        .map(|(index, (state_label, _))| {
117          let status = if index < current_state_index {
118            ProgressStepperStepStatus::Success
119          } else if index == current_state_index {
120            if index == HAPPY_PATH_STATES.len() - 1 {
121              ProgressStepperStepStatus::Success
122            } else {
123              ProgressStepperStepStatus::Default
124            }
125          } else {
126            ProgressStepperStepStatus::Pending
127          };
128
129          html_nested!(
130            <ProgressStepperStep {status}>
131              <div>{ state_label.to_string() }</div>
132            </ProgressStepperStep>
133          )
134        });
135
136      html!(<ProgressStepper>{ for steps }</ProgressStepper>)
137    }
138    _ => html!(),
139  }
140}