use std::{collections::HashMap, ops::Deref, sync::Arc}; use crate::{ database::DBOperator, models::{ Equipment, Material, MaterialCategory, MaterialRow, ModalStatus, ModalWinState, Position, Worker, }, }; use egui::{TextBuffer, mutex::RwLock}; use egui_dock::{DockArea, Style, TabViewer}; use egui_extras::{Column, TableBuilder}; use sqlx::types::BigDecimal; static TABS_CAN_BE_WINDOWS: bool = false; enum TabTypes { Equipment, Worker, Position, Salary, Material, Recipe, Product, Order, Settings, WorkerList, WorkerPosition, MaterialList, MaterialTypeList, RecipeList, RecipeDetail, ProductList, } struct Tab { tab_type: TabTypes, title: String, } pub struct App { pub tree: egui_dock::DockState, pub main_viewer: MainTabViewer, } impl Default for App { fn default() -> Self { let tree = egui_dock::DockState::new(vec![ Tab { tab_type: TabTypes::Equipment, title: "Оборудование".to_owned(), }, Tab { tab_type: TabTypes::Material, title: "Сырьё".to_owned(), }, Tab { tab_type: TabTypes::Worker, title: "Сотрудники".to_owned(), }, Tab { tab_type: TabTypes::Salary, title: "Выплаты".to_owned(), }, Tab { tab_type: TabTypes::Recipe, title: "Рецепты".to_owned(), }, Tab { tab_type: TabTypes::Product, title: "Продукция".to_owned(), }, Tab { tab_type: TabTypes::Settings, title: "Настройки".to_owned(), }, ]); let main_viewer = MainTabViewer::default(); Self { tree, main_viewer } } } impl App { pub fn new(_cc: &eframe::CreationContext<'_>) -> Self { Default::default() } } impl eframe::App for App { fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { DockArea::new(&mut self.tree) .style(Style::from_egui(ui.style().as_ref())) .show_inside(ui, &mut self.main_viewer); } fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { if self.main_viewer.is_dark_theme { ctx.set_visuals(egui::Visuals::dark()); } else { ctx.set_visuals(egui::Visuals::light()); } ctx.set_zoom_factor(self.main_viewer.interface_scale_ratio); } } struct MainTabViewer { positions: Arc>>>, workers: Arc>>>, equipment: Arc>>>, db_oper: DBOperator, worker_tabs: WorkerTabViewer, worker_tree: egui_dock::DockState, material_tabs: MaterialTabViewer, material_tree: egui_dock::DockState, rt: tokio::runtime::Runtime, is_dark_theme: bool, interface_scale_ratio: f32, } impl Default for MainTabViewer { fn default() -> Self { let rt = tokio::runtime::Runtime::new().unwrap(); let db_oper = rt.block_on(async { DBOperator::new().await }); Self { positions: Arc::new(RwLock::new(Option::Some( rt.block_on(async { db_oper.get_position().await.unwrap() }), ))), workers: Arc::new(RwLock::new(Option::Some( rt.block_on(async { db_oper.get_workers().await.unwrap() }), ))), equipment: Arc::new(RwLock::new(Option::Some( rt.block_on(async { db_oper.get_equipment().await.unwrap() }), ))), db_oper, worker_tabs: WorkerTabViewer::default(), worker_tree: egui_dock::DockState::new(vec![ Tab { title: "Работники".to_owned(), tab_type: TabTypes::WorkerList, }, Tab { title: "Должности".to_owned(), tab_type: TabTypes::WorkerPosition, }, ]), material_tabs: MaterialTabViewer::default(), material_tree: egui_dock::DockState::new(vec![ Tab { title: "Сырьё".to_owned(), tab_type: TabTypes::MaterialList, }, Tab { title: "Тип".to_owned(), tab_type: TabTypes::MaterialTypeList, }, ]), rt, is_dark_theme: true, interface_scale_ratio: 1.2, } } } impl MainTabViewer { fn show_equipment(&mut self, ui: &mut egui::Ui) { let mut table = TableBuilder::new(ui) .striped(true) .resizable(false) .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .header(30.0, |mut header| { header.col(|ui| { ui.heading("ID"); }); header.col(|ui| { ui.heading("Название"); }); header.col(|ui| { ui.heading("Инв. номер"); }); header.col(|ui| { ui.heading("Ответственный"); }); header.col(|ui| { ui.heading("Дата последнего\nя техобслуживания"); }); }) .body(|mut body| { for eq in self.equipment.read().clone().unwrap().iter() { body.row(20.0, |mut row| { row.col(|ui| { ui.label(eq.id.to_string()); }); row.col(|ui| { ui.label(&eq.name); }); row.col(|ui| { ui.label(&eq.inv_number); }); row.col(|ui| { ui.label(&eq.worker.full_name); }); row.col(|ui| { ui.label(eq.maintenance_date.date_naive().to_string()); }); }); } }); } fn show_worker(&mut self, ui: &mut egui::Ui) { let id = ui.make_persistent_id("WorkerMenu"); DockArea::new(&mut self.worker_tree) .style(Style::from_egui(ui.style().as_ref())) .id(id) .show_inside(ui, &mut self.worker_tabs); } fn show_material(&mut self, ui: &mut egui::Ui) { let id = ui.make_persistent_id("MaterialMenu"); DockArea::new(&mut self.material_tree) .id(id) .show_inside(ui, &mut self.material_tabs); } fn show_salary(&mut self, ui: &mut egui::Ui) { ui.label("Помогите"); } fn show_settings(&mut self, ui: &mut egui::Ui) { ui.checkbox(&mut self.is_dark_theme, "Тёмная тема"); ui.add( egui::Slider::new(&mut self.interface_scale_ratio, 1.0..=2.0) .text("Масштаб интерфейса"), ); if ui.button("Сбросить Масштаб интерфейса").clicked() { self.interface_scale_ratio = 1.2; } } } impl egui_dock::TabViewer for MainTabViewer { 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::Equipment => { &self.show_equipment(ui); } TabTypes::Worker => { &self.show_worker(ui); } TabTypes::Salary => { &self.show_salary(ui); } TabTypes::Material => { &self.show_material(ui); } TabTypes::Settings => { &self.show_settings(ui); } _ => { ui.label("This is not"); } } } fn allowed_in_windows(&self, _tab: &mut Self::Tab) -> bool { TABS_CAN_BE_WINDOWS } fn is_closeable(&self, _tab: &Self::Tab) -> bool { false } fn id(&mut self, tab: &mut Self::Tab) -> egui::Id { egui::Id::new(&tab.title) } } 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, } 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(Option::Some( 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; } }); } for wk in self.workers.read().clone().unwrap().iter() { ui.push_id(format!("{}:{}", &wk.full_name, &wk.id), |ui| { egui::CollapsingHeader::new(format!("#{:08} | {}", &wk.id, &wk.full_name)) .default_open(false) .show(ui, |ui| { ui.horizontal(|ui| { ui.label("Нанят: "); ui.label(format!( "{}", wk.hire_date.date_naive().format("%d-%m-%Y").to_string() )); }); ui.horizontal(|ui| { ui.label("Должность: "); ui.label(&wk.position.name); }); ui.horizontal(|ui| { ui.label("Уволен: "); ui.label(if wk.is_fired { "Да" } else { "Нет" }) }) }) }); } } fn show_position(&mut self, ui: &mut egui::Ui) { ui.horizontal(|ui| { if ui.button("Добавить").clicked() { self.show_position_add_modal = true; } if ui.button("Удалить").clicked() { self.show_position_delete_modal = true; self.delete_position = self.positions.read().clone().unwrap()[0].clone(); self.update_positions(); self.update_workers(); } if ui.button("Обновить").clicked() { self.update_positions(); } }); for eq in self.positions.read().clone().unwrap().iter() { ui.push_id(&eq.name, |ui| { egui::CollapsingHeader::new(&eq.name) .default_open(false) .show(ui, |ui| { ui.horizontal(|ui| { ui.monospace("Оклад:"); ui.label(format!("${}", eq.wage)); }); }) }); } if self.show_position_add_modal { egui::Modal::new("add_position".into()).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.show_position_add_modal = false; } let mut size = ui.spacing().interact_size; size.x = 200.0; egui::Grid::new("add_pos_grid").show(ui, |ui| { ui.label("Название"); ui.add_sized(size, |ui: &mut egui::Ui| { ui.text_edit_singleline(&mut self.add_position_name) }); ui.end_row(); ui.label("Оклад"); ui.text_edit_singleline(&mut self.add_position_wage); ui.end_row(); }); let resp = ui.add_enabled( self.add_position_name.len() > 0 && self.add_position_wage.len() > 0, egui::Button::new("Добавить"), ); if resp.clicked() { self.rt.block_on(async { self.db_oper .add_position(Position { id: 0, name: self.add_position_name.clone(), wage: BigDecimal::from( self.add_position_wage.trim().parse::().unwrap(), ), }) .await .ok(); }); self.update_positions(); self.show_position_add_modal = false; } }); } if self.show_position_delete_modal { egui::Modal::new("delete_position".into()).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.show_position_delete_modal = false; } egui::ComboBox::from_label("Должность") .selected_text(&self.delete_position.name) .show_ui(ui, |ui| { self.rt.block_on(async { for pos in self.positions.read().clone().unwrap().iter() { ui.selectable_value( &mut self.delete_position, pos.clone(), &pos.name, ); } }); }); let can_delete = self .workers .read() .clone() .unwrap() .iter() .find(|wrkr| wrkr.position.id == self.delete_position.id) .iter() .len() == 0; if !can_delete { ui.label( egui::RichText::new("Есть сотрудники с этой должностью") .color(egui::Color32::YELLOW), ); } let resp = ui.add_enabled(can_delete, egui::Button::new("Удалить")); if resp.clicked() { self.rt.block_on(async { self.db_oper .delete_position(self.delete_position.clone()) .await .unwrap(); }); } }); } } } 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 }); Self { workers: Arc::new(RwLock::new(Option::Some( rt.block_on(async { db_oper.get_workers().await.unwrap() }), ))), positions: Arc::new(RwLock::new(Option::Some( rt.block_on(async { db_oper.get_position().await.unwrap() }), ))), 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, } } } struct MaterialTabViewer { db_oper: DBOperator, rt: tokio::runtime::Runtime, process_mcat_state: ModalWinState, process_material_state: ModalWinState, mat_cats: Arc>>, mats: Arc>>, } impl MaterialTabViewer { fn update_material_category(&mut self) { self.mat_cats = Arc::new(RwLock::new( self.rt .block_on(async { self.db_oper.get_mcat().await.unwrap() }), )); } fn update_materials(&mut self) { self.mats = Arc::new(RwLock::new( self.rt .block_on(async { self.db_oper.get_materials().await.unwrap() }), )); } fn show_material(&mut self, ui: &mut egui::Ui) { ui.horizontal(|ui| { if ui.button("Добавить").clicked() { self.process_material_state.is_open = true; self.process_material_state.status = ModalStatus::Add; } if ui.button("Редактировать").clicked() { self.process_material_state.is_open = true; self.process_material_state.status = ModalStatus::Edit; } if ui.button("Удалить").clicked() { self.process_material_state.is_open = true; self.process_material_state.status = ModalStatus::Remove; } if ui.button("Обновить").clicked() { self.update_materials(); } }); TableBuilder::new(ui) .striped(true) .cell_layout(egui::Layout::centered_and_justified( egui::Direction::LeftToRight, )) .vscroll(true) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .column(Column::auto()) .header(20.0, |mut header| { header.col(|ui| { ui.heading("ID"); }); header.col(|ui| { ui.heading("Название"); }); header.col(|ui| { ui.heading("Тип"); }); header.col(|ui| { ui.heading("Количество"); }); }) .body(|mut body| { self.rt.block_on(async { for mat in self.mats.read().clone().iter() { body.row(30.0, |mut row| { row.col(|ui| { ui.label(mat.id.to_string()); }); row.col(|ui| { ui.label(&mat.name); }); row.col(|ui| { ui.label(&mat.category.name); }); row.col(|ui| { ui.label(mat.quantity.to_string()); }); }); } }) }); if self.process_material_state.is_open { egui::Modal::new("process_material".into()).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.process_material_state.is_open = false; } egui::Grid::new("process_material_grid").show(ui, |ui| { let old = self.process_material_state.data.get("id").unwrap().clone(); if self.process_material_state.status != ModalStatus::Add { ui.label("Материал"); let mats = self.mats.read().clone(); let sel_id = self .process_material_state .data .get("id") .map(|d| d.as_str()) .unwrap_or(""); let cur_name = mats .iter() .find(|m| m.id.to_string() == sel_id) .map(|m| m.name.clone()) .unwrap_or_default(); egui::ComboBox::from_id_salt("process_material_combobox_mat") .selected_text(cur_name) .show_ui(ui, |ui| { for mat in self.mats.read().iter().clone() { ui.selectable_value( self.process_material_state.data.get_mut("id").unwrap(), mat.id.to_string(), &mat.name, ); } let id = self .process_material_state .data .get_mut("id") .unwrap() .clone(); if id != old { self.process_material_state.data.insert( "name".to_owned(), mats.iter() .find(|m| m.id.to_string() == id) .unwrap() .name .clone(), ); self.process_material_state.data.insert( "quantity".to_owned(), mats.iter() .find(|m| m.id.to_string() == id) .unwrap() .quantity .to_string(), ); self.process_material_state.data.insert( "category_id".to_owned(), mats.iter() .find(|m| m.id.to_string() == id) .unwrap() .category .id .to_string(), ); } }); ui.end_row(); } let old = self .process_material_state .data .get("category_id") .unwrap() .clone(); if self.process_material_state.status != ModalStatus::Remove { ui.label("Название"); ui.text_edit_singleline( self.process_material_state.data.get_mut("name").unwrap(), ); ui.end_row(); ui.label("Количество"); ui.text_edit_singleline( self.process_material_state .data .get_mut("quantity") .unwrap(), ); ui.end_row(); ui.label("Тип"); let mats = self.mat_cats.read().clone(); let sel_id = self .process_material_state .data .get("category_id") .map(|d| d.as_str()) .unwrap_or_default(); let cur_name = mats .iter() .find(|m| m.id.to_string() == sel_id) .map(|m| m.name.clone()) .unwrap_or_default(); egui::ComboBox::from_id_salt("process_material_combobox_cat") .selected_text(cur_name) .show_ui(ui, |ui| { for mat in self.mat_cats.read().iter() { ui.selectable_value( self.process_material_state .data .get_mut("category_id") .unwrap(), mat.id.to_string(), &mat.name.clone(), ); } }); ui.end_row(); } }); match self.process_material_state.status { ModalStatus::Add => { let resp = ui.add_enabled( self.process_material_state.data.get("name").unwrap().len() > 0 && self .process_material_state .data .get("quantity") .unwrap() .len() > 0, egui::Button::new("Добавить"), ); if resp.clicked() { self.rt.block_on(async { self.db_oper .add_material(MaterialRow { id: self .process_material_state .data .get("id") .unwrap() .parse::() .unwrap(), name: self .process_material_state .data .get("name") .unwrap() .to_string(), quantity: self .process_material_state .data .get("quantity") .unwrap() .parse::() .unwrap(), category_id: self .process_material_state .data .get("category_id") .unwrap() .parse::() .unwrap(), }) .await .unwrap(); }); self.update_materials(); self.process_material_state.is_open = false; } } ModalStatus::Edit => { if ui.button("Сохранить").clicked() { self.rt.block_on(async { self.db_oper .update_material(MaterialRow { id: self .process_material_state .data .get("id") .unwrap() .parse::() .unwrap(), name: self .process_material_state .data .get("name") .unwrap() .to_string(), quantity: self .process_material_state .data .get("quantity") .unwrap() .parse::() .unwrap(), category_id: self .process_material_state .data .get("category_id") .unwrap() .parse::() .unwrap(), }) .await .unwrap(); }); self.update_materials(); self.process_material_state.is_open = false; } } ModalStatus::Remove => { if ui.button("Удалить").clicked() { self.rt.block_on(async { self.db_oper .delete_material(MaterialRow { id: self .process_material_state .data .get("id") .unwrap() .parse::() .unwrap(), name: "".to_owned(), quantity: 0, category_id: 0, }) .await .unwrap(); }); self.update_materials(); self.process_material_state.is_open = false; } } } }); } } fn show_material_type(&mut self, ui: &mut egui::Ui) { ui.horizontal(|ui| { if ui.button("Добавить").clicked() { self.process_mcat_state.is_open = true; self.process_mcat_state.status = ModalStatus::Add; } if ui.button("Редактировать").clicked() { self.process_mcat_state.is_open = true; self.process_mcat_state.status = ModalStatus::Edit; } if ui.button("Удалить").clicked() { self.process_mcat_state.is_open = true; self.process_mcat_state.status = ModalStatus::Remove; } if ui.button("Обновить").clicked() { self.update_material_category(); } }); if self.process_mcat_state.is_open { egui::Modal::new("process_mcat".into()).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.process_mcat_state.is_open = false; } egui::Grid::new("process_mcat_grid").show(ui, |ui| { let old = self.process_mcat_state.data.get("id").unwrap().clone(); let iterr = self.mat_cats.read().clone(); let selected_id = self .process_mcat_state .data .get("id") .map(|s| s.as_str()) .unwrap_or(""); let current_name = iterr .iter() .find(|mc| mc.id.to_string() == *selected_id) .map(|mc| mc.name.as_str()) .unwrap_or("Выбрать"); if self.process_mcat_state.status != ModalStatus::Add { ui.label("Тип"); egui::ComboBox::new("process_mcat_grid_selector", "Выбрать") .selected_text(current_name) .show_ui(ui, |ui| { self.rt.block_on(async { for mcat in iterr { ui.selectable_value( self.process_mcat_state.data.get_mut("id").unwrap(), mcat.id.to_string(), format!("{}|{}", mcat.id, &mcat.name), ); } }); }); ui.end_row(); } let current_id = self.process_mcat_state.data.get("id").unwrap().clone(); if self.process_mcat_state.data.get("id").unwrap() != &old { self.process_mcat_state.data.insert( "name".to_owned(), self.mat_cats .read() .iter() .find(|mc| mc.id.to_string() == current_id) .unwrap() .name .clone(), ); } if self.process_mcat_state.status != ModalStatus::Remove { ui.label("Название"); let mut name = self.process_mcat_state.data.get_mut("name").unwrap(); ui.text_edit_singleline(name); ui.end_row(); } match self.process_mcat_state.status { ModalStatus::Add => { if ui.button("Добавить").clicked() { let name = self.process_mcat_state.data.get("name").unwrap().clone(); self.rt.block_on(async { self.db_oper .add_material_category(MaterialCategory { id: 0, name }) .await .unwrap(); }); self.update_material_category(); self.process_mcat_state.is_open = false; } } ModalStatus::Edit => { if ui.button("Сохранить").clicked() { let id = self.process_mcat_state.data.get("id").unwrap().clone(); let name = self.process_mcat_state.data.get("name").unwrap().clone(); self.rt.block_on(async { self.db_oper .update_material_category(MaterialCategory { id: id.parse::().unwrap(), name: name.clone(), }) .await .unwrap() }); println!("{}", &name); self.update_material_category(); self.process_mcat_state.is_open = false; } } ModalStatus::Remove => { if ui.button("Удалить").clicked() { let idd = self.process_mcat_state.data.get("id").unwrap().clone(); self.rt.block_on(async { self.db_oper .delete_material_category(MaterialCategory { id: idd.parse::().unwrap(), name: "".to_owned(), }) .await .unwrap() }); self.update_material_category(); self.process_mcat_state.is_open = false; } } } }); }); } TableBuilder::new(ui) .striped(true) .column(Column::auto()) .column(Column::auto()) .header(20.0, |mut header| { header.col(|ui| { ui.heading("ID"); }); header.col(|ui| { ui.heading("Название"); }); }) .body(|mut body| { self.rt.block_on(async { for mcat in self.mat_cats.read().clone().iter() { body.row(20.0, |mut row| { row.col(|ui| { ui.label(mcat.id.to_string()); }); row.col(|ui| { ui.label(&mcat.name); }); }); } }); }); } } impl egui_dock::TabViewer for MaterialTabViewer { 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::MaterialList => self.show_material(ui), TabTypes::MaterialTypeList => self.show_material_type(ui), _ => { ui.label("Каким образом?"); } } } 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 MaterialTabViewer { fn default() -> Self { let rt = tokio::runtime::Runtime::new().unwrap(); let db_oper = rt.block_on(async { DBOperator::new().await }); let mat_cats = Arc::new(RwLock::new( rt.block_on(async { db_oper.get_mcat().await.unwrap() }), )); let mats = Arc::new(RwLock::new( rt.block_on(async { db_oper.get_materials().await.unwrap() }), )); Self { db_oper, rt, process_mcat_state: ModalWinState { data: HashMap::from([ ( String::from("id"), mat_cats.clone().read().clone()[0].id.to_string(), ), ( String::from("name"), mat_cats.clone().read().clone()[0].name.clone(), ), ]), status: ModalStatus::Add, ..Default::default() }, process_material_state: ModalWinState { data: HashMap::from([ ( "id".to_owned(), mats.clone().read().clone()[0].id.to_string(), ), ( "name".to_owned(), mats.clone().read().clone()[0].name.clone(), ), ( "quantity".to_owned(), mats.clone().read().clone()[0].quantity.to_string(), ), ("category_id".to_owned(), "1".to_owned()), ]), ..Default::default() }, mat_cats, mats, } } } struct RecipeTabViewer {} impl RecipeTabViewer { fn show_recipe_list(&mut self, ui: &mut egui::Ui) {} fn show_recipe_detail(&mut self, ui: &mut egui::Ui) {} } impl egui_dock::TabViewer for RecipeTabViewer { type Tab = Tab; fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText { tab.title.clone().into() } fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) { match &tab.tab_type { TabTypes::RecipeList => { self.show_recipe_list(ui); } TabTypes::RecipeDetail => { self.show_recipe_detail(ui); } _ => { ui.label("Hello!"); } } } }