Skip to main content

edc_web_components/components/
list_contract_negotiations.rs

1use crate::components::MultiStateSelector;
2use crate::models::ContractNegotiationItem;
3use patternfly_yew::prelude::*;
4use std::rc::Rc;
5use yew::prelude::*;
6
7#[derive(Clone, Debug, PartialEq, Properties)]
8pub struct ListContractNegotiationsProps {
9  pub contract_negotiation_items: Vec<ContractNegotiationItem>,
10  pub offset: usize,
11  pub limit: usize,
12  pub switch: bool,
13  pub statuses: Vec<(String, bool)>,
14  pub on_offset: Callback<usize>,
15  pub on_limit: Callback<usize>,
16  pub on_switch_view_consumer: Callback<bool>,
17  pub on_show_contract_negotiation: Callback<String>,
18  pub on_statuses: Callback<Vec<(String, bool)>>,
19  #[prop_or(true)]
20  pub show_status_selector: bool,
21  #[prop_or(true)]
22  pub show_consumer_provider_switch: bool,
23}
24
25#[component]
26pub fn ListContractNegotiations(props: &ListContractNegotiationsProps) -> Html {
27  let header = html_nested! {
28    <TableHeader<Columns>>
29      <TableColumn<Columns> label="State" index={Columns::State} />
30      <TableColumn<Columns> label="Contract Agreement ID" index={Columns::ContractAgreementId} />
31      <TableColumn<Columns> label="Counter Party ID" index={Columns::CounterPartyId} />
32      <TableColumn<Columns> label="Protocol" index={Columns::Protocol} />
33      <TableColumn<Columns> label="Kind" index={Columns::Kind} />
34      <TableColumn<Columns> label="" index={Columns::Actions} />
35    </TableHeader<Columns>>
36  };
37
38  let total_entries: Option<usize> = None;
39
40  let nav_callback = use_callback(
41    (
42      props.offset,
43      props.limit,
44      total_entries,
45      props.on_offset.clone(),
46    ),
47    |page: Navigation, (offset, limit, total_entries, on_offset)| {
48      let offset = match page {
49        Navigation::First => 0,
50        Navigation::Last => (total_entries.unwrap_or_default().saturating_sub(1) / limit) * limit,
51        Navigation::Previous => *offset - limit,
52        Navigation::Next => *offset + limit,
53        Navigation::Page(n) => n * limit,
54      };
55      on_offset.emit(offset);
56    },
57  );
58
59  let rows = props
60    .contract_negotiation_items
61    .iter()
62    .map(
63      |contract_negotiation_item| ContractNegotiationItemRenderer {
64        item: contract_negotiation_item.clone(),
65        on_show_contract_negotiation: props.on_show_contract_negotiation.clone(),
66      },
67    )
68    .collect();
69
70  let (entries, _) = use_table_data(MemoizedTableModel::new(Rc::new(rows)));
71
72  let statuses_selector = if props.show_status_selector {
73    html!(
74      <MultiStateSelector
75        selectable_items={props.statuses.clone()}
76        on_selected={props.on_statuses.clone()}
77      />
78    )
79  } else {
80    html!()
81  };
82
83  let consumer_provider_switch = if props.show_consumer_provider_switch {
84    html!(
85      <Switch
86        label="as Consumer"
87        label_off="as Provider"
88        onchange={props.on_switch_view_consumer.clone()}
89      />
90    )
91  } else {
92    html!()
93  };
94
95  html!(
96    <>
97      <Toolbar>
98        <ToolbarContent>
99          <ToolbarItem r#type={ToolbarItemType::BulkSelect}>
100            { consumer_provider_switch }
101            { statuses_selector }
102          </ToolbarItem>
103          <ToolbarItem r#type={ToolbarItemType::Pagination}>
104            <Pagination
105              offset={props.offset}
106              entries_per_page_choices={vec![5, 10, 25, 50, 100]}
107              selected_choice={props.limit}
108              onlimit={&props.on_limit}
109              onnavigation={&nav_callback}
110            />
111          </ToolbarItem>
112        </ToolbarContent>
113      </Toolbar>
114      <Table<Columns, UseTableData<Columns, MemoizedTableModel<ContractNegotiationItemRenderer>>>
115        mode={TableMode::Compact}
116        {header}
117        {entries}
118      />
119    </>
120  )
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
124enum Columns {
125  State,
126  ContractAgreementId,
127  CounterPartyId,
128  Protocol,
129  Kind,
130  Actions,
131}
132
133#[derive(Clone, Debug)]
134struct ContractNegotiationItemRenderer {
135  item: ContractNegotiationItem,
136  on_show_contract_negotiation: Callback<String>,
137}
138
139impl TableEntryRenderer<Columns> for ContractNegotiationItemRenderer {
140  fn render_cell(&self, context: CellContext<'_, Columns>) -> Cell {
141    match context.column {
142      Columns::State => {
143        let color = match self.item.state.as_str() {
144          "Finalized" => Color::Green,
145          "Terminated" => Color::Red,
146          _ => Color::Blue,
147        };
148
149        html! { <Label label={self.item.state.to_string()} {color} /> }
150      },
151      Columns::ContractAgreementId => html! { self.item.contract_agreement_id.to_string() },
152      Columns::CounterPartyId => html! { self.item.counter_party_id.to_string() },
153      Columns::Protocol => html! { self.item.protocol.to_string() },
154      Columns::Kind => html! { self.item.kind.to_string() },
155      Columns::Actions => {
156        let contract_negotiation_id = self.item.id.clone();
157        html! {
158          <Button
159            variant={ButtonVariant::Primary}
160            onclick={self.on_show_contract_negotiation.clone().reform(move |_| contract_negotiation_id.clone())}
161            icon={Icon::Eye}
162          >
163            { "Show" }
164          </Button>
165        }
166      },
167    }
168    .into()
169  }
170}