use std::collections::HashMap; use std::str::FromStr; use std::sync::{Arc}; use bigdecimal::BigDecimal; use egui::mutex::RwLock; use egui_extras::{Column, TableBuilder}; use crate::models::{ModalStatus, ModalWinState, Position, Worker}; use crate::database::*; use crate::tab::*; pub struct WorkerTabViewer { db_oper: DBOperator, workers: Arc>>>, positions: Arc>>, show_worker_add_modal: bool, show_worker_edit_modal: bool, show_position_add_modal: bool, fire_worker_id: String, add_worker_name: String, add_worker_hire_date: String, add_worker_position: Position, can_add_worker: bool, can_edit_worker: bool, add_position_name: String, add_position_wage: String, edit_worker_id: i32, edit_worker_name: String, edit_worker_hire_date: String, edit_worker_is_fired: bool, edit_worker_position: Position, rt: tokio::runtime::Runtime, show_position_delete_modal: bool, delete_position: Position, process_position_state: ModalWinState, } impl WorkerTabViewer { fn update_workers(&mut self) { self.workers = Arc::new(RwLock::new(Option::Some( self.rt .block_on(async { self.db_oper.get_workers().await.unwrap() }), ))); } fn update_positions(&mut self) { self.positions = Arc::new(RwLock::new( self.rt .block_on(async { self.db_oper.get_position().await.unwrap() }), )); } fn show_worker(&mut self, ui: &mut egui::Ui) { ui.horizontal(|ui| { if ui.button("Добавить").clicked() { self.show_worker_add_modal = true; self.add_worker_name = String::new(); self.add_worker_position = Position::default(); }; if ui.button("Редактировать").clicked() { self.show_worker_edit_modal = true; self.rt.block_on(async { let wrkr = match self.db_oper.get_worker_by_id(self.edit_worker_id).await { Ok(worker) => worker, Err(_) => Worker::default(), }; self.edit_worker_name = wrkr.full_name; self.edit_worker_hire_date = wrkr.hire_date.format("%Y-%m-%d").to_string(); self.edit_worker_position = wrkr.position; self.edit_worker_is_fired = wrkr.is_fired; }); } if ui.button("Обновить").clicked() { self.update_workers(); } }); if self.show_worker_add_modal { egui::Modal::new("add_worker".into()).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.show_worker_add_modal = false; } ui.horizontal(|ui| { ui.vertical(|ui| { ui.label("Имя сотрудника:"); ui.label("Дата найма:"); ui.label("Должность:"); }); ui.vertical(|ui| { ui.add( egui::TextEdit::singleline(&mut self.add_worker_name) .hint_text("Напр: Иванов Иван"), ); ui.add( egui::TextEdit::singleline(&mut self.add_worker_hire_date) .hint_text("Напр: 12-02-2010"), ); egui::ComboBox::from_label("Выбрать!") .selected_text(format!("{}", self.add_worker_position.name)) .show_ui(ui, |ui| { self.rt.block_on(async { for pos in self.db_oper.get_position().await.unwrap().iter() { ui.selectable_value( &mut self.add_worker_position, pos.clone(), &pos.name, ); } }); }); }); }); if chrono::NaiveDate::parse_from_str(&self.add_worker_hire_date, "%d-%m-%Y") .is_err() { ui.label(egui::RichText::new("ДАТА УКАЗАНА НЕВЕРНО").color(egui::Color32::RED)); self.can_add_worker = false; } else { self.can_add_worker = true; } ui.vertical_centered(|ui| { let resp = ui.add_enabled(self.can_add_worker, egui::Button::new("Добавить")); if resp.clicked() { self.rt.block_on(async { let ndate = chrono::NaiveDate::parse_from_str( &self.add_worker_hire_date, "%d-%m-%Y", ) .unwrap(); self.db_oper .add_worker(Worker { id: 0, full_name: self.add_worker_name.clone(), hire_date: ndate .and_hms_opt(0, 0, 0) .unwrap() .and_local_timezone(chrono::Local) .unwrap(), position: self.add_worker_position.clone(), is_fired: false, }) .await .ok(); self.show_worker_add_modal = false; }); self.update_workers(); } }); }); } if self.show_worker_edit_modal { egui::Modal::new("edit_worker".into()).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.show_worker_edit_modal = false; } let mut size = ui.spacing().interact_size; size.x = 200.0; egui::Grid::new("edit_worker_grid").show(ui, |ui| { ui.label("ID"); let old = self.edit_worker_id.clone(); egui::ComboBox::from_id_salt("edit_worker_id") .selected_text(format!("#{:08}", &self.edit_worker_id)) .show_ui(ui, |ui| { self.rt.block_on(async { for worker in self.db_oper.get_workers().await.unwrap().iter() { ui.selectable_value( &mut self.edit_worker_id, worker.id, format!("#{:08} | {}", worker.id, worker.full_name), ); } }); }); if self.edit_worker_id != old { self.rt.block_on(async { let wrkr = match self.db_oper.get_worker_by_id(self.edit_worker_id).await { Ok(worker) => worker, Err(_) => Worker::default(), }; self.edit_worker_name = wrkr.full_name; self.edit_worker_hire_date = wrkr.hire_date.format("%Y-%m-%d").to_string(); self.edit_worker_position = wrkr.position; self.edit_worker_is_fired = wrkr.is_fired; }); } ui.end_row(); ui.label("Имя"); ui.add_sized(size, |ui: &mut egui::Ui| { ui.text_edit_singleline(&mut self.edit_worker_name) }); ui.end_row(); ui.label("Должность"); egui::ComboBox::from_id_salt("edit_worker_pos") .selected_text(&self.edit_worker_position.name) .show_ui(ui, |ui| { self.rt.block_on(async { for pos in self.db_oper.get_position().await.unwrap().iter() { ui.selectable_value( &mut self.edit_worker_position, pos.clone(), &pos.name, ); } }); }); ui.end_row(); ui.label("Дата найма"); ui.text_edit_singleline(&mut self.edit_worker_hire_date); ui.end_row(); ui.checkbox(&mut self.edit_worker_is_fired, "Уволен"); ui.end_row(); }); if chrono::NaiveDate::parse_from_str(&self.edit_worker_hire_date, "%Y-%m-%d") .is_ok() && self.edit_worker_name.len() > 0 { self.can_edit_worker = true; } else { self.can_edit_worker = false; } let resp = ui.add_enabled(self.can_edit_worker, egui::Button::new("Добавить")); if resp.clicked() { self.rt.block_on(async { let ndate = chrono::NaiveDate::parse_from_str( &self.edit_worker_hire_date, "%Y-%m-%d", ) .unwrap(); self.db_oper .update_worker(Worker { id: self.edit_worker_id, full_name: self.edit_worker_name.clone(), hire_date: ndate .and_hms_opt(0, 0, 0) .unwrap() .and_local_timezone(chrono::Local) .unwrap(), position: self.edit_worker_position.clone(), is_fired: self.edit_worker_is_fired, }) .await .unwrap(); }); self.update_workers(); self.show_worker_edit_modal = false; } }); } TableBuilder::new(ui) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .sense(egui::Sense::click()) .header(30.0, |mut header| { header.col(|ui| { ui.heading("Имя"); }); header.col(|ui| { ui.heading("Дата найма"); }); header.col(|ui| { ui.heading("Должность"); }); header.col(|ui| { ui.heading("Уволен"); }); }) .body(|mut body| { body.ui_mut().style_mut().interaction.selectable_labels = false; for wk in self.workers.read().clone().unwrap().iter() { body.row(25.0, |mut row| { row.col(|ui| { ui.label(&wk.full_name); }); row.col(|ui| { ui.label(wk.hire_date.date_naive().format("%d-%m-%Y").to_string()); }); row.col(|ui| { ui.label(&wk.position.name); }); row.col(|ui| { ui.label(if wk.is_fired { "Да" } else { "Нет" }); }); row.col(|ui| { if ui.button("Уволить").clicked() { todo!(); } }); }); } }); } fn show_position(&mut self, ui: &mut egui::Ui) { ui.horizontal(|ui| { if ui.button("Добавить").clicked() { // self.show_position_add_modal = true; self.process_position_state.is_open = true; self.process_position_state.status = ModalStatus::Add; self.process_position_state .data .insert("id".into(), "0".to_string()); self.process_position_state .data .insert("name".into(), String::new()); self.process_position_state .data .insert("wage".into(), String::from("0")); } if ui.button("Обновить").clicked() { self.update_positions(); } }); TableBuilder::new(ui) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .sense(egui::Sense::click()) .header(30.0, |mut header| { header.col(|ui| { ui.heading("Название"); }); header.col(|ui| { ui.heading("Оклад"); }); }) .body(|mut body| { body.ui_mut().style_mut().interaction.selectable_labels = false; for pos in self.positions.read().clone().iter() { body.row(20.0, |mut row| { row.col(|ui| { ui.label(pos.name.clone()); }); row.col(|ui| { ui.label(format!("${}", pos.wage)); }); row.col(|ui| { let resp = ui.add(egui::Button::new("Удалить")); if resp.clicked() { self.process_position_state.is_open = true; self.process_position_state.status = ModalStatus::Remove; self.process_position_state .data .insert("id".into(), pos.id.to_string()); self.process_position_state .data .insert("name".into(), pos.name.clone()); self.process_position_state .data .insert("wage".into(), pos.wage.to_string()); } }); if row.response().double_clicked() { self.process_position_state.is_open = true; self.process_position_state.status = ModalStatus::Edit; self.process_position_state .data .insert("id".into(), pos.id.to_string()); self.process_position_state .data .insert("name".into(), pos.name.clone()); self.process_position_state .data .insert("wage".into(), pos.wage.to_string()); } }); } }); if self.process_position_state.is_open { egui::Modal::new(egui::Id::new("process_worker_pos_modal")).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.process_position_state.is_open = false; } if self.process_position_state.status != ModalStatus::Remove { egui::Grid::new("process_worker_pos_grid").show(ui, |ui| { ui.label("Название"); let mut name = self.process_position_state.data.get_mut("name").unwrap(); let mut spacing = ui.spacing().interact_size; spacing.x = 150.0; ui.add_sized(spacing, |ui: &mut egui::Ui| ui.text_edit_singleline(name)); ui.end_row(); ui.label("Оклад"); let mut wage = self.process_position_state.data.get_mut("wage").unwrap(); ui.text_edit_singleline(wage); }); } else { ui.label("Вы уверены, что хотите удалить:"); ui.label(format!( "{}?", self.process_position_state.data["name"].clone() )); } let btext: String; match self.process_position_state.status { ModalStatus::Add => { btext = String::from("Добавить"); self.process_position_state.can_finish = true; } ModalStatus::Edit => { btext = String::from("Сохранить"); self.process_position_state.can_finish = true; } ModalStatus::Remove => { self.process_position_state.can_finish = self .workers .read() .clone() .unwrap() .iter() .find(|wrkr| wrkr.position.id == self.delete_position.id) .iter() .len() == 0; btext = String::from("Удалить"); } } let button = ui.add_enabled( self.process_position_state.can_finish, egui::Button::new(btext), ); if button.clicked() || ui.input(|i| i.key_pressed(egui::Key::Enter)) { match self.process_position_state.status { ModalStatus::Add => { let pos = self.process_position_state.data.clone(); self.rt.block_on(async { self.db_oper .add_position(Position { id: pos["id"].parse::().unwrap(), name: pos["name"].clone(), wage: bigdecimal::BigDecimal::from_str( pos["wage"].as_str(), ) .unwrap(), }) .await; }) } ModalStatus::Edit => { let pos = self.process_position_state.data.clone(); self.rt.block_on(async { self.db_oper .update_position(Position { id: pos["id"].parse::().unwrap(), name: pos["name"].clone(), wage: bigdecimal::BigDecimal::from_str( pos["wage"].as_str(), ) .unwrap(), }) .await; }) } ModalStatus::Remove => { let pos = self.process_position_state.data.clone(); self.rt.block_on(async { self.db_oper .delete_position(Position { id: pos["id"].parse::().unwrap(), name: String::new(), wage: BigDecimal::from(0), }) .await; }); } } self.update_positions(); self.process_position_state.is_open = false; } }); } } } impl egui_dock::TabViewer for WorkerTabViewer { type Tab = Tab; fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText { (&*tab.title).into() } fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) { match &tab.tab_type { TabTypes::WorkerList => { self.show_worker(ui) }, TabTypes::WorkerPosition => self.show_position(ui), _ => { ui.label("Nope"); } } } fn id(&mut self, tab: &mut Self::Tab) -> egui::Id { egui::Id::new(&tab.title) } fn allowed_in_windows(&self, _tab: &mut Self::Tab) -> bool { TABS_CAN_BE_WINDOWS } fn is_closeable(&self, _tab: &Self::Tab) -> bool { false } } impl Default for WorkerTabViewer { fn default() -> Self { let rt = tokio::runtime::Runtime::new().unwrap(); let db_oper = rt.block_on(async { DBOperator::new().await }); let positions = Arc::new(RwLock::new( rt.block_on(async { db_oper.get_position().await.unwrap() }), )); let mut pos_map: HashMap = HashMap::new(); pos_map.extend(|| -> Vec<(String, String)> { let pos = positions.read()[0].clone(); vec![ ("id".to_string(), pos.id.to_string()), ("name".to_string(), pos.name.clone()), ("wage".to_string(), pos.wage.to_string()), ] }()); Self { process_position_state: ModalWinState { is_open: false, can_finish: false, data: pos_map, status: ModalStatus::Add, }, workers: Arc::new(RwLock::new(Option::Some( rt.block_on(async { db_oper.get_workers().await.unwrap() }), ))), positions: positions, show_worker_add_modal: false, show_worker_edit_modal: false, show_position_add_modal: false, show_position_delete_modal: false, fire_worker_id: String::new(), add_worker_name: String::new(), add_worker_position: Position::default(), add_worker_hire_date: String::new(), can_add_worker: false, can_edit_worker: false, add_position_name: String::new(), add_position_wage: String::new(), delete_position: Position::default(), edit_worker_id: 1, edit_worker_name: String::new(), edit_worker_hire_date: String::new(), edit_worker_position: Position::default(), edit_worker_is_fired: false, db_oper, rt, } } }