mcai_workflow/components/
edit_notification_hook.rs1use crate::{ActionButton, Button, Field, Modal, ModalMessage};
2use css_in_rust_next::Style;
3use mcai_models::{NotificationHook, NotificationHookCondition};
4use wasm_bindgen::JsCast;
5use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement, InputEvent};
6use yew::{html, Callback, Component, Context, Html, Properties};
7use yew_feather::{plus::Plus, trash_2::Trash2};
8
9#[derive(PartialEq, Properties)]
10pub struct EditNotificationHookProperties {
11 pub event: Callback<EditNotificationHookMessage>,
12 pub notification_hook: Option<NotificationHook>,
13}
14
15pub enum EditNotificationHookMessage {
16 Submit(NotificationHook),
17 Cancel,
18 Delete,
19}
20
21pub enum FieldId {
22 Label,
23 Endpoint,
24}
25
26pub enum InternalMessage {
27 Update(FieldId, String),
28 UpdateCredential(Option<String>),
29 UpdateConditions(Vec<NotificationHookCondition>),
30 AddCredential,
31 RemoveCredential,
32 Modal(ModalMessage),
33}
34
35pub struct EditNotificationHook {
36 style: Style,
37 label: String,
38 endpoint: String,
39 credentials: Option<String>,
40 conditions: Vec<NotificationHookCondition>,
41}
42
43impl EditNotificationHook {
44 fn is_valid(&self) -> bool {
45 !self.label.is_empty() && !self.endpoint.is_empty() && !self.conditions.is_empty()
46 }
47}
48
49impl Component for EditNotificationHook {
50 type Message = InternalMessage;
51 type Properties = EditNotificationHookProperties;
52
53 fn create(ctx: &Context<Self>) -> Self {
54 let style = Style::create(
55 "Component",
56 concat!(include_str!("edit_notification_hook.css")),
57 )
58 .unwrap();
59
60 if let Some(notification_hook) = &ctx.props().notification_hook {
61 Self {
62 style,
63 label: notification_hook.label.clone(),
64 endpoint: notification_hook.endpoint.clone(),
65 credentials: notification_hook.credentials.clone(),
66 conditions: notification_hook.conditions.clone(),
67 }
68 } else {
69 Self {
70 style,
71 label: String::new(),
72 endpoint: String::new(),
73 credentials: None,
74 conditions: vec![],
75 }
76 }
77 }
78
79 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
80 match msg {
81 InternalMessage::Update(field, value) => match field {
82 FieldId::Label => self.label = value,
83 FieldId::Endpoint => self.endpoint = value,
84 },
85 InternalMessage::UpdateCredential(value) => self.credentials = value,
86 InternalMessage::AddCredential => self.credentials = Some(String::new()),
87 InternalMessage::RemoveCredential => self.credentials = None,
88 InternalMessage::UpdateConditions(conditions) => {
89 self.conditions = conditions;
90 }
91 InternalMessage::Modal(message) => match message {
92 ModalMessage::Submit | ModalMessage::Update => {
93 let start_parameter = NotificationHook {
94 label: self.label.clone(),
95 endpoint: self.endpoint.clone(),
96 credentials: self.credentials.clone(),
97 conditions: self.conditions.clone(),
98 };
99
100 ctx
101 .props()
102 .event
103 .emit(EditNotificationHookMessage::Submit(start_parameter));
104 }
105 ModalMessage::Cancel => {
106 ctx.props().event.emit(EditNotificationHookMessage::Cancel);
107 }
108 ModalMessage::Delete => {
109 ctx.props().event.emit(EditNotificationHookMessage::Delete);
110 }
111 },
112 }
113 true
114 }
115
116 fn view(&self, ctx: &Context<Self>) -> Html {
117 let action_buttons = if ctx.props().notification_hook.is_some() {
118 vec![ActionButton::Delete, ActionButton::Update(self.is_valid())]
119 } else {
120 vec![ActionButton::Submit(self.is_valid())]
121 };
122
123 let callback_label = ctx.link().batch_callback(|e: InputEvent| {
124 let target: Option<EventTarget> = e.target();
125 let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
126 input.map(|input| InternalMessage::Update(FieldId::Label, input.value()))
127 });
128
129 let callback_endpoint = ctx.link().batch_callback(|e: InputEvent| {
130 let target: Option<EventTarget> = e.target();
131 let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
132 input.map(|input| InternalMessage::Update(FieldId::Endpoint, input.value()))
133 });
134
135 let callback_credential = ctx.link().batch_callback(|e: InputEvent| {
136 let target: Option<EventTarget> = e.target();
137 let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
138 input.map(|input| InternalMessage::UpdateCredential(Some(input.value())))
139 });
140
141 let credential_inner = self.credentials.as_ref().map(|credential| html!(
142 <>
143 <input type="text" value={credential.clone()} oninput={callback_credential} />
144 <Button label="" icon={html!(<Trash2 />)} onclick={ctx.link().callback(|_| InternalMessage::RemoveCredential)}/>
145 </>
146 )).unwrap_or_else(|| html!(
147 <Button label="Add value" icon={html!(<Plus />)} onclick={ctx.link().callback(|_| InternalMessage::AddCredential)}/>
148 ));
149
150 let callback_conditions = ctx.link().batch_callback(|e: Event| {
151 let target: Option<EventTarget> = e.target();
152 target
153 .and_then(|t| t.dyn_into::<HtmlSelectElement>().ok())
154 .map(|input| {
155 let list = input.selected_options();
156 let mut result = vec![];
157
158 for index in 0..list.length() {
159 if let Some(element) = list.item(index) {
160 match element.get_attribute("value").as_deref() {
161 Some("job_completed") => result.push(NotificationHookCondition::JobCompleted),
162 Some("job_error") => result.push(NotificationHookCondition::JobError),
163 Some("workflow_completed") => {
164 result.push(NotificationHookCondition::WorkflowCompleted)
165 }
166 Some("workflow_error") => result.push(NotificationHookCondition::WorkflowError),
167 _ => {}
168 }
169 }
170 }
171
172 result
173 })
174 .map(InternalMessage::UpdateConditions)
175 });
176
177 let input_field = html!(
178 <>
179 <Field label="Label"><input type="text" value={self.label.clone()} oninput={callback_label} /></Field>
180 <Field label="Endpoint"><input type="text" value={self.endpoint.clone()} oninput={callback_endpoint} /></Field>
181 <Field label="Credentials">{credential_inner}</Field>
182 <Field label="Condition(s)">
183 <select onchange={callback_conditions} multiple=true>
184 <option value={"job_completed"} selected={self.conditions.contains(&NotificationHookCondition::JobCompleted)}>{"Job completed"}</option>
185 <option value={"job_error"} selected={self.conditions.contains(&NotificationHookCondition::JobError)}>{"Job error"}</option>
186 <option value={"workflow_completed"} selected={self.conditions.contains(&NotificationHookCondition::WorkflowCompleted)}>{"Workflow completed"}</option>
187 <option value={"workflow_error"} selected={self.conditions.contains(&NotificationHookCondition::WorkflowError)}>{"Workflow error"}</option>
188 </select>
189 </Field>
190 </>
191 );
192
193 html!(
194 <Modal
195 event={ctx.link().callback(InternalMessage::Modal)}
196 height="50vh" width="19vw"
197 modal_title={"Edit Notification Hook"}
198 actions={action_buttons}>
199 <div class={self.style.clone()}>
200 <div class="editNotificationHook">
201 {input_field}
202 </div>
203 </div>
204 </Modal>
205 )
206 }
207}