From 26cf43bfaebc39159a89896734a8d31e752120ee Mon Sep 17 00:00:00 2001 From: Ralf Zerres Date: Sun, 28 Feb 2021 19:07:24 +0100 Subject: [PATCH] ticketdata: introduce new widget * Cargo.toml: introduce carte `lettre` used in send_ticketdata.rs * locales: extend translations * theme: extend translations * send_ticketdata: send harm data via email * ticket_data: aquisition of harm data * update constant usage Signed-off-by: Ralf Zerres --- advotracker/Cargo.toml | 17 + .../assets/advotracker/advotracker_de_DE.ron | 72 +-- advotracker/src/data/constants.rs | 104 ++++- advotracker/src/locales/advotracker.json | 10 + advotracker/src/services/exports/mod.rs | 3 + .../src/services/exports/send_ticketdata.rs | 61 +++ advotracker/src/widgets/main_view.rs | 24 +- advotracker/src/widgets/menu/menu_state.rs | 315 +++++++++---- advotracker/src/widgets/menu/menu_view.rs | 145 ++++-- advotracker/src/widgets/mod.rs | 3 + advotracker/src/widgets/ticketdata/mod.rs | 12 + .../widgets/ticketdata/ticketdata_state.rs | 143 ++++++ .../src/widgets/ticketdata/ticketdata_view.rs | 440 ++++++++++++++++++ 13 files changed, 1148 insertions(+), 201 deletions(-) create mode 100644 advotracker/src/services/exports/send_ticketdata.rs create mode 100644 advotracker/src/widgets/ticketdata/mod.rs create mode 100644 advotracker/src/widgets/ticketdata/ticketdata_state.rs create mode 100644 advotracker/src/widgets/ticketdata/ticketdata_view.rs diff --git a/advotracker/Cargo.toml b/advotracker/Cargo.toml index 6cfaabd..e0b5c7c 100644 --- a/advotracker/Cargo.toml +++ b/advotracker/Cargo.toml @@ -24,12 +24,14 @@ clap = { version = "~2.33", features = ["suggestions", "color"] } csv = { version = "~1.1" } dotenv = { version = "~0.15.0" } envy = { version = "~0.4" } +lettre = "0.10.0-alpha.4" lazy_static = { version = "~1.4.0" } log = { version = "~0.4.8" } locales = { version = "~0.1" } #orbtk = { version = "~0.3.1-alpha4" } #orbtk = { git = "https://github.com/redox-os/orbtk.git", branch = "develop" } orbtk = { path = "../../orbtk" } +#regex = { version = "~1" } serde = { version = "~1.0", features = ["derive"] } #tokio = { version = "~0.2", features = ["macros", "rt-threaded", "stream", "time"] } tracing = { version = "~0.1" } @@ -47,3 +49,18 @@ name = "advotracker" identifier = "nwx.advotracker" short_description = "Online legal advice helper." description = "Supports lawyers to capture relevant data encountered during an online legal advice.\n" + +[profile.dev] +opt-level = 1 +incremental = true + +[profile.release] +incremental = true + +#[[bin]] +#//name = "policycheck" +#path = "src/bin/policycheck.rs" + +[[bin]] +name = "advotracker" +path = "src/main.rs" diff --git a/advotracker/assets/advotracker/advotracker_de_DE.ron b/advotracker/assets/advotracker/advotracker_de_DE.ron index 8630eb1..dea6a4a 100644 --- a/advotracker/assets/advotracker/advotracker_de_DE.ron +++ b/advotracker/assets/advotracker/advotracker_de_DE.ron @@ -10,37 +10,49 @@ Dictionary ( // the map of active identifiers // like a struct, but keys are also values instead of just beenig identifiers words: { - // policycheck_view - "Validation policy number": "Prüfung Versicherungsnummer", - "Policy number": "Versicherungsnummer", - "Policy code": "ID der Vers.-Nummer", - "Checklist elements: ": "Prüflistenelemente: ", - "Check result": "Prüfungsergebnis", - "Importing data": "Importiere Datensätze", - "Processing time": "Bearbeitungszeit", - "Error:": "Fehler:", - "Reason": "Grund", - "Policy number is to long": "Die Nummer ist zu lang", - "Policy number is to short": "Die Nummer ist zu kurz", - "The given policy number is invalid": "Die Versicherungsnummer ist ungültig", - "The given policy number is valid": "Die Versicherungsnummer ist gültig", - "Only numbers are valid": "Nur Nummern sind zulässig", - "Account": "Benutzer", - "Toggle theme": "Thema wechseln", - "Quit": "Beenden", + // policycheck_view + "Validation policy number": "Prüfung Versicherungsnummer", + "Policy number": "Versicherungsnummer", + "Policy code": "Policen-Code", + "Checklist elements: ": "Prüflistenelemente: ", + "Check result": "Prüfungsergebnis", + "Importing data": "Importiere Datensätze", + "Processing time": "Bearbeitungszeit", + "Error:": "Fehler:", + "Reason": "Grund", + "Policy number is to long": "Die Nummer ist zu lang", + "Policy number is to short": "Die Nummer ist zu kurz", + "The given policy number is invalid": "Die Versicherungsnummer ist ungültig", + "The given policy number is valid": "Die Versicherungsnummer ist gültig", + "Only numbers are valid": "Nur Nummern sind zulässig", + "Account": "Benutzer", + "Toggle theme": "Thema wechseln", + "Quit": "Beenden", - // localization view - "Language ID": "Sprache ID", - "Localization dialog": "Lokalisierungs-Dialog", - "German": "Deutsch", - "English": "Englisch", + // ticketdata_view + "Callback number": "Rückrufnummer", + "Callback date": "Rückrufdatum", + "Clear": "Zurücksetzen", + "Deductible": "Selbstbehalt", + "Harm type": "Schadenstyp", + "IVR comment": "IVR Kommentar", + "Policy holder": "Versicherungsnehmer", + "Recipient (To)": "Empfänger (To)", + "Copie (CC)": "Kopie (CC)", + "Send": "Senden", - // configuration view - "Configuration settings": "Konfigurationseinstellungen", - "Configuration file": "Konfigurationsdatei", - "Language Id": "Sprach-Id", - "Default theme": "Standard-Thema", - "load": "laden", - "save": "speichern" + // localization view + "Language ID": "Sprache ID", + "Localization dialog": "Lokalisierungs-Dialog", + "German": "Deutsch", + "English": "Englisch", + + // configuration view + "Configuration settings": "Konfigurationseinstellungen", + "Configuration file": "Konfigurationsdatei", + "Language Id": "Sprach-Id", + "Default theme": "Standard-Thema", + "Load": "Laden", + "Save": "Speichern", } ) diff --git a/advotracker/src/data/constants.rs b/advotracker/src/data/constants.rs index 52fbe4d..db13757 100644 --- a/advotracker/src/data/constants.rs +++ b/advotracker/src/data/constants.rs @@ -1,20 +1,47 @@ /* * advotracker - Hotline tackingtool for Advocats * - * Copyright 2020 Ralf Zerres + * Copyright 2021 Ralf Zerres * SPDX-License-Identifier: (0BSD or MIT) */ +// Component Values (Properties) +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_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_POLICY_CHECK: &str = "policy_check"; +pub static PROP_POLICY_PROGRESS_COUNT: &str = "policy_progress_count"; + +pub static PROP_POLICY_DATA_LIST: &str = "policy_data_list"; +pub static PROP_POLICY_DATA_COUNT: &str = "policy_data_count"; + +pub static PROP_POLICY_LIST: &str = "policy_list"; +pub static PROP_POLICY_LIST_COUNT: &str = "policy_list_count"; + // Styles (RON based theme system) pub static STYLE_BOTTOM_BAR: &str = "bottom_bar"; -pub static STYLE_HEADER_BAR: &str = "header_bar"; -pub static STYLE_SEPERATOR: &str = "seperator"; -//pub static STYLE_ITEM_BUTTON: &str = "item_button"; -pub static STYLE_MENU: &str = "menu"; pub static STYLE_BUTTON_MENU: &str = "button_menu"; +pub static STYLE_BUTTON_ACTION: &str = "button_action"; +pub static STYLE_CONTAINER_ACTION: &str = "container_action"; +pub static STYLE_CONTAINER_MAIL: &str = "container_mail"; +pub static STYLE_HEADER_BAR: &str = "header_bar"; +pub static STYLE_HEADER_TEXT: &str = "header_text"; +pub static STYLE_SEPARATOR: &str = "separator"; +pub static STYLE_MAIL_LABEL: &str = "mail_label"; +pub static STYLE_MAIL_TO: &str = "mail_to"; +pub static STYLE_MAIL_CC: &str = "mail_cc"; +pub static STYLE_MENU: &str = "menu"; +pub static STYLE_STACK_ACTION: &str = "stack_action"; pub static STYLE_STACK_MENU: &str = "stack_menu"; // Widget IDs (DCES: Entity[id] => [Component1, .. , Component] -> data or state) +pub static ID_CONFIGURATION_VIEW: &str = "Configuration"; pub static ID_CONFIGURATION_FORM: &str = "configuration_form"; pub static ID_CONFIGURATION_HEADER: &str = "configuration_header"; pub static ID_CONFIGURATION_LABEL_CONFIG_FILE: &str = "configuration_label_config_file"; @@ -22,27 +49,37 @@ pub static ID_CONFIGURATION_CONFIG_FILE: &str = "configuration_config_file"; pub static ID_CONFIGURATION_LABEL_LANGUAGE_ID: &str = "configuration_label_language_id"; pub static ID_CONFIGURATION_LANGUAGE_ID: &str = "configuration_language_id"; +pub static ID_LOCALIZATION_VIEW: &str = "Localization"; pub static ID_LOCALIZATION_FORM: &str = "localization_form"; pub static ID_LOCALIZATION_HEADER: &str = "localization_header"; pub static ID_LOCALIZATION_LANGUAGES: &str = "localization_languages"; pub static ID_LOCALIZATION_LABEL_LANGUAGE_NAME: &str = "localization_label_language_name"; pub static ID_LOCALIZATION_LANGUAGE_NAME: &str = "localization_language_name"; +pub static ID_MAIN_GRID: &str = "main_grid"; +pub static ID_MAIN_TABWIDGET: &str = "main_tabwidget"; +pub static ID_MAIN_BUTTON_MENU: &str = "main_button_menu"; + +pub static ID_MENU_VIEW: &str = "menu_view"; pub static ID_MENU_POPUP: &str = "menu_popup"; pub static ID_MENU_STACK: &str = "menu_stack"; +pub static ID_MENU_BOTTOM_BAR: &str = "menu_bottom_bar"; pub static ID_MENU_BUTTON: &str = "menu_button"; +pub static ID_MENU_BUTTON_MENU: &str = "menu_button_menu"; pub static ID_MENU_GRID: &str = "menu_grid"; +pub static ID_MENU_HEADER: &str = "menu_header"; +pub static ID_MENU_HEADER_BAR: &str = "menu_header_bar"; pub static ID_MENU_LABEL_ACCOUNT: &str = "menu_label_account"; pub static ID_MENU_LABEL_QUIT: &str = "menu_label_quit"; pub static ID_MENU_LABEL_TOGGLE_THEME: &str = "menu_label_toggle_theme"; pub static ID_MENU_SHORTCUT_QUIT: &str = "menu_shortcut_quit"; pub static ID_MENU_TOGGLE_THEME: &str = "menu_toggle_theme"; +pub static ID_POLICY_CHECK_VIEW: &str = "policy_check_view"; pub static ID_POLICY_CHECK_FORM: &str = "policy_check_form"; -pub static ID_POLICY_CHECK_FORM_ROW_0: &str = "policy_check_form_row_0"; -pub static ID_POLICY_CHECK_FORM_ROW_1: &str = "policy_check_form_row_1"; -pub static ID_POLICY_CHECK_FORM_ROW_2: &str = "policy_check_form_row_2"; +pub static ID_POLICY_CHECK_BOTTOM_BAR: &str = "policy_check_bottom_bar"; pub static ID_POLICY_CHECK_HEADER: &str = "policy_check_header"; +pub static ID_POLICY_CHECK_HEADER_BAR: &str = "policy_check_header_bar"; pub static ID_POLICY_CHECK_ITEMS_WIDGET: &str = "policy_check_items_widget"; pub static ID_POLICY_CHECK_BUTTON_RESULT: &str = "policy_check_button_result"; pub static ID_POLICY_CHECK_BUTTON_MENU: &str = "policy_check_button_menu"; @@ -76,13 +113,44 @@ pub static ID_POLICY_LIST_ADD_BUTTON: &str = "policy_list_add_button"; pub static ID_POLICY_LIST_ITEMS_WIDGET: &str = "policy_list_items_widget"; pub static ID_POLICY_LIST_TEXT_BOX: &str = "policy_list_text_box"; -// Component Values (Properties) -pub static PROP_ADVOTRACKER: &str = "advotracker"; -pub static PROP_POLICY_CHECK: &str = "policy_check"; -pub static PROP_POLICY_PROGRESS_COUNT: &str = "policy_progress_count"; - -pub static PROP_POLICY_DATA_LIST: &str = "policy_data_list"; -pub static PROP_POLICY_DATA_COUNT: &str = "policy_data_count"; - -pub static PROP_POLICY_LIST: &str = "policy_list"; -pub static PROP_POLICY_LIST_COUNT: &str = "policy_list_count"; +pub static ID_TICKET_DATA_ACTION_BUTTON_CLEAR: &str = "ticket_data_action_button_clear"; +pub static ID_TICKET_DATA_ACTION_BUTTON_SEND: &str = "ticket_data_action_button_send"; +pub static ID_TICKET_DATA_ACTION_GRID: &str = "ticket_data_action_grid"; +pub static ID_TICKET_DATA_BOTTOM_BAR: &str = "ticket_data_bottom_bar"; +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"; +pub static ID_TICKET_DATA_FORM: &str = "ticket_data_form"; +pub static ID_TICKET_DATA_GRID: &str = "ticket_data_grid"; +pub static ID_TICKET_DATA_GRID_MAIL: &str = "ticket_data_grid_mail"; +pub static ID_TICKET_DATA_HARM_TYPE: &str = "ticket_data_harm_type"; +pub static ID_TICKET_DATA_HEADER_BAR: &str = "ticket_data_header_bar"; +pub static ID_TICKET_DATA_HEADER: &str = "ticket_data_header"; +pub static ID_TICKET_DATA_HINT: &str = "ticket_data_hint"; +pub static ID_TICKET_DATA_ITEMS_WIDGET: &str = "ticket_data_items_widget"; +pub static ID_TICKET_DATA_IVR_COMMENT: &str = "ticket_data_ivr_comment"; +pub static ID_TICKET_DATA_LABEL_CALLBACK_DATE: &str = "ticket_data_label_callback_data"; +pub static ID_TICKET_DATA_LABEL_CALLBACK_NUMBER: &str = "ticket_data_label_callback_number"; +pub static ID_TICKET_DATA_LABEL_DEDUCTIBLE: &str = "ticket_data_label_deductible"; +pub static ID_TICKET_DATA_LABEL_HARM_TYPE: &str = "ticket_data_label_harm_type"; +pub static ID_TICKET_DATA_LABEL_HINT: &str = "ticket_data_label_hint"; +pub static ID_TICKET_DATA_LABEL_IVR_COMMENT: &str = "ticket_data_label_ivr_comment"; +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_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_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/locales/advotracker.json b/advotracker/src/locales/advotracker.json index 8e341c5..be7c535 100644 --- a/advotracker/src/locales/advotracker.json +++ b/advotracker/src/locales/advotracker.json @@ -225,6 +225,16 @@ "de_DE": "Die Nummer ist zu kurz", "C": "Policy number is to short" }, + "sendticketdata.export.started":{ + "de_DE.UTF-8": "Versenden von ticket daten via eMail gestartet", + "de_DE": "Versenden von ticket daten via eMail gestartet", + "C": "Send ticket data via eMail started" + }, + "sendticketdata.export.finished":{ + "de_DE.UTF-8": "Versenden von ticket daten via eMail beendet", + "de_DE": "Versenden von ticket daten via eMail beendet", + "C": "Send ticket data via eMail finished" + }, "state.started": { "de_DE.UTF-8": "gestartet", "de_DE": "gestartet", diff --git a/advotracker/src/services/exports/mod.rs b/advotracker/src/services/exports/mod.rs index 5dec007..bd16ef3 100644 --- a/advotracker/src/services/exports/mod.rs +++ b/advotracker/src/services/exports/mod.rs @@ -5,5 +5,8 @@ * SPDX-License-Identifier: (0BSD or MIT) */ +/// Send ticketdata via email +pub mod send_ticketdata; + /// Exporting Allianz DirecCall data pub mod allianzdirectcall; diff --git a/advotracker/src/services/exports/send_ticketdata.rs b/advotracker/src/services/exports/send_ticketdata.rs new file mode 100644 index 0000000..ff435af --- /dev/null +++ b/advotracker/src/services/exports/send_ticketdata.rs @@ -0,0 +1,61 @@ +/* + * advotracker - Hotline tackingtool for Advocats + * + * Copyright 2021 Ralf Zerres + * SPDX-License-Identifier: (0BSD or MIT) + */ + +use lettre::{ + transport::smtp::authentication::Credentials, + //Message, SmtpTransport, Transport, + Message, SmtpTransport, +}; +use locales::t; +use std::error::Error; +//use std::process; +use tracing::{info, trace}; + +/// send ticket data via eMail +pub fn sendticketdata(recipient: &str, body: &str, 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(); + + info!("emal: {:?}", email); + + // WIP testing ends here + //process::exit(0); + + // Create credential for remote authentication (username, password) + //let creds = Credentials::new("ralf.zerres@networkx.de".to_string(), "dekifjgh".to_string()); + 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") + .unwrap() + .credentials(creds) + .build(); + + // Send the email + // match mailer.send(&email) { + // Ok(_) => info!("Email sent successfully!"), + // Err(e) => error!("Could not send email: {:?}", e), + // } + + trace!(target: "sendticketdata", recipient = ?recipient); + state = t!("state.finished", lang); + res = t!("csv.export.finished", lang); + trace!(target: "csv-export", process = ?res, state = ?state); + + Ok(()) +} diff --git a/advotracker/src/widgets/main_view.rs b/advotracker/src/widgets/main_view.rs index 8057a3e..3286e42 100644 --- a/advotracker/src/widgets/main_view.rs +++ b/advotracker/src/widgets/main_view.rs @@ -8,11 +8,15 @@ use orbtk::prelude::*; use crate::{ - data::structures::PolicyCheck, + data::{ + constants::*, + structures::PolicyCheck, + }, + widgets::configuration::configuration_view::ConfigurationView, widgets::policycheck::policycheck_view::PolicycheckView, widgets::localization::localization_view::LocalizationView, - widgets::configuration::configuration_view::ConfigurationView, widgets::menu::menu_view::MenuView, + widgets::ticketdata::ticketdata_view::TicketdataView, }; // [START] views @@ -23,14 +27,15 @@ widget!(MainView { // policylist_view: u32, // policydata_view: u32, policycheck_view: PolicyCheck + //ticketdata_view: TicketData }); impl Template for MainView { fn template(self, _id: Entity, ctx: &mut BuildContext<'_>) -> Self { - let policycheck_view = PolicycheckView::new() + //let policycheck_view = PolicycheckView::new() //.policy_number_count(0) //.policylist_view(id) - .build(ctx); + // .build(ctx); // let policylist_view = PolicyListView::new() // .back_entity(policycheck_view.0) @@ -46,7 +51,7 @@ impl Template for MainView { // .build(ctx); self.name("MainView") - .policycheck_view(PolicyCheck::default()) + //.policycheck_view(PolicyCheck::default()) // //.policycheck_view(0) // .policydata_view(policydata_view.0) // //.policylist_view(PolicyList::default()) @@ -55,10 +60,11 @@ impl Template for MainView { // .child(policylist_view) .child( TabWidget::new() - .tab("Policynumber check", policycheck_view) - .tab("Localization", LocalizationView::new().build(ctx)) - .tab("Configuration", ConfigurationView::new().build(ctx)) - .tab("Menu", MenuView::new().build(ctx)) + .tab(ID_POLICY_CHECK_VIEW, PolicycheckView::new().build(ctx)) + .tab(ID_TICKET_DATA_VIEW, TicketdataView::new().build(ctx)) + .tab(ID_LOCALIZATION_VIEW, LocalizationView::new().build(ctx)) + .tab(ID_CONFIGURATION_VIEW, ConfigurationView::new().build(ctx)) + .tab(ID_MENU_VIEW, MenuView::new().build(ctx)) .build(ctx), ) //.child(policycheck_view) diff --git a/advotracker/src/widgets/menu/menu_state.rs b/advotracker/src/widgets/menu/menu_state.rs index 7b61404..d665af9 100644 --- a/advotracker/src/widgets/menu/menu_state.rs +++ b/advotracker/src/widgets/menu/menu_state.rs @@ -1,13 +1,14 @@ /* * advotracker - Hotline tackingtool for Advocats * - * Copyright 2020 Ralf Zerres + * Copyright 2021 Ralf Zerres * SPDX-License-Identifier: (0BSD or MIT) */ use cfg_if::cfg_if; use orbtk::prelude::*; use orbtk::shell::event::Key; +use tracing::{info, trace}; use std::process; @@ -21,88 +22,59 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub enum MenuAction { CreateMenu, - CreateMenuToggleTheme(Entity), + CreateMenuToggleTheme, RemoveMenu, - RemoveMenuToggleTheme(Entity), - SetTheme + RemoveMenuToggleTheme, + SetTheme, + UpdateMenuRelativePosition } /// Valid `structures` that are handled inside the state of the `Menu` widget. #[derive(AsAny, Default)] pub struct MenuState { action: Option, - menu: Entity, + menu: Option, + //menu_toggle_theme: Option menu_toggle_theme: Entity } /// Method definitions, that react on any given state change inside the `Menu` widget. impl MenuState { - /// Set a menu holding all valid menu elements - /// The elements are ordered compontents inside a stack - pub fn create_menu(&mut self, ctx: &mut Context<'_>) { - // create a stack as a child of the given component - let stack = ctx - .entity_of_child(ID_MENU_BUTTON) - .expect("PolicycheckState: Can't find entity of resource 'ID_MENU_BUTTON'."); - //.entity_of_child(ID_MENU_BUTTON) - //.expect("PolicycheckState: Can't find entity of resource 'ID_POLICY_CHECK_POPUP_MENU'."); - let current_entity = ctx.entity(); - let build_context = &mut ctx.build_context(); - - // create a new popup menu overlay - self.menu = create_menu_popup(current_entity, build_context); - - // create a menu_popup widget as a child of entity "ID_POLICY_CHECK_BUTTON_MENU" - build_context.append_child(stack, self.menu); - - println!("Popup Menu created: {:?}", self.menu); - } - - /// Set a toggle_theme menu - /// Select the active theme from a `ComboBox` offering a list of valid `themes`` - fn create_menu_toggle_theme(&mut self, ctx: &mut Context<'_>) { - let stack = ctx - .entity_of_child(ID_MENU_LABEL_TOGGLE_THEME) - .expect("MenuState: Can't find entity of resource 'ID_MENU_LABEL_TOGGLE_THEME'."); - let current_entity = ctx.entity(); - let build_context = &mut ctx.build_context(); - - // create a new menu overlay - self.menu_toggle_theme = create_menu_toggle_theme_popup(current_entity, build_context); - - // create a menu_popup widget as a child of entity "ID_POPUP_MENU" - build_context.append_child(stack, self.menu_toggle_theme); - - println!("Popup Menu Toggle Theme created: {:?}", self.menu_toggle_theme); + /// Create a toggle_theme menu as a child of the given parent + /// Select the active theme from a `ComboBox` offering a list of valid `themes` + fn _create_menu_toggle_theme(&mut self, _ctx: &mut Context<'_>) { + self.action = Some(MenuAction::CreateMenuToggleTheme); } /// Remove the menu popup box fn remove_menu(&mut self, ctx: &mut Context<'_>) { + self.action = Some(MenuAction::RemoveMenu); ctx.remove_child(ctx.entity()); - println!("Popup {:?} removed !", ctx.entity()); + println!("Popup {:?} removed.", ctx.entity()); } /// Remove the menu popup box - fn remove_menu_toggle_theme(&mut self, id: Entity, ctx: &mut Context<'_>) { - ctx.remove_child(self.menu_toggle_theme); - println!("Popup {:?} removed !", id); + fn _remove_menu_toggle_theme(&mut self, _ctx: &mut Context<'_>) { + self.action = Some(MenuAction::RemoveMenuToggleTheme); } /// Sets a new action. pub fn set_action(&mut self, action: MenuAction) { self.action = action.into(); } + + /// Update the relative position of the menu overlay + fn _update_menu_relative_position(&mut self) { + println!("TODO: Update relative position of menu {}.", ID_MENU_POPUP); + //self.action = Some(MenuAction::UpdateRelativePosition); + } } /// Supported methods handled inside the `MenuState` impl State for MenuState { - /// Initialize the state of widgets inside `MenuState` - fn init(&mut self, _: &mut Registry, _ctx: &mut Context<'_>) { - // Initialize required entities - // let menu_button = ctx - // .entity_of_child(ID_MENU_STACK) - // .expect("MenuState.init: Can't find resource entity 'PolicycheckView::ID_POLICY_CHECK_BUTTON_MENU'."); -} + // /// Initialize the state of widgets inside `MenuState` + // fn init(&mut self, _: &mut Registry, ctx: &mut Context<'_>) { + // } /// Handle messages for the `MenuState` fn messages( @@ -113,6 +85,40 @@ impl State for MenuState { ) { for message in messages.read::() { match message { + MenuAction::CreateMenu => { + info!("WIP: create a menu popup in MenuState"); + + // assign parent entity + let parent = ctx.entity(); + trace!(parent = ?parent); + //let parent_name = ctx.get_widget().get::("text"); + //trace!(parent = ?parent, parent_name = ?parent_name); + + // assign target -> search for a child of parent with given id + let target = ctx + .entity_of_child(ID_MENU_BUTTON_MENU) + .expect("MenuState: Can't find entity of resource 'ID_MENU_BUTTON_MENU'."); + + trace!(target = ?target.0, entity_of_child = ?ID_MENU_BUTTON_MENU); + + // create the target entity as child of parent + let menu_popup = create_menu_popup(parent, &mut ctx.build_context()); + //let menu_popup = create_popup(target, "Testmenu Popup", &mut ctx.build_context()); + + // append the child to target (overlay stays on top of the main tree) + ctx.build_context() + .append_child_to_overlay(menu_popup) + .expect("Failed create an overlay that consumes popup `menu` as its child."); + self.menu = Some(menu_popup); + + trace!(menu = ?self.menu); + + // open: is the default + //ctx.get_widget(menu).set("open", true); + + info!("CreateMenu: parent {:?}, target: {:?}, popup: {:?}", parent, target, self.menu); + } + MenuAction::SetTheme => { let theme_index = *MenuView::selected_index_ref(&ctx.widget()); @@ -135,8 +141,8 @@ impl State for MenuState { } } } - }, - _ => (), + } + _ => {} } } } @@ -145,18 +151,51 @@ impl State for MenuState { fn update(&mut self, _registry: &mut Registry, ctx: &mut Context<'_>) { if let Some(action) = self.action { match action { - MenuAction::CreateMenu => { - println!("MenuAction::CreateMenu(_entity)"); - self.create_menu(ctx); - } - MenuAction::CreateMenuToggleTheme(_entity) => { - self.create_menu_toggle_theme(ctx); + MenuAction::CreateMenuToggleTheme => { + let menu_target = ctx + .entity_of_child(ID_MENU_LABEL_TOGGLE_THEME) + .expect("MenuState: Can't find entity of resource 'ID_MENU_LABEL_TOGGLE_THEME'."); + let current_entity = ctx.entity(); + let build_context = &mut ctx.build_context(); + + // create a new menu popup + self.menu_toggle_theme = create_menu_toggle_theme_popup(current_entity, build_context); + + // create a menu_popup widget as a child of entity "ID_POPUP_MENU" + build_context.append_child(menu_target, self.menu_toggle_theme); + + ctx.get_widget(self.menu_toggle_theme).clone::("visibility"); + + println!("Popup Menu Toggle Theme created: {:?}", self.menu_toggle_theme); } MenuAction::RemoveMenu => { self.remove_menu(ctx); } - MenuAction::RemoveMenuToggleTheme(entity) => { - self.remove_menu_toggle_theme(entity, ctx); + MenuAction::RemoveMenuToggleTheme => { + ctx.remove_child(self.menu_toggle_theme); + println!("Popup {:?} removed.", ctx.entity()); + } + MenuAction::UpdateMenuRelativePosition => { + if let Some(menu) = self.menu { + // let theme_combo_box = ctx.entity_of_child(ID_MENU_TOGGLE_THEME).unwrap(); + // let selected_index: i32 = ctx.get_widget(theme_combo_box).clone("selected_index"); + // // let relative_position: RelativePosition = + // // ctx.get_widget(ID_MENU_POPUP).clone_or_default("relative_position"); + // match selected_index { + // 0 => { + // RelativePosition::relative_position_mut(&mut ctx.widget()).into_bottom(); + // //RelativePosition::get_mut::("relative_position").into_bottom(); + // // ctx + // // .get_widget(self.ID_MENU_POPUP.unwrap()) + // // .set("relative_position", relative_position.into_bottom()); + // }, + // 1 => RelativePosition::relative_position_mut(&mut ctx.widget()).into_top(), + // 2 => RelativePosition::relative_position_mut(&mut ctx.widget()).into_left(), + // 3 => RelativePosition::relative_position_mut(&mut ctx.widget()).into_right(), + // _ => panic!(), + // } + println!("Relative position of menu {:?} updated.", menu); + } } _ => (), } @@ -165,14 +204,21 @@ impl State for MenuState { } /// Create a new popup presenting the menu components -pub fn create_menu_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { +fn create_menu_popup(target: Entity, ctx: &mut BuildContext<'_>) -> Entity { Popup::new() .id(ID_MENU_POPUP) - .style("container_menu") - .target(id) + .name(ID_MENU_POPUP) + .target(target.0) .open(true) - .width(280) - .height(140) + // WIP: this requires orbtk-develop with PR #304 (new_popup) + // Specify the popup position relative to the target + .relative_position(RelativePosition::Bottom(5.0)) + // Reference target from given Point + //.target(Point::new(100.0,100.0)) + //.style("popup_menu") + .width(300.0) + .height(100.0) + .on_key_down(move | ctx, key_event | { match key_event.key { Key::Q(..) => { @@ -183,7 +229,7 @@ pub fn create_menu_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { }, Key::Escape => { println!("KeyHandler: got Escape"); - ctx.get_mut::(id) + ctx.get_mut::(target) .set_action(MenuAction::RemoveMenu); }, _ => { @@ -192,30 +238,43 @@ pub fn create_menu_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { }; true }) + .child( + Container::new() + .child( + TextBlock::new() + //.style("popup_text_block") + .h_align("center") + .v_align("enter") + .text(ID_MENU_POPUP) + .build(ctx), + ) + .build(ctx), + ) .child( Grid::new() .id(ID_MENU_GRID) + .name(ID_MENU_GRID) .columns( Columns::create() - .push("80") // Menu Button - .push("1") // Seperator - .push("*") // Keyboard Shortcut - .build(), + .push("180") // Menu Button + .push("1") // Seperator + .push("auto") // Keyboard Shortcut ) .rows( Rows::create() .push("auto") .push("auto") .push("auto") - .build(), ) .child( Button::new() .id(ID_MENU_LABEL_ACCOUNT) + .name(ID_MENU_LABEL_ACCOUNT) .style("button_menu") + //.h_align("start") .attach(Grid::row(0)) .attach(Grid::column(0)) - .attach(Grid::column_span(2)) + //.attach(Grid::column_span(2)) .icon(material_icons_font::MD_PERSON) .text("Account") .build(ctx), @@ -223,26 +282,30 @@ pub fn create_menu_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { .child( Button::new() .id(ID_MENU_LABEL_TOGGLE_THEME) + .name(ID_MENU_LABEL_TOGGLE_THEME) .style("button_menu") + //.h_align("start") .attach(Grid::row(1)) .attach(Grid::column(0)) - .attach(Grid::column_span(2)) + //.attach(Grid::column_span(2)) .icon(material_icons_font::MD_EDIT) .text("Toggle theme") - // .on_click(move |ctx, _| { - // ctx.get_mut::(id) - // .set_action(MenuAction::SetTheme(id)); - // true - // }) + .on_click(move |ctx, _| { + ctx.get_mut::(target) + .set_action(MenuAction::CreateMenuToggleTheme); + true + }) .build(ctx), ) .child( Button::new() .id(ID_MENU_LABEL_QUIT) + .name(ID_MENU_LABEL_QUIT) .style("button_menu") + //.h_align("start") .attach(Grid::row(2)) .attach(Grid::column(0)) - .attach(Grid::column_span(2)) + //.attach(Grid::column_span(2)) .icon(material_icons_font::MD_SETTINGS_POWER) .text("Quit") .on_mouse_down(move |_states, _| { @@ -253,7 +316,8 @@ pub fn create_menu_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { .child( TextBlock::new() .id(ID_MENU_SHORTCUT_QUIT) - .style("button_menu") + .name(ID_MENU_SHORTCUT_QUIT) + //.style("button_menu") .attach(Grid::row(2)) .attach(Grid::column(2)) .margin((0, 0, 16, 0)) @@ -267,30 +331,93 @@ pub fn create_menu_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { .build(ctx) } + +fn _create_popup(target: Entity, text: &str, ctx: &mut BuildContext<'_>) -> Entity { + Popup::new() + .id("test_popup") + .name("Test Popup") + // Reference target from given Entity + .target(target.0) + // Reference target from given Point + //.target(Point::new(0.0, 5.0)) + // Specify the popup position relative to the target (the button in this case) + //.relative_position(RelativePosition::Bottom(5.0)) + .open(true) + .style("popup_form") + .width(350.0) + .height(100.0) + .child( + Container::new() + .child( + TextBlock::new() + //.style("popup_text_block") + .h_align("center") + .text(text) + .build(ctx), + ) + .build(ctx), + ) + .build(ctx) +} + + + /// Create a new popup submenu to toogle the active theme -pub fn create_menu_toggle_theme_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { - //let themes_count = themes.len(); - //ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), new_width); +fn create_menu_toggle_theme_popup(id: Entity, ctx: &mut BuildContext<'_>) -> Entity { + cfg_if! { + if #[cfg(windows)] { + // define the list of supported themes + let themes = vec![ + "default_dark".to_string(), + "default_light".to_string(), + "redox".to_string(), + "fluent_dark".to_string(), + "fluent_light".to_string() + ]; + } else { + // define the list of supported themes + let themes = vec![ + "default_dark".to_string(), + "default_light".to_string(), + "redox".to_string(), + ]; + } + } + + let themes_count = themes.len(); Popup::new() .id(ID_MENU_TOGGLE_THEME) + .target(id.0) .style("container_menu") - .target(id) - .open(true) .width(280) .height(140) + .on_key_down(move | _ctx, key_event | { + match key_event.key { + Key::Escape => { + println!("KeyHandler: got Escape"); + //ctx.get_mut::(id) + // .set_action(MenuAction::RemoveMenuToggleTheme); + }, + _ => { + println!("KeyHandler: got {:?}", key_event.key); + }, + }; + true + }) .child( ComboBox::new() .attach(Grid::column(2)) .attach(Grid::row(6)) - //.count(themes_count) - .items_builder(move |build_ctx, index| { + .count(themes_count) + .style("combo_box_form") + .items_builder(move |ctx, index| { let theme_name = - MenuView::themes_ref(&build_ctx.get_widget(id))[index].clone(); - TextBlock::new().v_align("center").text(theme_name).build(build_ctx) + MenuView::themes_ref(&ctx.get_widget(id))[index].clone(); + TextBlock::new().v_align("center").text(theme_name).build(ctx) }) - .on_changed("selected_index", move |ctx, _| { - ctx.send_message(MenuAction::SetTheme, id); + .on_changed("selected_index", move |states, _entity| { + states.send_message(MenuAction::SetTheme, id); println!("changed theme."); }) .selected_index(id) diff --git a/advotracker/src/widgets/menu/menu_view.rs b/advotracker/src/widgets/menu/menu_view.rs index c6d9ca3..5149b2d 100644 --- a/advotracker/src/widgets/menu/menu_view.rs +++ b/advotracker/src/widgets/menu/menu_view.rs @@ -5,7 +5,6 @@ * SPDX-License-Identifier: (0BSD or MIT) */ -use cfg_if::cfg_if; use orbtk::prelude::*; use crate::data::constants::*; @@ -27,56 +26,102 @@ widget!( /// All GUI elements are styled using the "style" attribute referencing to a ron based css impl Template for MenuView { fn template(self, id: Entity, ctx: &mut BuildContext<'_>) -> Self { - cfg_if! { - if #[cfg(windows)] { - // define the list of supported themes - let themes = vec![ - "default_dark".to_string(), - "default_light".to_string(), - "redox".to_string(), - "fluent_dark".to_string(), - "fluent_light".to_string() - ]; - } else { - // define the list of supported themes - let themes = vec![ - "default_dark".to_string(), - "default_light".to_string(), - "redox".to_string(), - ]; - } - } + let menu_bottom_bar = Container::new() + .id(ID_MENU_BOTTOM_BAR) + .style(STYLE_BOTTOM_BAR) + .attach(Grid::row(3)) + .attach(Grid::column(1)) + .attach(Grid::column_span(2)) + .v_align("end") + .child( + Container::new() + .child( Container::new() + .margin((0, 16, 16, 0)) + .v_align("center") + .child( + ImageWidget::new() + .image("assets/advotracker/hiedemann_logo.png") + .v_align("center") + .build(ctx), + ) + .build(ctx), + ) + .build(ctx), + ) + .child( + Container::new() + .attach(Grid::column(1)) + .h_align("end") + .v_align("end") + .child( + TextBlock::new() + .margin((0, 9, 48, 0)) + .text("©Networkx GmbH") + .build(ctx) + ) + .build(ctx), + ) + .build(ctx); + + let menu_button_menu = Button::new() + .id(ID_MENU_BUTTON_MENU) + .name(ID_MENU_BUTTON_MENU) + .style("button_single_content") + .icon(material_icons_font::MD_MENU) + .attach(Grid::column(2)) + //.min_size(16, 16) + //.h_align("end") + .h_align("start") + .on_click(move |ctx, _| { + ctx.send_message(MenuAction::CreateMenu, id); + true + }) + .build(ctx); + + let menu_header_bar = Container::new() + .id(ID_MENU_HEADER_BAR) + .style(STYLE_HEADER_BAR) + .attach(Grid::row(0)) + .attach(Grid::column(1)) + .attach(Grid::column_span(2)) + .child( + Grid::new() + .child( + TextBlock::new() + .style("header") + .id(ID_MENU_HEADER) + .v_align("center") + .h_align("left") + .text("Ticket Data") + .build(ctx), + ) + .build(ctx), + ) + .child(menu_button_menu) + .build(ctx); //self.themes(themes).child(MenuState::create_menu(ID_MENU_POPUP, ctx)) - self.themes(themes).child( - Stack::new() - .id(ID_MENU_STACK) - .child( - TextBlock::new() - .style("header") - .attach(Grid::row(0)) - .attach(Grid::column(0)) - .style("small_text") - .text("Select theme") - .build(ctx), - ) - .child( - Button::new() - //.id("button_menu") - .id(ID_MENU_BUTTON) - .style("button_single_content") - .icon(material_icons_font::MD_MENU) - .attach(Grid::column(2)) - .h_align("end") - .on_click(move |ctx, _| { - println!("WIP: open menu popup from MenuView"); - ctx.get_mut::(id) - .set_action(MenuAction::CreateMenu); - true - }) - .build(ctx), - ) - .build(ctx), - ) + self.name("MenuView") + .child( + Grid::new() + .id(ID_MENU_VIEW) + .columns( + Columns::create() + .push(50) // Left margin + .push("*") // Content + .push(50) // Right margin + ) + .rows( + Rows::create() + .push("auto") // Header_Bar + .push(28) // Seperator + .push("*") // InputForm + .push("auto") // Bottom_Bar + ) + + .child(menu_header_bar) // Row 0 + .child(menu_bottom_bar) // Row 3 + .build(ctx), + ) } } diff --git a/advotracker/src/widgets/mod.rs b/advotracker/src/widgets/mod.rs index 8e52a7c..47f48ee 100644 --- a/advotracker/src/widgets/mod.rs +++ b/advotracker/src/widgets/mod.rs @@ -28,3 +28,6 @@ pub mod policycheck; // /// Policylists widget. // pub mod policylist; + +/// Ticketdata wigdet. +pub mod ticketdata; diff --git a/advotracker/src/widgets/ticketdata/mod.rs b/advotracker/src/widgets/ticketdata/mod.rs new file mode 100644 index 0000000..9462fb6 --- /dev/null +++ b/advotracker/src/widgets/ticketdata/mod.rs @@ -0,0 +1,12 @@ +/* + * advotracker - Hotline tackingtool for Advocats + * + * Copyright 2020 Ralf Zerres + * SPDX-License-Identifier: (0BSD or MIT) + */ + +/// The ticket data state +pub mod ticketdata_state; + +/// The tckicket data view +pub mod ticketdata_view; diff --git a/advotracker/src/widgets/ticketdata/ticketdata_state.rs b/advotracker/src/widgets/ticketdata/ticketdata_state.rs new file mode 100644 index 0000000..2302de5 --- /dev/null +++ b/advotracker/src/widgets/ticketdata/ticketdata_state.rs @@ -0,0 +1,143 @@ +/* + * advotracker - Hotline tackingtool for Advocats + * + * Copyright 2020 Ralf Zerres + * SPDX-License-Identifier: (0BSD or MIT) + */ + +//use locales::t; +use orbtk::prelude::*; + +use serde::Deserialize; +//use std::process; +//use std::collections::HashMap; +use std::time::SystemTime; +//use tracing::{error, info, trace}; +use tracing::trace; + +use crate::{ + data::constants::*, + widgets::global_state::GlobalState, + //services::exports::sendticketdata, +}; + +/// Valid `actions` that are handled as state changes in the `Ticketdata` widget. +#[derive(Debug, Clone, Copy)] +pub enum TicketdataAction { + ClearEntry(Entity), + /// Clear text in the form + ClearForm(), + ChangeTheme(), + InputTextChanged(Entity), + ParseEntry(Entity), + RemoveFocus(Entity), + SetToggleTheme(Entity), + SetEntry(Entity), + SendForm(), + SetVisibility(Entity), + TextChanged(Entity, usize) +} + +/// Define valid environment variables provided via .env files +/// located in the current call directory. +/// This is primarily used in testing scenarios (eg. debugging). +#[derive(Debug, Deserialize)] +struct Environment { + test_lang: String, + rust_log: String, +} + +/// Valid `structures` that are handled inside the state of the `Ticket` widget. +#[derive(AsAny, Default)] +pub struct TicketdataState { + action: Option, + //duration: Duration, + lang: String, + button_menu: Entity +} + +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(ctx: &mut Context<'_>, entity: Entity) { + 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")); + + //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); + // 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("")); + } + + /// Clear ticket data form + pub fn send_message(&mut self) { + println!("WIP: send_message"); + //self.action.push(TicketdataAction::ClearForm()); + //self.clear_entry(ticket_data_policy_holder); + } + + /// Sets a new action. + pub fn set_action(&mut self, action: TicketdataAction) { + self.action = action.into(); + } + +} + +/// Supported methods handled inside the `TicketState` +impl State for TicketdataState { + /// Initialize the state of widgets inside `TicketState` + fn init(&mut self, _: &mut Registry, ctx: &mut Context<'_>) { + let time_start= SystemTime::now(); + + trace!(target: "advotracker", ticketdata_state = "init", status = "started"); + + // Initialize required entities + self.button_menu = ctx + .entity_of_child(ID_TICKET_DATA_BUTTON_MENU) + .expect("TicketState.init: Can't find resource entity 'ID_TICKET_DATA_BUTTON_MENU'."); + + // Get language from environment + self.lang = TicketdataState::get_lang(); + + let time_end = SystemTime::now(); + let duration = time_end.duration_since(time_start); + + trace!(target: "advotracker", ticketdata_state = "init", status = "finished", duration = ?duration); + } + + fn messages( + &mut self, + mut messages: MessageReader, + _registry: &mut Registry, + ctx: &mut Context<'_>, + ) { + for action in messages.read::() { + match action { + TicketdataAction::ClearForm() => { + println!("WIP: Message 'ClearForm' received"); + TicketdataState::clear_entry(ctx, ctx.entity()) + //TicketdataState::clear_entry(ctx, ctx.entity()); + } + TicketdataAction::SendForm() => { + println!("WIP: Message 'SendForm' received"); + } + _ => { println!("messages: action not implemented!"); } + } + } + } +} diff --git a/advotracker/src/widgets/ticketdata/ticketdata_view.rs b/advotracker/src/widgets/ticketdata/ticketdata_view.rs new file mode 100644 index 0000000..7eb67d4 --- /dev/null +++ b/advotracker/src/widgets/ticketdata/ticketdata_view.rs @@ -0,0 +1,440 @@ +/* + * advotracker - Hotline tackingtool for Advocats + * + * Copyright 2021 Ralf Zerres + * SPDX-License-Identifier: (0BSD or MIT) + */ + +use orbtk::{ + prelude::*, + //shell::event::Key, +}; + +use crate::{ + data::constants::*, + widgets::ticketdata::ticketdata_state::{TicketdataAction, TicketdataState}, +}; + +// Macro that initializes the widget structures/variables for the policy check view +widget!( + /// Form to enter data of a ticket record + TicketdataView { + lang: String, + policy_data_count: u32, + ticket_data_title: String + } +); + +/// The template implementation of the ticket view +/// All GUI elements are styled using the "style" attribute referencing to a ron based css +impl Template for TicketdataView { + fn template(self, id: Entity, ctx: &mut BuildContext<'_>) -> Self { + let tenent_logo = Container::new() + .margin((16, 16, 0, 0)) + .attach(Grid::column(0)) + .v_align("center") + .child( + ImageWidget::new() + .image("assets/advotracker/hiedemann_logo.png") + .v_align("center") + .build(ctx), + ) + .build(ctx); + + let ticket_data_bottom_bar = Container::new() + .id(ID_TICKET_DATA_BOTTOM_BAR) + .style(STYLE_BOTTOM_BAR) + .attach(Grid::row(4)) + .attach(Grid::column(1)) + .attach(Grid::column_span(2)) + .v_align("end") + .child( + Container::new() + .attach(Grid::column(1)) + .h_align("end") + .v_align("end") + .child( + TextBlock::new() + .margin((0, 9, 48, 0)) + .text("©Networkx GmbH") + .build(ctx) + ) + .build(ctx), + ) + .build(ctx); + + let ticket_data_button_menu = Button::new() + .id(ID_TICKET_DATA_BUTTON_MENU) + .attach(Grid::row(0)) + .attach(Grid::column(2)) + .h_align("end") + .icon(material_icons_font::MD_MENU) + //.style("button_single_content") + .style(STYLE_BUTTON_MENU) + .on_click(move |_ctx, _| { + println!("WIP: open menu popup from MenuView"); + // ctx.get_mut::(id) + // .set_action(MenuAction::CreateMenu(ID_MENU_STACK)); + true + }) + .build(ctx); + + let ticket_data_form = Container::new() + .id(ID_TICKET_DATA_FORM) + .name(ID_TICKET_DATA_FORM) + .attach(Grid::row(2)) + .attach(Grid::column(1)) + .style("container_form") + .child( + Grid::new() + .columns( + Columns::create() + .push("auto") // Label + .push(16) // Delimiter + .push("*") // Data + .push(32) // Delimiter (2x margin) + ) + .rows( + Rows::create() + .push("auto") // Row 0 + .push(14) // Seperator + .push("auto") // Row 2 + .push(14) // Seperator + .push("auto") // Row 4 + .push(14) // Seperator + .push("auto") // Row 6 + .push(14) // Seperator + .push("auto") // Row 8 + .push(14) // Seperator + .push("auto") // Row 10 + .push(14) // Seperator + .push("auto") // Row 12 + .push(14) // Seperator + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_POLICY_NUMBER) + .attach(Grid::row(0)) + .attach(Grid::column(0)) + .text("Policy number") + .h_align("end") + .v_align("center") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_POLICY_NUMBER) + .attach(Grid::row(0)) + .attach(Grid::column(2)) + .text("9999999990") + .v_align("center") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_POLICY_HOLDER) + .attach(Grid::row(2)) + .attach(Grid::column(0)) + .text("Policy holder") + .h_align("end") + .v_align("center") + .build(ctx), + ) + .child( + TextBox::new() + .id(ID_TICKET_DATA_POLICY_HOLDER) + .attach(Grid::row(2)) + .attach(Grid::column(2)) + .water_mark("Name des Versicherungsnehmers") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_DEDUCTIBLE) + .attach(Grid::row(4)) + .attach(Grid::column(0)) + .text("Deductible") + .h_align("end") + .v_align("center") + .build(ctx), + ) + .child( + TextBox::new() + .id(ID_TICKET_DATA_DEDUCTIBLE) + .attach(Grid::row(4)) + .attach(Grid::column(2)) + .water_mark("im Beratungsgespräch erfragen") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_CALLBACK_NUMBER) + .attach(Grid::row(6)) + .attach(Grid::column(0)) + .text("Callback number") + .h_align("end") + .v_align("center") + .build(ctx), + ) + .child( + TextBox::new() + .id(ID_TICKET_DATA_CALLBACK_NUMBER) + .attach(Grid::row(6)) + .attach(Grid::column(2)) + .water_mark("wie zu erreichen") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_CALLBACK_DATE) + .attach(Grid::row(8)) + .attach(Grid::column(0)) + .text("Callback date") + .h_align("end") + .v_align("center") + .build(ctx), + ) + .child( + TextBox::new() + .id(ID_TICKET_DATA_CALLBACK_DATE) + .attach(Grid::row(8)) + .attach(Grid::column(2)) + .water_mark("Rückruf gewünscht um") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_HARM_TYPE) + .attach(Grid::row(10)) + .attach(Grid::column(0)) + .water_mark("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) + .attach(Grid::row(10)) + .attach(Grid::column(2)) + .water_mark("Rechtsproblem/Schadenstyp") + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_IVR_COMMENT) + .attach(Grid::row(12)) + .attach(Grid::column(0)) + .text("IVR comment") + .h_align("end") + .v_align("center") + .build(ctx), + ) + .child( + TextBox::new() + .id(ID_TICKET_DATA_IVR_COMMENT) + .attach(Grid::row(12)) + .attach(Grid::column(2)) + .water_mark("Rechtsrat / Kommentar zu Haftungs, bzw. Deckung") + //.height(48.0) + .build(ctx), + ) + .build(ctx), + ) + .build(ctx); + + let ticket_data_form_action = Container::new() + .id(ID_TICKET_DATA_ACTION_GRID) + .attach(Grid::row(3)) + .attach(Grid::column(1)) + //.style(STYLE_CONTAINER_ACTION) + .padding(14) + .h_align("center") + .child( + Stack::new() + //.style(STYLE_STACK_ACTION) + .orientation("horizontal") + .spacing(50) + .child( + Button::new() + .id(ID_TICKET_DATA_ACTION_BUTTON_CLEAR) + .style(STYLE_BUTTON_ACTION) + .text("Clear") + .on_click(move |states, _| { + println!("WIP: clear from"); + states.send_message(TicketdataAction::ClearForm, id); + true + }) + .build(ctx), + ) + .child( + Button::new() + .id(ID_TICKET_DATA_ACTION_BUTTON_SEND) + .style(STYLE_BUTTON_ACTION) + .text("Send") + //.visibility(Visibility::Collapsed) + .on_click(move |states, _| { + println!("WIP: send mail"); + states.send_message(TicketdataAction::SendForm, id); + true + }) + .build(ctx), + ) + .build(ctx), + ) + .build(ctx); + + let ticket_data_form_mail = Container::new() + .id(ID_TICKET_DATA_CONTAINER_MAIL) + .name(ID_TICKET_DATA_CONTAINER_MAIL) + .attach(Grid::row(1)) + .attach(Grid::column(1)) + .style(STYLE_CONTAINER_MAIL) + .child( + Grid::new() + .columns( + Columns::create() + .push(16) // Delimiter + .push("stretch") // Label + .push(16) // Delimiter + .push("auto") // MailAddress + .push("32") // Delimiter (2x margin) + ) + .rows( + Rows::create() + .push("auto") // Row 0 + .push(2) // Seperator + .push("auto") // Row 2 + .push(2) // Seperator + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_MAIL_TO) + .attach(Grid::row(0)) + .attach(Grid::column(1)) + .style(STYLE_MAIL_LABEL) + .text("Recipient (To)") + .build(ctx), + ) + .child( + ComboBox::new() + .id(ID_TICKET_DATA_COMBO_BOX_MAIL_TO) + .attach(Grid::row(0)) + .attach(Grid::column(3)) + .style(STYLE_MAIL_TO) + .on_changed("selected_item", move |states, _entity| { + states + .get_mut::(id); + }) + .items_builder(move |ictx, index| match index { + 0 => TextBlock::new() + .text(PROP_MAIL_TO_1) + .h_align("start") + .v_align("center") + .build(ictx), + 1 => TextBlock::new() + .text(PROP_MAIL_TO_2) + .h_align("start") + .v_align("center") + .build(ictx), + 2 => TextBlock::new() + .text(PROP_MAIL_TO_3) + .h_align("start") + .v_align("center") + .build(ictx), + 3 => TextBlock::new() + .text(PROP_MAIL_TO_4) + .h_align("start") + .v_align("center") + .build(ictx), + _ => panic!(), + }) + .count(4) + .selected_index(0) + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_TICKET_DATA_LABEL_MAIL_CC) + .attach(Grid::row(2)) + .attach(Grid::column(1)) + .style(STYLE_MAIL_LABEL) + .text("Copie (CC)") + .build(ctx), + ) + .child( + ComboBox::new() + .id(ID_TICKET_DATA_COMBO_BOX_MAIL_CC) + .attach(Grid::row(2)) + .attach(Grid::column(3)) + .style(STYLE_MAIL_CC) + .items_builder(move |ictx, index| match index { + 0 => TextBlock::new() + .text(PROP_MAIL_CC_1) + .h_align("start") + .v_align("center") + .build(ictx), + 1 => TextBlock::new() + .text(PROP_MAIL_CC_2) + .h_align("start") + .v_align("center") + .build(ictx), + _ => panic!(), + }) + .count(2) + .selected_index(0) + .build(ctx), + ) + .build(ctx), + ) + .build(ctx); + + let ticket_data_header_text = TextBlock::new() + .id(ID_TICKET_DATA_HEADER) + .attach(Grid::column(0)) + .style(STYLE_HEADER_TEXT) + .text("Ticket Data") + .build(ctx); + + let ticket_data_header_bar = Container::new() + .id(ID_TICKET_DATA_HEADER_BAR) + .attach(Grid::row(0)) + .attach(Grid::column_span(3)) + .style(STYLE_HEADER_BAR) + .child(tenent_logo) + .child(ticket_data_header_text) + .child(ticket_data_button_menu) + .build(ctx); + + // Widget: Ticket data view + self.id(ID_TICKET_DATA_VIEW) + .name(ID_TICKET_DATA_VIEW) + .min_height(410.0) + .child( + Grid::new() + .id(ID_TICKET_DATA_GRID) + .columns( + Columns::create() + .push(50) // Left margin + .push("*") // Content + .push(50) // Right margin + ) + .rows( + Rows::create() + .push("auto") // Header_Bar + .push("auto") // Mail_Form + .push("*") // Input_Form + .push("auto") // Action + .push("auto") // Bottom_Bar + ) + + .child(ticket_data_header_bar) // row 0 + .child(ticket_data_form_mail) // row 1 + .child(ticket_data_form) // row 2 + .child(ticket_data_form_action) // row 3 + .child(ticket_data_bottom_bar) // row 4 + .build(ctx), + ) + } +}