edc-web-ui 0.2.0

A Rust Web interface with patternfly-yew client for EDC
Documentation
use crate::contexts::use_edc_connector_context;
use crate::models::PolicyDefinitionItem;
use edc_connector_client::types::query::Query;
use patternfly_yew::prelude::*;
use yew::prelude::*;
use yew::suspense::use_future_with;

#[derive(Clone, Debug, PartialEq, Properties)]
pub struct PolicySelectorProps {
  pub selected_policy: Option<PolicyDefinitionItem>,
  pub onselect: Callback<PolicyDefinitionItem>,
  #[prop_or("selectable-policy".to_string())]
  pub select_id: String,
}

#[component]
pub fn PolicySelector(props: &PolicySelectorProps) -> Html {
  html!(
    <Suspense>
      <PolicySelectorInner
        onselect={props.onselect.clone()}
        selected_policy={props.selected_policy.clone()}
        select_id={props.select_id.clone()}
      />
    </Suspense>
  )
}

#[derive(Clone, Debug, PartialEq, Properties)]
pub struct PolicySelectorInnerProps {
  pub selected_policy: Option<PolicyDefinitionItem>,
  pub onselect: Callback<PolicyDefinitionItem>,
  pub select_id: String,
}

#[component]
fn PolicySelectorInner(props: &PolicySelectorInnerProps) -> HtmlResult {
  let limit = use_state(|| 10usize);
  let offset = use_state(|| 0usize);
  let force_refresh = use_state(|| 0usize);
  let edc_connector_context = use_edc_connector_context();

  let policies = use_future_with(
    (edc_connector_context, *limit, *offset, *force_refresh),
    |parameters| async move {
      let (edc_connector_context, limit, offset, _) = (*parameters).clone();

      let query = Query::builder()
        .limit(limit as u32)
        .offset(offset as u32)
        .build();

      if let Some(client) = edc_connector_context.get_client() {
        client
          .policies()
          .query(query)
          .await
          .unwrap_or_default()
          .into_iter()
          .map(PolicyDefinitionItem::from)
          .collect::<Vec<_>>()
      } else {
        vec![]
      }
    },
  )?;

  let policies = (*policies).clone();

  let items = policies.iter().map(|policy_definition_item| {
    let policy_definition_item_id = policy_definition_item.id.to_string();
    let onselect = props.onselect.clone();
    let policy_definition_item = policy_definition_item.clone();

    let selected = if let Some(selected_policy) = &props.selected_policy {
      selected_policy.id == policy_definition_item.id
    } else {
      false
    };

    let selectable_actions = yew::props!(CardSelectableActionsObjectProperties {
      action: CardSelectableActionsVariant::SingleSelect {
        onchange: Some(onselect.reform(move |_| policy_definition_item.clone()))
      },
      base: yew::props!(CardSelectableActionsObjectBase {
        name: props.select_id.clone(),
      })
    });

    html!(
      <Card selectable=true {selected}>
        <CardHeader {selectable_actions}>{ policy_definition_item_id }</CardHeader>
      </Card>
    )
  });

  let onlimit = use_callback(limit.setter(), |limit, limit_setter| {
    limit_setter.set(limit)
  });

  let total_entries = Option::<usize>::None;

  let nav_callback = use_callback(
    (offset.clone(), *limit, total_entries),
    |page: Navigation, (offset, limit, total_entries)| {
      let new_offset = match page {
        Navigation::First => 0,
        Navigation::Last => (total_entries.unwrap_or_default().saturating_sub(1) / limit) * limit,
        Navigation::Previous => **offset - limit,
        Navigation::Next => **offset + limit,
        Navigation::Page(n) => n * limit,
      };
      offset.set(new_offset);
    },
  );

  Ok(html!(
    <Stack gutter=true>
      <StackItem>
        <Pagination
          offset={*offset}
          entries_per_page_choices={vec![5, 10, 25, 50, 100]}
          selected_choice={*limit}
          onlimit={&onlimit}
          onnavigation={&nav_callback}
        />
      </StackItem>
      <StackItem>
        <Gallery gutter=true>{ for items }</Gallery>
      </StackItem>
    </Stack>
  ))
}