diff --git a/advotracker/src/data/constants.rs b/advotracker/src/data/constants.rs index b66d48c..4bf2adf 100644 --- a/advotracker/src/data/constants.rs +++ b/advotracker/src/data/constants.rs @@ -10,10 +10,14 @@ pub static PROP_ADVOTRACKER: &str = "advotracker"; pub static PROP_MAIL_CC_1: &str = "advotracker@hiedemann.de"; pub static PROP_MAIL_CC_2: &str = "service@hiedemann.de"; +pub static PROP_MAIL_BCC_1: &str = "schloemer@hiedemann.de"; +pub static PROP_MAIL_BCC_2: &str = "support@networkx.de"; pub static PROP_MAIL_TO_1: &str = "allianz@ponschab-partner.com"; pub static PROP_MAIL_TO_2: &str = "kontakt@chevalier.law"; pub static PROP_MAIL_TO_3: &str = "kontakt@metamedlaw.de"; pub static PROP_MAIL_TO_4: &str = "sekretariat@m2-mediation.de"; +pub static PROP_MAIL_FROM: &str = "advotracker@heidemann.de"; +pub static PROP_MAIL_SUBJECT: &str = "ZMB Allianz - neues Mandat"; pub static PROP_POLICY_CHECK: &str = "policy_check"; pub static PROP_POLICY_PROGRESS_COUNT: &str = "policy_progress_count"; @@ -91,6 +95,7 @@ pub static ID_POLICY_CHECK_LABEL_HINT: &str = "policy_check_label_hint"; pub static ID_POLICY_CHECK_LABEL_MENU: &str = "policy_check_label_menu"; pub static ID_POLICY_CHECK_LABEL_POLICY_NUMBER: &str = "policy_check_label_policy_number"; pub static ID_POLICY_CHECK_LABEL_RESULT: &str = "policy_check_label_result"; +pub static ID_POLICY_CHECK_POLICY_CODE: &str = "policy_check_policy_code"; pub static ID_POLICY_CHECK_POLICY_NUMBER: &str = "policy_number"; pub static ID_POLICY_CHECK_POPUP_PROGRESS: &str = "policy_check_popup_progress"; pub static ID_POLICY_CHECK_PROGRESS_BAR: &str = "policy_check_progress_bar"; @@ -123,8 +128,6 @@ pub static ID_TICKET_DATA_BUTTON_MENU: &str = "ticket_data_button_menu"; pub static ID_TICKET_DATA_BUTTON_RESULT: &str = "ticket_data_button_result"; pub static ID_TICKET_DATA_CALLBACK_DATE: &str = "ticket_data_label_callback_date"; pub static ID_TICKET_DATA_CALLBACK_NUMBER: &str = "ticket_data_label_callback_number"; -pub static ID_TICKET_DATA_COMBO_BOX_MAIL_TO: &str = "ticket_data_combo_box_mail_to"; -pub static ID_TICKET_DATA_COMBO_BOX_MAIL_CC: &str = "ticket_data_combo_box_mail_cc"; pub static ID_TICKET_DATA_CONTAINER_MAIL: &str = "ticket_data_container_mail"; pub static ID_TICKET_DATA_COUNT_BLOCK: &str = "ticket_data_count_block"; pub static ID_TICKET_DATA_DEDUCTIBLE: &str = "ticket_data_deductible"; @@ -146,13 +149,13 @@ pub static ID_TICKET_DATA_LABEL_IVR_COMMENT: &str = "ticket_data_label_ivr_comme pub static ID_TICKET_DATA_LABEL_MAIL_CC: &str = "ticket_data_label_mail_cc"; pub static ID_TICKET_DATA_LABEL_MAIL_TO: &str = "ticket_data_label_mail_to"; pub static ID_TICKET_DATA_LABEL_MENU: &str = "ticket_data_label_menu"; +pub static ID_TICKET_DATA_LABEL_POLICY_CODE: &str = "ticket_data_label_policy_code"; pub static ID_TICKET_DATA_LABEL_POLICY_HOLDER: &str = "ticket_data_label_policy_holder"; -pub static ID_TICKET_DATA_LABEL_POLICY_NUMBER: &str = "ticket_data_label_policy_number"; pub static ID_TICKET_DATA_LOGO_BAR: &str = "ticket_data_logo_bar"; pub static ID_TICKET_DATA_MAIL_CC: &str = "ticket_data_mail_cc"; pub static ID_TICKET_DATA_MAIL_TO: &str = "ticket_data_mail_to"; +pub static ID_TICKET_DATA_POLICY_CODE: &str = "ticket_data_policy_code"; pub static ID_TICKET_DATA_POLICY_HOLDER: &str = "ticket_data_policy_holder"; -pub static ID_TICKET_DATA_POLICY_NUMBER: &str = "ticket_data_policy_number"; pub static ID_TICKET_DATA_RESULT: &str = "ticket_data_result"; pub static ID_TICKET_DATA_VIEW: &str = "ticket_data_view"; pub static ID_TICKET_DATA_WIDGET: &str = "ticket_data_widget"; diff --git a/advotracker/src/data/structures.rs b/advotracker/src/data/structures.rs index f136616..ca74de0 100644 --- a/advotracker/src/data/structures.rs +++ b/advotracker/src/data/structures.rs @@ -110,6 +110,37 @@ pub struct CsvExportRecord { pub ivr_comment: String, } +/// Handel fields of an Email (header, body) +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +pub struct Email { + /// recipient address + pub mail_to: String, + /// Carbon Copy recipient address + pub mail_cc: String, + /// Blind Carbon Copy recipient address + pub mail_bcc: String, + /// Sender address + pub mail_from: String, + /// Mail subject + pub subject: String, + /// Body policy code + pub policy_code: String, + /// Body type policy holder + pub policy_holder: String, + /// Body type deductible + pub deductible: String, + /// Body callback number + pub callback_number: String, + /// Body type callback date + pub callback_date: String, + /// Body type harm type + pub harm_type: String, + /// Body type ivr comment + pub ivr_comment: String +} + +impl Email {} + /// Harm data are list/collections of harm types. You may toggle them to an unselected state. #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct HarmData { @@ -157,20 +188,6 @@ pub struct PolicyCheck { impl PolicyCheck {} -// #[derive(Default, Clone, Debug, Serialize, Deserialize)] -// pub struct PolicyCheckList { -// pub title: String, -// pub list: Vec -// } - -// impl PolicyCheckList { -// pub fn new(title: impl Into) -> Self { -// PolicyCheckList { -// title: title.into(), -// ..Default::default() -// } -// } - /// Structure collecting policy data elements #[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct PolicyList { diff --git a/advotracker/src/services/exports/send_ticketdata.rs b/advotracker/src/services/exports/send_ticketdata.rs index ff435af..b2d1e7f 100644 --- a/advotracker/src/services/exports/send_ticketdata.rs +++ b/advotracker/src/services/exports/send_ticketdata.rs @@ -6,56 +6,115 @@ */ use lettre::{ + message::{header, MultiPart, SinglePart}, + Message, SmtpTransport, Transport, transport::smtp::authentication::Credentials, - //Message, SmtpTransport, Transport, - Message, SmtpTransport, }; use locales::t; +use maud::html; use std::error::Error; //use std::process; -use tracing::{info, trace}; +use tracing::{info, error, trace}; + +use crate::data::structures::Email; /// send ticket data via eMail -pub fn sendticketdata(recipient: &str, body: &str, lang: &str) -> Result<(), Box> { +pub fn sendticketdata(email: &Email, lang: &str) -> Result<(), Box> { let mut res = t!("sendticketdata.export.started", lang); let mut state = t!("state.started", lang); trace!(target: "sendticketdata", process = ?res, state = ?state); - let email = Message::builder() - .from("Advotracker Hiedemann ".parse().unwrap()) - .reply_to("Support ".parse().unwrap()) - //.to("Ralf Zerres ".parse().unwrap()) - .to(recipient.parse().unwrap()) - .subject("Advotracker Ticket") - //.body("Eine neue advotracker eMail!".to_string()) - .body(body.to_string()) - .unwrap(); + // The html we want to send. + // It uses https://crates.io/crates/maud + let html = html! { + head { + title { (email.subject) " (" (email.policy_code) ")" } + style type="text/css" { + "h2, h4 { font-family: Arial, Helvetica, sans-serif; }" + } + } + div style="display: flex; flex-direction: column; align-items: center;" { + // compose with variables and strings + h2 { (email.subject) " (" (email.policy_code) ")" } + p { "Policen-Code: " (email.policy_code) } + p { "Policen-Code: " (email.policy_code) } + p { "Versicherungsnehmer: " (email.policy_holder) } + p { "Selbstbehalt: "(email.deductible) } + p { "Rückrufnummer: " (email.callback_number) } + p { "Rückrufdatum: " (email.callback_date) } + p { "Schadenstyp: "(email.harm_type) } + p { "IVR Kommentar: "(email.ivr_comment) } + } + }; - info!("emal: {:?}", email); + let ascii_body = String::new() + + &"Policen-Code: ".to_string() + &(email.policy_code) + &"\n" + + &"Versicherungsnehmer: ".to_string() + &(email.policy_holder) + &"\n" + + &"Selbstbehalt: ".to_string() + &(email.deductible) + &"\n" + + &"Rückrufnummer: ".to_string()+ &(email.callback_number) + &"\n" + + &"Rückrufdatum: ".to_string() + &(email.callback_date) + &"\n" + + &"Schadenstyp: ".to_string() + &(email.harm_type) + &"\n" + + &"IVR Kommentar: ".to_string() + &(email.ivr_comment) + &"\n"; - // WIP testing ends here - //process::exit(0); + info!("email body: {:?}", ascii_body); + + let message = Message::builder() + .from((email.mail_from).parse().unwrap()) + //.reply_to("Support ".parse().unwrap()) + .to("Networkx-Info ".parse().unwrap()) + .cc("Ralf Zerres ".parse().unwrap()) + .reply_to((email.mail_from).parse().unwrap()) + //.to((email.mail_to).parse().unwrap()) + //.cc((email.mail_cc).parse().unwrap()) + //.bcc((email.mail_bcc).parse().unwrap()) + .subject(String::new() + + &email.subject.to_string() + + &" (".to_string() + + &email.policy_code.to_string() + + &")".to_string() + ) + .multipart( + MultiPart::alternative() // This is composed of two parts. + .singlepart( + SinglePart::builder() + .header(header::ContentType( + "text/plain; charset=utf8".parse().unwrap(), + )) + .body(String::from(ascii_body)), + ) + .singlepart( + SinglePart::builder() + .header(header::ContentType( + "text/html; charset=utf8".parse().unwrap(), + )) + .body(html.into_string()), + ), + ) + .expect("failed to build email"); + + + info!("message: {:?}", message); // Create credential for remote authentication (username, password) - //let creds = Credentials::new("ralf.zerres@networkx.de".to_string(), "dekifjgh".to_string()); + // WIP: get credentials from config file / environment let creds = Credentials::new("ralf.zerres.de@gmail.com".to_string(), "20jacara03".to_string()); // Open a remote connection to relay server - let _mailer = SmtpTransport::relay("smtp.gmail.com") + let mailer = SmtpTransport::relay("smtp.gmail.com") .unwrap() .credentials(creds) .build(); // Send the email - // match mailer.send(&email) { - // Ok(_) => info!("Email sent successfully!"), - // Err(e) => error!("Could not send email: {:?}", e), - // } + match mailer.send(&message) { + Ok(_) => info!("Email sent successfully!"), + Err(e) => error!("Could not send email: {:?}", e), + } - trace!(target: "sendticketdata", recipient = ?recipient); + trace!(target: "sendticketdata", email = ?email); state = t!("state.finished", lang); - res = t!("csv.export.finished", lang); - trace!(target: "csv-export", process = ?res, state = ?state); + res = t!("sendticketdata.export.finished", lang); + trace!(target: "sendticketdata", process = ?res, state = ?state); Ok(()) } diff --git a/advotracker/src/widgets/ticketdata/ticketdata_state.rs b/advotracker/src/widgets/ticketdata/ticketdata_state.rs index f62d14f..b8a33f2 100644 --- a/advotracker/src/widgets/ticketdata/ticketdata_state.rs +++ b/advotracker/src/widgets/ticketdata/ticketdata_state.rs @@ -15,9 +15,9 @@ use std::time::SystemTime; use tracing::{info, trace}; use crate::{ - data::constants::*, + data::{constants::*, structures::Email}, widgets::global_state::GlobalState, - //services::exports::sendticketdata, + services::exports::send_ticketdata::sendticketdata, widgets::policycheck::policycheck_state::{PolicycheckAction, PolicycheckState}, }; @@ -33,20 +33,9 @@ pub enum TicketdataAction { RemoveFocus(Entity), SetToggleTheme(Entity), SetEntry(Entity), - SendForm(), - SetVisibility(Entity), + SendForm(), SetVisibility(Entity), TextChanged(Entity, usize), - UpdatePolicyNumber(String) -} - -/// Handel fields of an Email (header, body) -#[derive(Clone, PartialEq)] -pub struct Email { - recipient_to: String, - recipient_cc: String, - sender: String, - subject: String, - body: String, + UpdatePolicyCode(String) } /// Define valid environment variables provided via .env files @@ -72,43 +61,79 @@ impl GlobalState for TicketdataState {} /// Method definitions, that react on any given state change inside the `Ticketdata` widget. impl TicketdataState { - /// Clear text in text box. - pub fn clear_entry(entity: Entity, ctx: &mut Context<'_>) { + /// Clear the text property of all children of the given form entity + pub fn clear_form(entity: Entity, ctx: &mut Context<'_>) { if let Some(count) = ctx.get_widget(entity).children_count() { - println!("Widget name: {:?}", ctx.get_widget(entity).get::("name")); - println!("Widget id: {:?}", ctx.get_widget(entity).get::("id")); + info!("Widget name: {:?}", ctx.get_widget(entity).get::("name")); + info!("Widget id: {:?}", ctx.get_widget(entity).get::("id")); + } + // identify the form by its id + if let form_entity = ctx.child(ID_TICKET_DATA_GRID).entity() { + info!("Form id: {:?}", ctx.get_widget(form_entity).get::("id")); + info!("Form node name: {:?}", ctx.get_widget(form_entity).get::("name")); + + // Loop through children + if let Some(count) = ctx.get_widget(form_entity).children_count() { + for c in 1..=count { + info!("WIP clear entry of child {:?}", c); + //info!("WIP clear entry of child {:?}: {:?}", c, ctx.get_child(form_entity).get::("name")); + } + } + } + // let Some(count) = ctx.child(ID_TICKET_DATA_GRID).entitry().children_count() { + // for c in 1..=count { + // println!("WIP clear entry of child {:?}", c); + // } + // } //let mut children = vec![]; //get_all_children(&mut children, entity, ecm.entity_store()); //trace!("Children {:?}: {:?}", children.len(), children); //println!("Child name: {:?}", ctx.try_child("ticket_data_form")); //TextBlock::text_set(&mut ctx.child(ID_TICKET_DATA_POLICY_HOLDER), ""); - println!("Number of childs: {:?}", count); - for c in 1..=count { - println!("WIP clear entry of child {:?}", c); + //println!("Number of childs: {:?}", count); + //for c in 1..=count { + // println!("WIP clear entry of child {:?}", c); // children = get_all_children() //println!("Child {:?}: Entity: {:?}", c, ctx.child_from_index(c)); //println!("Text: {:?}", ctx.get_widget(entity).entity_of_child(entity)); //TextBlock::text_set(&mut ctx.child(ID_TICKET_DATA_POLICY_HOLDER), ""); - } - } + //} //TextBox::text_set(&mut ctx.widget(entity), String::from("")); } - pub fn send_form(entity: Entity, _ctx: &mut Context<'_>) { - info!("WIP: Sending form to construct eMail for {:?}", entity); - } + pub fn send_form(entity: Entity, ctx: &mut Context<'_>, lang: &str) { - /// sending message 'ClearForm' - pub fn send_message_clear_form(&mut self) { - info!("Sending message 'TicketdataAction::ClearForm'"); - self.actions.push(TicketdataAction::ClearForm()); - } + // type conversion (String -> u64) + //let policy_code = ctx.child(ID_TICKET_DATA_POLICY_CODE).get::("text").unwrap().parse::().unwrap(); - /// sending message 'SendForm' - pub fn send_message_send_form(&mut self) { - info!("Sending message 'TicketdataAction::SendForm'"); - self.actions.push(TicketdataAction::SendForm()); + // WIP: get selected items ComboBox'es + //let mail_cc_index = *TicketdataView::selected_index_ref(&ctx.widget()) as usize; + //let mail_cc_selected = TicketdataView::mail_cc_ref(&ctx.widget())[mail_cc_index].clone(); + + // create Email structures + let email = Email { + // WIP: mail_to -> selected index auslesen + //mail_to: ctx.child(ID_TICKET_DATA_MAIL_TO).get::("text").to_string(), + // WIP: mail_cc -> selected index auslesen + //mail_cc: ctx.child(ID_TICKET_DATA_MAIL_CC).get::("text").to_string(), + mail_to: PROP_MAIL_TO_1.to_string(), + mail_cc: PROP_MAIL_CC_1.to_string(), + mail_bcc: PROP_MAIL_BCC_1.to_string(), + mail_from: PROP_MAIL_FROM.to_string(), + subject: PROP_MAIL_SUBJECT.to_string(), + policy_code: ctx.child(ID_TICKET_DATA_POLICY_CODE).get::("text").to_string(), + policy_holder: ctx.child(ID_TICKET_DATA_POLICY_HOLDER).get::("text").to_string(), + deductible: ctx.child(ID_TICKET_DATA_DEDUCTIBLE).get::("text").to_string(), + callback_number: ctx.child(ID_TICKET_DATA_CALLBACK_NUMBER).get::("text").to_string(), + callback_date: ctx.child(ID_TICKET_DATA_CALLBACK_DATE).get::("text").to_string(), + harm_type: ctx.child(ID_TICKET_DATA_HARM_TYPE).get::("text").to_string(), + ivr_comment: ctx.child(ID_TICKET_DATA_IVR_COMMENT).get::("text").to_string(), + }; + + info!("WIP: Sending form to construct eMail to {:?}", email); + // send email via service + let _ = sendticketdata(&email, &lang); } } @@ -143,30 +168,24 @@ impl State for TicketdataState { _registry: &mut Registry, ctx: &mut Context<'_>, ) { - for action in messages.read::() { - match action { + for message in messages.read::() { + match message { TicketdataAction::ClearForm() => { - println!("message: {:?} recieved", action); - info!("message: {:?} recieved", action); - //TicketdataState::clear_entry(ctx.entity(), ctx); - //ctx.clear_children_of(self.ticket_data_form); - + info!("message: {:?} recieved", message); + TicketdataState::clear_form(ctx.entity(), ctx); } TicketdataAction::SendForm() => { - println!("message: {:?} recieved", action); - info!("message: {:?} recieved", action); - TicketdataState::send_form(ctx.entity(), ctx); + info!("message: {:?} recieved", message); + TicketdataState::send_form(ctx.entity(), ctx, &self.lang); } _ => { println!("messages: action not implemented!"); } } } - for action in messages.read::() { - match action { - PolicycheckAction::UpdatePolicyNumber(policy_number) => { - info!("Message received: 'PolicycheckAction::UpdatePolicyNumber({:?})'", policy_number); - //progress_bar.set::("val", current_progress + amount); - //TextBlock::text_set(&mut ctx.child(ID_TICKET_DATA_POLICY_NUMBER), "9999999992"); - TextBlock::text_set(&mut ctx.child(ID_TICKET_DATA_POLICY_NUMBER), policy_number); + for message in messages.read::() { + match message { + PolicycheckAction::UpdatePolicyCode => { + info!("Message received: 'PolicycheckAction::UpdatePolicyCode'"); + //TextBlock::text_set(&mut ctx.child(ID_TICKET_DATA_POLICY_CODE), policy_code); } _ => { println!("messages: action not implemented!"); } } diff --git a/advotracker/src/widgets/ticketdata/ticketdata_view.rs b/advotracker/src/widgets/ticketdata/ticketdata_view.rs index 48088fe..8fbd13c 100644 --- a/advotracker/src/widgets/ticketdata/ticketdata_view.rs +++ b/advotracker/src/widgets/ticketdata/ticketdata_view.rs @@ -12,8 +12,7 @@ use orbtk::{ use crate::{ data::constants::*, - //widgets::ticketdata::ticketdata_state::{TicketdataAction, TicketdataState}, - widgets::ticketdata::ticketdata_state::TicketdataState, + widgets::ticketdata::ticketdata_state::{TicketdataAction, TicketdataState}, }; // Macro that initializes the widget structures/variables for the policy check view @@ -91,6 +90,8 @@ impl Template for TicketdataView { .style("container_form") .child( Grid::new() + .id(ID_TICKET_DATA_GRID) + .name(ID_TICKET_DATA_GRID) .columns( Columns::create() .push("auto") // Label @@ -117,17 +118,17 @@ impl Template for TicketdataView { ) .child( TextBlock::new() - .id(ID_TICKET_DATA_LABEL_POLICY_NUMBER) + .id(ID_TICKET_DATA_LABEL_POLICY_CODE) .attach(Grid::row(0)) .attach(Grid::column(0)) - .text("Policy number") + .text("Policy code") .h_align("end") .v_align("center") .build(ctx), ) .child( TextBlock::new() - .id(ID_TICKET_DATA_POLICY_NUMBER) + .id(ID_TICKET_DATA_POLICY_CODE) .attach(Grid::row(0)) .attach(Grid::column(2)) .text("9999999990") @@ -149,6 +150,7 @@ impl Template for TicketdataView { .id(ID_TICKET_DATA_POLICY_HOLDER) .attach(Grid::row(2)) .attach(Grid::column(2)) + .text("") .water_mark("Name des Versicherungsnehmers") .build(ctx), ) @@ -167,6 +169,7 @@ impl Template for TicketdataView { .id(ID_TICKET_DATA_DEDUCTIBLE) .attach(Grid::row(4)) .attach(Grid::column(2)) + .text("") .water_mark("im Beratungsgespräch erfragen") .build(ctx), ) @@ -185,6 +188,7 @@ impl Template for TicketdataView { .id(ID_TICKET_DATA_CALLBACK_NUMBER) .attach(Grid::row(6)) .attach(Grid::column(2)) + .text("") .water_mark("wie zu erreichen") .build(ctx), ) @@ -203,6 +207,7 @@ impl Template for TicketdataView { .id(ID_TICKET_DATA_CALLBACK_DATE) .attach(Grid::row(8)) .attach(Grid::column(2)) + .text("") .water_mark("Rückruf gewünscht um") .build(ctx), ) @@ -211,17 +216,18 @@ impl Template for TicketdataView { .id(ID_TICKET_DATA_LABEL_HARM_TYPE) .attach(Grid::row(10)) .attach(Grid::column(0)) - .water_mark("Harm type") + .text("Harm type") .h_align("end") .v_align("center") .build(ctx), ) .child( TextBox::new() - .id(ID_TICKET_DATA_LABEL_HARM_TYPE) - .name(ID_TICKET_DATA_LABEL_HARM_TYPE) + .id(ID_TICKET_DATA_HARM_TYPE) + .name(ID_TICKET_DATA_HARM_TYPE) .attach(Grid::row(10)) .attach(Grid::column(2)) + .text("") .water_mark("Rechtsproblem/Schadenstyp") .build(ctx), ) @@ -240,6 +246,7 @@ impl Template for TicketdataView { .id(ID_TICKET_DATA_IVR_COMMENT) .attach(Grid::row(12)) .attach(Grid::column(2)) + .text("") .water_mark("Rechtsrat / Kommentar zu Haftungs, bzw. Deckung") //.height(48.0) .build(ctx), @@ -266,9 +273,7 @@ impl Template for TicketdataView { .style(STYLE_BUTTON_ACTION) .text("Clear") .on_click(move |states, _| { - //println!("WIP: clear from"); - //states.send_message(TicketdataAction::ClearForm, id); - states.get_mut::(id).send_message_clear_form(); + states.send_message(TicketdataAction::ClearForm(), id); false }) .build(ctx), @@ -280,9 +285,7 @@ impl Template for TicketdataView { .text("Send") //.visibility(Visibility::Collapsed) .on_click(move |states, _entity| { - //println!("WIP: send mail"); - //states.get_mut::(id).send_message(TicketdataAction::SendForm, id); - states.get_mut::(id).send_message_send_form(); + states.send_message(TicketdataAction::SendForm(), id); false }) .build(ctx), @@ -325,7 +328,7 @@ impl Template for TicketdataView { ) .child( ComboBox::new() - .id(ID_TICKET_DATA_COMBO_BOX_MAIL_TO) + .id(ID_TICKET_DATA_MAIL_TO) .attach(Grid::row(0)) .attach(Grid::column(3)) .style(STYLE_MAIL_TO) @@ -371,7 +374,7 @@ impl Template for TicketdataView { ) .child( ComboBox::new() - .id(ID_TICKET_DATA_COMBO_BOX_MAIL_CC) + .id(ID_TICKET_DATA_MAIL_CC) .attach(Grid::row(2)) .attach(Grid::column(3)) .style(STYLE_MAIL_CC)