edc_web_ui/components/
list_contract_definitions.rs

1use crate::contexts::use_edc_connector_context;
2use edc_connector_client::types::contract_definition::ContractDefinition;
3use edc_connector_client::types::query::Query;
4use patternfly_yew::prelude::*;
5use std::rc::Rc;
6use yew::platform::spawn_local;
7use yew::prelude::*;
8use yew::suspense::use_future_with;
9
10#[function_component]
11pub fn ListContractDefinitions() -> Html {
12  let fallback = html!("Loading ...");
13
14  html!(
15    <Suspense {fallback}>
16      <ListContractDefinitionsInner />
17    </Suspense>
18  )
19}
20
21#[function_component]
22pub fn ListContractDefinitionsInner() -> HtmlResult {
23  let edc_connector_context = use_edc_connector_context();
24
25  let offset = use_state(|| 0usize);
26  let limit = use_state(|| 10usize);
27
28  let contract_definition_list = use_future_with(
29    (edc_connector_context, *limit, *offset),
30    |parameters| async move {
31      let (edc_connector_context, limit, offset) = &*parameters;
32
33      let query = Query::builder()
34        .limit(*limit as u32)
35        .offset(*offset as u32)
36        .build();
37
38      if let Some(client) = edc_connector_context.get_client() {
39        client.contract_definitions().query(query).await
40      } else {
41        Ok(vec![])
42      }
43    },
44  )?;
45
46  let contract_definition_list = &(*contract_definition_list);
47
48  let header = html_nested! {
49    <TableHeader<Columns>>
50      <TableColumn<Columns> label="ID" index={Columns::Id} />
51      <TableColumn<Columns> label="Access Policy ID" index={Columns::AccessPolicyId} />
52      <TableColumn<Columns> label="Contract Policy ID" index={Columns::ContractPolicyId} />
53      <TableColumn<Columns> label="" index={Columns::Actions} />
54    </TableHeader<Columns>>
55  };
56
57  let limit_callback = use_callback(limit.clone(), |number, limit| limit.set(number));
58
59  let total_entries: Option<usize> = None;
60
61  let nav_callback = use_callback(
62    (offset.clone(), *limit, total_entries),
63    |page: Navigation, (offset, limit, total_entries)| {
64      let o = match page {
65        Navigation::First => 0,
66        Navigation::Last => (total_entries.unwrap_or_default().saturating_sub(1) / limit) * limit,
67        Navigation::Previous => **offset - limit,
68        Navigation::Next => **offset + limit,
69        Navigation::Page(n) => n * limit,
70      };
71      offset.set(o);
72    },
73  );
74
75  let rows = contract_definition_list
76    .as_ref()
77    .unwrap()
78    .iter()
79    .map(|contract_definition| ContractDefinitionRenderer(contract_definition.clone()))
80    .collect();
81
82  let (entries, _) = use_table_data(MemoizedTableModel::new(Rc::new(rows)));
83
84  let table = html!(
85    <>
86      <Toolbar>
87        <ToolbarContent>
88          <ToolbarItem r#type={ToolbarItemType::Pagination}>
89            <Pagination
90              offset={*offset}
91              entries_per_page_choices={vec![5, 10, 25, 50, 100]}
92              selected_choice={*limit}
93              onlimit={&limit_callback}
94              onnavigation={&nav_callback}
95            />
96          </ToolbarItem>
97        </ToolbarContent>
98      </Toolbar>
99      <Table<Columns, UseTableData<Columns, MemoizedTableModel<ContractDefinitionRenderer>>>
100        mode={TableMode::Compact}
101        {header}
102        {entries}
103        />
104    </>
105  );
106
107  Ok(table)
108}
109
110#[derive(Clone, Debug, Eq, PartialEq)]
111enum Columns {
112  Id,
113  AccessPolicyId,
114  ContractPolicyId,
115  Actions,
116}
117
118#[derive(Clone, Debug)]
119struct ContractDefinitionRenderer(ContractDefinition);
120
121impl ContractDefinitionRenderer {}
122
123impl TableEntryRenderer<Columns> for ContractDefinitionRenderer {
124  fn render_cell(&self, context: CellContext<'_, Columns>) -> Cell {
125    match context.column {
126      Columns::Id => html! {self.0.id().to_string()},
127      Columns::AccessPolicyId => html!(self.0.access_policy_id()),
128      Columns::ContractPolicyId => html!(self.0.contract_policy_id()),
129      Columns::Actions => {
130        let contract_definition_id = self.0.id().to_string();
131
132        html!(
133          <DeleteContractDefinition {contract_definition_id} />
134        )
135      }
136    }
137    .into()
138  }
139}
140
141#[derive(Clone, PartialEq, Properties)]
142pub struct Props {
143  pub contract_definition_id: String,
144}
145
146#[function_component]
147pub fn DeleteContractDefinition(props: &Props) -> Html {
148  let edc_connector_context = use_edc_connector_context();
149
150  let onclick = use_callback(
151    (edc_connector_context, props.contract_definition_id.clone()),
152    move |_, (edc_connector_context, contract_definition_id)| {
153      let edc_connector_context = edc_connector_context.clone();
154      let contract_definition_id = contract_definition_id.to_string();
155
156      spawn_local(async move {
157        if let Some(client) = edc_connector_context.get_client() {
158          let _ = client
159            .contract_definitions()
160            .delete(&contract_definition_id)
161            .await;
162        }
163      });
164    },
165  );
166
167  html!(
168    <Button
169      variant={ButtonVariant::Danger}
170      icon={Icon::Trash}
171      {onclick}
172      >
173      {"Delete"}
174    </Button>
175  )
176}