BreweryControlSystem/code/src/app.rs

439 lines
17 KiB
Rust

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<Tab>,
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<RwLock<Option<Vec<Position>>>>,
workers: Arc<RwLock<Option<Vec<Worker>>>>,
equipment: Arc<RwLock<Option<Vec<Equipment>>>>,
db_oper: DBOperator,
worker_tabs: WorkerTabViewer,
worker_tree: egui_dock::DockState<Tab>,
material_tabs: MaterialTabViewer,
material_tree: egui_dock::DockState<Tab>,
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)
}
}