273 lines
8.8 KiB
Rust
273 lines
8.8 KiB
Rust
use crate::{
|
|
components::{
|
|
form::{
|
|
attribute_input::{ListAttributeInput, SingleAttributeInput},
|
|
static_value::StaticValue,
|
|
submit::Submit,
|
|
},
|
|
group_details::{Attribute, AttributeSchema, Group},
|
|
},
|
|
infra::{
|
|
common_component::{CommonComponent, CommonComponentParts},
|
|
form_utils::{read_all_form_attributes, AttributeValue, EmailIsRequired, IsAdmin},
|
|
schema::AttributeType,
|
|
},
|
|
};
|
|
use anyhow::{Ok, Result};
|
|
use graphql_client::GraphQLQuery;
|
|
use yew::prelude::*;
|
|
|
|
/// The GraphQL query sent to the server to update the group details.
|
|
#[derive(GraphQLQuery)]
|
|
#[graphql(
|
|
schema_path = "../schema.graphql",
|
|
query_path = "queries/update_group.graphql",
|
|
response_derives = "Debug",
|
|
variables_derives = "Clone,PartialEq,Eq",
|
|
custom_scalars_module = "crate::infra::graphql"
|
|
)]
|
|
pub struct UpdateGroup;
|
|
|
|
/// A [yew::Component] to display the group details, with a form allowing to edit them.
|
|
pub struct GroupDetailsForm {
|
|
common: CommonComponentParts<Self>,
|
|
/// True if we just successfully updated the group, to display a success message.
|
|
just_updated: bool,
|
|
updated_group_name: bool,
|
|
group: Group,
|
|
form_ref: NodeRef,
|
|
}
|
|
|
|
pub enum Msg {
|
|
/// A form field changed.
|
|
Update,
|
|
/// The "Submit" button was clicked.
|
|
SubmitClicked,
|
|
/// We got the response from the server about our update message.
|
|
GroupUpdated(Result<update_group::ResponseData>),
|
|
}
|
|
|
|
#[derive(yew::Properties, Clone, PartialEq)]
|
|
pub struct Props {
|
|
/// The current group details.
|
|
pub group: Group,
|
|
pub group_attributes_schema: Vec<AttributeSchema>,
|
|
pub is_admin: bool,
|
|
pub on_display_name_updated: Callback<()>,
|
|
}
|
|
|
|
impl CommonComponent<GroupDetailsForm> for GroupDetailsForm {
|
|
fn handle_msg(
|
|
&mut self,
|
|
ctx: &Context<Self>,
|
|
msg: <Self as Component>::Message,
|
|
) -> Result<bool> {
|
|
match msg {
|
|
Msg::Update => Ok(true),
|
|
Msg::SubmitClicked => self.submit_group_update_form(ctx),
|
|
Msg::GroupUpdated(Err(e)) => Err(e),
|
|
Msg::GroupUpdated(Result::Ok(_)) => {
|
|
self.just_updated = true;
|
|
if self.updated_group_name {
|
|
self.updated_group_name = false;
|
|
ctx.props().on_display_name_updated.emit(());
|
|
}
|
|
Ok(true)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn mut_common(&mut self) -> &mut CommonComponentParts<Self> {
|
|
&mut self.common
|
|
}
|
|
}
|
|
|
|
impl Component for GroupDetailsForm {
|
|
type Message = Msg;
|
|
type Properties = Props;
|
|
|
|
fn create(ctx: &Context<Self>) -> Self {
|
|
Self {
|
|
common: CommonComponentParts::<Self>::create(),
|
|
just_updated: false,
|
|
updated_group_name: false,
|
|
group: ctx.props().group.clone(),
|
|
form_ref: NodeRef::default(),
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
self.just_updated = false;
|
|
CommonComponentParts::<Self>::update(self, ctx, msg)
|
|
}
|
|
|
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
let link = &ctx.link();
|
|
|
|
let can_edit =
|
|
|a: &AttributeSchema| (ctx.props().is_admin || a.is_editable) && !a.is_readonly;
|
|
let display_field = |a: &AttributeSchema| {
|
|
if can_edit(a) {
|
|
get_custom_attribute_input(a, &self.group.attributes)
|
|
} else {
|
|
get_custom_attribute_static(a, &self.group.attributes)
|
|
}
|
|
};
|
|
html! {
|
|
<div class="py-3">
|
|
<form
|
|
class="form"
|
|
ref={self.form_ref.clone()}>
|
|
<StaticValue label="Group ID" id="groupId">
|
|
<i>{&self.group.id}</i>
|
|
</StaticValue>
|
|
{
|
|
ctx
|
|
.props()
|
|
.group_attributes_schema
|
|
.iter()
|
|
.filter(|a| a.is_hardcoded && a.name != "group_id")
|
|
.map(display_field)
|
|
.collect::<Vec<_>>()
|
|
}
|
|
{
|
|
ctx
|
|
.props()
|
|
.group_attributes_schema
|
|
.iter()
|
|
.filter(|a| !a.is_hardcoded)
|
|
.map(display_field)
|
|
.collect::<Vec<_>>()
|
|
}
|
|
<Submit
|
|
text="Save changes"
|
|
disabled={self.common.is_task_running()}
|
|
onclick={link.callback(|e: MouseEvent| {e.prevent_default(); Msg::SubmitClicked})} />
|
|
</form>
|
|
{
|
|
if let Some(e) = &self.common.error {
|
|
html! {
|
|
<div class="alert alert-danger">
|
|
{e.to_string() }
|
|
</div>
|
|
}
|
|
} else { html! {} }
|
|
}
|
|
<div hidden={!self.just_updated}>
|
|
<div class="alert alert-success mt-4">{"Group successfully updated!"}</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_custom_attribute_input(
|
|
attribute_schema: &AttributeSchema,
|
|
group_attributes: &[Attribute],
|
|
) -> Html {
|
|
let values = group_attributes
|
|
.iter()
|
|
.find(|a| a.name == attribute_schema.name)
|
|
.map(|attribute| attribute.value.clone())
|
|
.unwrap_or_default();
|
|
if attribute_schema.is_list {
|
|
html! {
|
|
<ListAttributeInput
|
|
name={attribute_schema.name.clone()}
|
|
attribute_type={Into::<AttributeType>::into(attribute_schema.attribute_type.clone())}
|
|
values={values}
|
|
/>
|
|
}
|
|
} else {
|
|
html! {
|
|
<SingleAttributeInput
|
|
name={attribute_schema.name.clone()}
|
|
attribute_type={Into::<AttributeType>::into(attribute_schema.attribute_type.clone())}
|
|
value={values.first().cloned().unwrap_or_default()}
|
|
/>
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_custom_attribute_static(
|
|
attribute_schema: &AttributeSchema,
|
|
group_attributes: &[Attribute],
|
|
) -> Html {
|
|
let values = group_attributes
|
|
.iter()
|
|
.find(|a| a.name == attribute_schema.name)
|
|
.map(|attribute| attribute.value.clone())
|
|
.unwrap_or_default();
|
|
html! {
|
|
<StaticValue label={attribute_schema.name.clone()} id={attribute_schema.name.clone()}>
|
|
{values.into_iter().map(|x| html!{<div>{x}</div>}).collect::<Vec<_>>()}
|
|
</StaticValue>
|
|
}
|
|
}
|
|
|
|
impl GroupDetailsForm {
|
|
fn submit_group_update_form(&mut self, ctx: &Context<Self>) -> Result<bool> {
|
|
let mut all_values = read_all_form_attributes(
|
|
ctx.props().group_attributes_schema.iter(),
|
|
&self.form_ref,
|
|
IsAdmin(ctx.props().is_admin),
|
|
EmailIsRequired(false),
|
|
)?;
|
|
let base_attributes = &self.group.attributes;
|
|
all_values.retain(|a| {
|
|
let base_val = base_attributes
|
|
.iter()
|
|
.find(|base_val| base_val.name == a.name);
|
|
base_val
|
|
.map(|v| v.value != a.values)
|
|
.unwrap_or(!a.values.is_empty())
|
|
});
|
|
if all_values.iter().any(|a| a.name == "display_name") {
|
|
self.updated_group_name = true;
|
|
}
|
|
let remove_attributes: Option<Vec<String>> = if all_values.is_empty() {
|
|
None
|
|
} else {
|
|
Some(all_values.iter().map(|a| a.name.clone()).collect())
|
|
};
|
|
let insert_attributes: Option<Vec<update_group::AttributeValueInput>> =
|
|
if remove_attributes.is_none() {
|
|
None
|
|
} else {
|
|
Some(
|
|
all_values
|
|
.into_iter()
|
|
.filter(|a| !a.values.is_empty())
|
|
.map(
|
|
|AttributeValue { name, values }| update_group::AttributeValueInput {
|
|
name,
|
|
value: values,
|
|
},
|
|
)
|
|
.collect(),
|
|
)
|
|
};
|
|
let mut group_input = update_group::UpdateGroupInput {
|
|
id: self.group.id,
|
|
displayName: None,
|
|
removeAttributes: None,
|
|
insertAttributes: None,
|
|
};
|
|
let default_group_input = group_input.clone();
|
|
group_input.removeAttributes = remove_attributes;
|
|
group_input.insertAttributes = insert_attributes;
|
|
// Nothing changed.
|
|
if group_input == default_group_input {
|
|
return Ok(false);
|
|
}
|
|
let req = update_group::Variables { group: group_input };
|
|
self.common.call_graphql::<UpdateGroup, _>(
|
|
ctx,
|
|
req,
|
|
Msg::GroupUpdated,
|
|
"Error trying to update group",
|
|
);
|
|
Ok(false)
|
|
}
|
|
}
|