use std::{collections::HashMap, default::Default, ops::Deref, str::FromStr, sync::Arc}; use crate::{ database::DBOperator, material_tab::MaterialTabViewer, models::{ Equipment, LoginModalState, Material, MaterialCategory, MaterialRow, ModalStatus, ModalWinState, Position, Worker, }, tab::*, worker_tab::{self, *}, }; use egui::{TextBuffer, Vec2, mutex::RwLock}; use egui_dock::{DockArea, Style, TabViewer}; use egui_extras::{Column, TableBuilder}; use sha2::{Digest, Sha256}; use sqlx::types::BigDecimal; pub struct App<'a> { pub login_modal_state: LoginModalState<'a>, pub tree: egui_dock::DockState, pub main_viewer: MainTabViewer, pub db_oper: DBOperator, pub rt: tokio::runtime::Runtime, pub is_admin: bool, } impl Default for App<'_> { fn default() -> Self { let mut 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(); let rt = tokio::runtime::Runtime::new().unwrap(); Self { tree, main_viewer, login_modal_state: LoginModalState { is_open: true, data: HashMap::from([("login", String::new()), ("password", String::new())]), status: crate::models::LoginStatus::Login, ..Default::default() }, db_oper: rt.block_on(DBOperator::new()), rt, is_admin: false, } } } 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) { if self.login_modal_state.is_open { egui::Modal::new(egui::Id::new("login_modal")).show(ui.ctx(), |ui| { ui.label("Пройдите авторизацию:"); ui.add( egui::TextEdit::singleline( self.login_modal_state.data.get_mut("login").unwrap(), ) .hint_text("Введите логин"), ); ui.add( egui::TextEdit::singleline( self.login_modal_state.data.get_mut("password").unwrap(), ) .password(true) .hint_text("Пароль"), ); if self.login_modal_state.fail { ui.label(&self.login_modal_state.fail_message); } ui.vertical_centered(|ui| { if ui.button("Авторизоваться").clicked() || ui.input(|i| i.key_pressed(egui::Key::Enter)) { let uname = self.login_modal_state.data.get("login").unwrap().clone(); self.login_modal_state.fail = false; let mut hashed_pass = Sha256::new(); hashed_pass.update(self.login_modal_state.data.get("password").unwrap()); let pass_hash = hex::encode(hashed_pass.finalize()); if self.rt.block_on(async { self.db_oper .check_password(uname.clone(), pass_hash) .await .unwrap() }) { self.login_modal_state.fail = false; self.login_modal_state.status = crate::models::LoginStatus::Finished; self.login_modal_state.is_open = false; if self.rt.block_on(async { self.db_oper.check_admin(uname.clone()).await.unwrap() }) { self.main_viewer.is_admin = true } else { self.main_viewer.is_admin = false; } } else { self.login_modal_state.fail = true; self.login_modal_state.fail_message = "Неверные логин или пароль".to_string(); } } }); }); } 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, equipment_modal_state: ModalWinState, is_dark_theme: bool, interface_scale_ratio: f32, is_admin: bool, } 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, equipment_modal_state: ModalWinState { is_open: false, can_finish: false, data: HashMap::from([ ("id".to_owned(), String::new()), ("inv_number".to_owned(), String::new()), ("maintenance_date".to_owned(), String::new()), ("worker_id".to_owned(), String::new()), ]), status: ModalStatus::Add, }, is_admin: false, } } } impl MainTabViewer { fn new(is_admin: bool) -> Self { Self { is_admin, worker_tabs: WorkerTabViewer::new(is_admin), material_tabs: MaterialTabViewer::new(is_admin), ..Default::default() } } fn show_equipment(&mut self, ui: &mut egui::Ui) { if self.is_admin { ui.horizontal(|ui| { if ui.button("Добавить").clicked() { self.equipment_modal_state.is_open = true; self.equipment_modal_state.status = ModalStatus::Add; self.equipment_modal_state .data .insert("id".into(), "0".into()); self.equipment_modal_state .data .insert("name".into(), "".into()); self.equipment_modal_state .data .insert("inv_numver".into(), "".into()); self.equipment_modal_state .data .insert("worker_id".into(), "0".into()); } }); } 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()) .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("Дата последнего\nтехобслуживания"); }); }) .body(|mut body| { body.ui_mut().style_mut().interaction.selectable_labels = false; for eq in self.equipment.read().clone().unwrap().iter() { body.row(20.0, |mut row| { 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()); }); row.col(|ui| { if self.is_admin { if ui.button("Удалить").clicked() { self.equipment_modal_state.is_open = true; self.equipment_modal_state.status = ModalStatus::Remove; self.equipment_modal_state .data .insert("id".into(), eq.id.to_string()); self.equipment_modal_state .data .insert("name".into(), eq.name.clone()); self.equipment_modal_state .data .insert("inv_numver".into(), eq.inv_number.clone()); self.equipment_modal_state .data .insert("worker_id".into(), eq.worker.id.to_string()); } } }); if row.response().double_clicked() { self.equipment_modal_state.is_open = true; self.equipment_modal_state.status = ModalStatus::Edit; self.equipment_modal_state .data .insert("id".into(), eq.id.to_string()); self.equipment_modal_state .data .insert("name".into(), eq.name.clone()); self.equipment_modal_state .data .insert("inv_numver".into(), eq.inv_number.clone()); self.equipment_modal_state .data .insert("worker_id".into(), eq.worker.id.to_string()); } }); } }); if self.equipment_modal_state.is_open { egui::Modal::new(egui::Id::new("process_equipment_modal")).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { self.equipment_modal_state.is_open = false; } if self.equipment_modal_state.status != ModalStatus::Remove { egui::Grid::new("process_equipment_grid").show(ui, |ui| { ui.label("Название"); let mut name = self.equipment_modal_state.data.get_mut("name").unwrap(); ui.add_sized(Vec2::new(200.0, 20.0), egui::TextEdit::singleline(name)); ui.end_row(); ui.label("Инв. номер"); let mut inv_num = self .equipment_modal_state .data .get_mut("inv_number") .unwrap(); ui.text_edit_singleline(inv_num); ui.end_row(); ui.label("Ответственный"); // egui::ComboBox::new("process_equipment_combobox"); ui.end_row(); ui.label("Дата последнего\nтехобслуживания"); let mut maintenance = self .equipment_modal_state .data .get_mut("maintenance_date") .unwrap(); ui.text_edit_singleline(maintenance); }); } }); } } 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) } }