mcai_workflow/components/edit_parameter/
mcai_field.rs

1use crate::{
2  EditBool, EditBoolMessage, EditExtended, EditExtendedMessage, EditIcon, EditIconMessage,
3  EditNumber, EditNumberMessage, EditRequirement, EditRequirementMessage, EditString,
4  EditStringArray, EditStringArrayMessage, EditStringMessage, IconEvent, SharedWorkflow,
5};
6use css_in_rust_next::Style;
7use mcai_models::{ParameterType, Workflow};
8use serde_json::{Map, Value};
9use std::ops::DerefMut;
10use yew::{html, Callback, Component, Context, Html, Properties};
11use yew_feather::{check_square::CheckSquare, edit::Edit, slash::Slash, square::Square};
12
13pub enum McaiFieldMessage {
14  ModalDisplay,
15  EditArrayString(EditStringArrayMessage),
16  EditBool(EditBoolMessage),
17  EditExtended(EditExtendedMessage),
18  EditNumber(EditNumberMessage),
19  EditRequirement(EditRequirementMessage),
20  EditString(EditStringMessage),
21  IconListEvent(EditIconMessage),
22}
23
24#[derive(PartialEq, Properties)]
25pub struct McaiFieldProperties {
26  pub kind: ParameterType,
27  pub step_id: Option<u32>,
28  pub field_name: String,
29  #[prop_or_default]
30  pub is_icon: bool,
31  pub workflow: SharedWorkflow,
32  pub event: Callback<()>,
33}
34
35pub struct McaiField {
36  modal_display: bool,
37  is_editable: bool,
38  style: Style,
39}
40
41impl Component for McaiField {
42  type Message = McaiFieldMessage;
43  type Properties = McaiFieldProperties;
44
45  fn create(ctx: &Context<Self>) -> Self {
46    let is_editable = ctx.props().workflow.lock().unwrap().is_definition();
47    let style = Style::create("Component", include_str!("mcai_field.css")).unwrap();
48
49    McaiField {
50      modal_display: false,
51      is_editable,
52      style,
53    }
54  }
55
56  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
57    match msg {
58      McaiFieldMessage::ModalDisplay => {
59        self.modal_display = true;
60        true
61      }
62      McaiFieldMessage::EditString(message) => {
63        if let EditStringMessage::Submit(value) = message {
64          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
65          {
66            if let Some(step_id) = ctx.props().step_id {
67              let is_parameter = ctx.props().field_name != "name";
68              definition.set_parameter_on_step(
69                step_id,
70                &ctx.props().field_name,
71                ParameterType::String {
72                  value,
73                  default: None,
74                  required: true,
75                },
76                is_parameter,
77              );
78            } else {
79              definition.set_workflow_property(
80                &ctx.props().field_name,
81                ParameterType::String {
82                  value,
83                  default: None,
84                  required: true,
85                },
86              );
87            }
88          }
89        }
90
91        ctx.props().event.emit(());
92        self.modal_display = false;
93        true
94      }
95      McaiFieldMessage::EditExtended(message) => {
96        if let EditExtendedMessage::Submit(value) = message {
97          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
98          {
99            if let Some(step_id) = ctx.props().step_id {
100              let is_parameter = ctx.props().field_name != "name";
101              definition.set_parameter_on_step(
102                step_id,
103                &ctx.props().field_name,
104                ParameterType::Extended {
105                  value,
106                  default: Value::Object(Map::default()),
107                  required: true,
108                },
109                is_parameter,
110              );
111            } else {
112              definition.set_workflow_property(
113                &ctx.props().field_name,
114                ParameterType::Extended {
115                  value,
116                  default: Value::Object(Map::default()),
117                  required: true,
118                },
119              );
120            }
121          }
122        }
123
124        ctx.props().event.emit(());
125        self.modal_display = false;
126        true
127      }
128      McaiFieldMessage::IconListEvent(message) => {
129        if let EditIconMessage::Selected(value) = message {
130          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
131          {
132            let IconEvent::SelectedIcon(icon_name) = value;
133            if let Some(step_id) = ctx.props().step_id {
134              definition.set_parameter_on_step(
135                step_id,
136                &ctx.props().field_name,
137                ParameterType::String {
138                  value: Some(icon_name),
139                  default: None,
140                  required: true,
141                },
142                false,
143              );
144            } else {
145              definition.set_workflow_property(
146                &ctx.props().field_name,
147                ParameterType::String {
148                  value: Some(icon_name),
149                  default: None,
150                  required: true,
151                },
152              );
153            }
154          }
155
156          ctx.props().event.emit(());
157        }
158        self.modal_display = false;
159        true
160      }
161      McaiFieldMessage::EditArrayString(message) => {
162        if let EditStringArrayMessage::Submit(value) = message {
163          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
164          {
165            if let Some(step_id) = ctx.props().step_id {
166              let is_parameter = ctx.props().field_name != "name";
167              definition.set_parameter_on_step(
168                step_id,
169                &ctx.props().field_name,
170                ParameterType::ArrayOfStrings {
171                  value,
172                  default: vec![],
173                  required: true,
174                },
175                is_parameter,
176              );
177            } else {
178              definition.set_workflow_property(
179                &ctx.props().field_name,
180                ParameterType::ArrayOfStrings {
181                  value,
182                  default: vec![],
183                  required: true,
184                },
185              );
186            }
187          }
188        }
189
190        ctx.props().event.emit(());
191        self.modal_display = false;
192        true
193      }
194      McaiFieldMessage::EditNumber(message) => {
195        if let EditNumberMessage::Submit(value) = message {
196          let new_value = match ctx.props().kind {
197            ParameterType::Number { .. } => ParameterType::Number {
198              default: None,
199              value,
200              required: true,
201            },
202            ParameterType::Integer { .. } => ParameterType::Integer {
203              default: None,
204              value: value.map(|v| v as u32),
205              required: true,
206            },
207            _ => unreachable!(),
208          };
209
210          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
211          {
212            if let Some(step_id) = ctx.props().step_id {
213              let is_parameter = ctx.props().field_name != "name";
214              definition.set_parameter_on_step(
215                step_id,
216                &ctx.props().field_name,
217                new_value,
218                is_parameter,
219              );
220            } else {
221              definition.set_workflow_property(&ctx.props().field_name, new_value);
222            }
223          }
224        }
225
226        ctx.props().event.emit(());
227        self.modal_display = false;
228        true
229      }
230      McaiFieldMessage::EditBool(message) => {
231        if let EditBoolMessage::Submit(value) = message {
232          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
233          {
234            if let Some(step_id) = ctx.props().step_id {
235              let is_parameter = ctx.props().field_name != "name";
236              definition.set_parameter_on_step(
237                step_id,
238                &ctx.props().field_name,
239                ParameterType::Boolean {
240                  default: None,
241                  value,
242                  required: true,
243                },
244                is_parameter,
245              );
246            } else {
247              definition.set_workflow_property(
248                &ctx.props().field_name,
249                ParameterType::Boolean {
250                  default: None,
251                  value,
252                  required: true,
253                },
254              );
255            }
256          }
257        }
258
259        ctx.props().event.emit(());
260        self.modal_display = false;
261        true
262      }
263      McaiFieldMessage::EditRequirement(message) => {
264        if let EditRequirementMessage::Submit(value) = message {
265          if let Workflow::Definition(definition) = ctx.props().workflow.lock().unwrap().deref_mut()
266          {
267            if let Some(step_id) = ctx.props().step_id {
268              definition.set_parameter_on_step(
269                step_id,
270                &ctx.props().field_name,
271                ParameterType::Requirements {
272                  default: None,
273                  value,
274                  required: true,
275                },
276                true,
277              );
278            }
279          }
280        }
281        ctx.props().event.emit(());
282        self.modal_display = false;
283        true
284      }
285    }
286  }
287
288  fn view(&self, ctx: &Context<Self>) -> Html {
289    let modal = self
290      .modal_display
291      .then(|| {
292        let title = format!("Edit {}", ctx.props().field_name);
293
294        match &ctx.props().kind {
295          ParameterType::ArrayOfStrings { value, .. }
296          | ParameterType::ArrayOfTemplates { value, .. } => html!(
297            <EditStringArray
298              field_name={ctx.props().field_name.clone()}
299              event={ctx.link().callback(McaiFieldMessage::EditArrayString)}
300              title={title}
301              value={value.clone()}
302              />
303          ),
304          ParameterType::Boolean {
305            value, required, ..
306          } => html!(
307            <EditBool
308              field_name={ctx.props().field_name.clone()}
309              event={ctx.link().callback(McaiFieldMessage::EditBool)}
310              title={title}
311              value={*value}
312              required={*required}
313              />
314          ),
315          ParameterType::Integer {
316            value, required, ..
317          } => html!(
318            <EditNumber
319              field_name={ctx.props().field_name.clone()}
320              event={ctx.link().callback(McaiFieldMessage::EditNumber)}
321              title={title}
322              value={value.map(|v| v as f64)}
323              number_step={1.0}
324              required={*required}
325              />
326          ),
327          ParameterType::Number {
328            value, required, ..
329          } => html!(
330            <EditNumber
331              field_name={ctx.props().field_name.clone()}
332              event={ctx.link().callback(McaiFieldMessage::EditNumber)}
333              title={title}
334              value={*value}
335              number_step={0.00000000001}
336              required={*required}
337              />
338          ),
339          ParameterType::Requirements { value, .. } => html!(
340            <EditRequirement
341              field_name={ctx.props().field_name.clone()}
342              event={ctx.link().callback(McaiFieldMessage::EditRequirement)}
343              title={title}
344              requirement={value.clone()}
345              />
346          ),
347          ParameterType::String {
348            value, required, ..
349          }
350          | ParameterType::Template {
351            value, required, ..
352          } => {
353            if ctx.props().is_icon {
354              html!(
355                <EditIcon
356                  event={ctx.link().callback(McaiFieldMessage::IconListEvent)}
357                  title={title}
358                  />
359              )
360            } else {
361              html!(
362                <EditString
363                  event={ctx.link().callback(McaiFieldMessage::EditString)}
364                  field_name={ctx.props().field_name.clone()}
365                  title={title}
366                  value={value.clone()}
367                  required={*required}
368                  />
369              )
370            }
371          }
372          ParameterType::Extended {
373            value, required, ..
374          } => {
375            html!(
376              <EditExtended
377                event={ctx.link().callback(McaiFieldMessage::EditExtended)}
378                field_name={ctx.props().field_name.clone()}
379                title={title}
380                value={value.clone()}
381                required={*required}
382                />
383            )
384          }
385          _ => html!(),
386        }
387      })
388      .unwrap_or_default();
389
390    let display_value = {
391      match &ctx.props().kind {
392        ParameterType::ArrayOfStrings { value, .. }
393        | ParameterType::ArrayOfTemplates { value, .. } => value
394          .iter()
395          .map(|value| {
396            html!(
397              <div>
398                {value}
399              </div>
400            )
401          })
402          .collect::<Html>(),
403        ParameterType::Boolean {
404          value, required, ..
405        } => {
406          let inner = match if *required {
407            Some(value.unwrap_or_default())
408          } else {
409            value.map(|value| value)
410          } {
411            Some(true) => html!(<CheckSquare />),
412            Some(false) => html!(<Square />),
413            None => html!(<Slash color={"#888"} />),
414          };
415
416          html!(<div>{inner}</div>)
417        }
418        ParameterType::Integer {
419          value, required, ..
420        } => value
421          .map(|value| html!(value.to_string()))
422          .unwrap_or_else(|| {
423            if *required {
424              html!(0)
425            } else {
426              html!(<Slash color={"#888"} />)
427            }
428          }),
429        ParameterType::Number {
430          value, required, ..
431        } => value
432          .map(|value| html!(value.to_string()))
433          .unwrap_or_else(|| {
434            if *required {
435              html!(0f64)
436            } else {
437              html!(<Slash color={"#888"} />)
438            }
439          }),
440        ParameterType::Requirements {
441          value, required, ..
442        } => value
443          .as_ref()
444          .map(|value| html!(<>{value.paths.len()}{" path(s)"}</>))
445          .unwrap_or_else(|| {
446            if *required {
447              html!("")
448            } else {
449              html!(<Slash color={"#888"} />)
450            }
451          }),
452        ParameterType::String {
453          value, required, ..
454        }
455        | ParameterType::Template {
456          value, required, ..
457        } => {
458          if ctx.props().is_icon {
459            html!(<i class="material-icons">{value.clone().unwrap_or_default()}</i>)
460          } else {
461            value.as_ref().map(|value| html!(value)).unwrap_or_else(|| {
462              (!required)
463                .then(|| html!(<Slash color={"#888"} />))
464                .unwrap_or_default()
465            })
466          }
467        }
468        ParameterType::Extended {
469          value, required, ..
470        } => {
471          if matches!(value, Value::Null) {
472            if *required {
473              html!("")
474            } else {
475              html!(<Slash color={"#888"} />)
476            }
477          } else {
478            html!(value)
479          }
480        }
481        _ => html!(),
482      }
483    };
484
485    if self.is_editable {
486      html!(
487        <span class={self.style.clone()} onclick={ctx.link().callback(|_|McaiFieldMessage::ModalDisplay)}>
488          <span class="value">
489            {display_value}
490          </span>
491          <Edit class="fieldIcon" />
492          {modal}
493        </span>
494      )
495    } else {
496      html!(
497        <span class={self.style.clone()}>
498          <span class="value">
499            {display_value}
500          </span>
501        </span>
502      )
503    }
504  }
505}