1028 lines
42 KiB
Rust
1028 lines
42 KiB
Rust
use std::{
|
||
collections::{HashMap, HashSet},
|
||
default::Default,
|
||
str::FromStr,
|
||
sync::Arc,
|
||
};
|
||
|
||
use crate::{
|
||
database::DBOperator,
|
||
material_tab::MaterialTabViewer,
|
||
misc::{self, ELEMENTS_PER_PAGE},
|
||
models::{
|
||
Equipment, EquipmentFilter, EquipmentModalState, LoginModalState, LoginStatus, Material,
|
||
MaterialCategory, MaterialRow, ModalStatus, ModalWinState, Position, Salary,
|
||
SalaryModalState, Worker,
|
||
},
|
||
order_tab::OrderTabViewer,
|
||
recipe_tab::RecipeTabViewer,
|
||
reports::Reporter,
|
||
tab::*,
|
||
worker_tab::{self, *},
|
||
};
|
||
use bigdecimal::ToPrimitive;
|
||
use chrono::{Datelike, NaiveDate};
|
||
use egui::{TextBuffer, Vec2, mutex::RwLock};
|
||
use egui_dock::{DockArea, DockState, 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::Order,
|
||
title: "Заказы".to_owned(),
|
||
},
|
||
Tab {
|
||
tab_type: TabTypes::Client,
|
||
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.status == LoginStatus::Finished {
|
||
ui.horizontal(|ui| {
|
||
ui.label(self.login_modal_state.data.get("login").unwrap());
|
||
if ui.button("Выйти").clicked() {
|
||
self.login_modal_state.is_open = true;
|
||
self.login_modal_state.status = LoginStatus::Login;
|
||
self.login_modal_state.data.insert("login", String::new());
|
||
self.login_modal_state
|
||
.data
|
||
.insert("password", String::new());
|
||
}
|
||
});
|
||
}
|
||
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;
|
||
}
|
||
self.main_viewer = MainTabViewer::new(self.main_viewer.is_admin);
|
||
} 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 {
|
||
workers: Arc<RwLock<Vec<Worker>>>,
|
||
equipment: Arc<RwLock<Vec<Equipment>>>,
|
||
salaries: Arc<RwLock<Vec<Salary>>>,
|
||
|
||
db_oper: DBOperator,
|
||
rt: tokio::runtime::Runtime,
|
||
|
||
worker_tabs: WorkerTabViewer,
|
||
worker_tree: egui_dock::DockState<Tab>,
|
||
material_tabs: MaterialTabViewer,
|
||
material_tree: egui_dock::DockState<Tab>,
|
||
recipe_tab: RecipeTabViewer,
|
||
order_tabs: OrderTabViewer,
|
||
order_tree: egui_dock::DockState<Tab>,
|
||
|
||
equipment_modal_state: EquipmentModalState,
|
||
salary_modal_state: SalaryModalState,
|
||
|
||
is_dark_theme: bool,
|
||
interface_scale_ratio: f32,
|
||
equipment_page: i32,
|
||
salaries_page: i32,
|
||
workers_page: i32,
|
||
|
||
is_admin: bool,
|
||
reporter: Reporter,
|
||
only_hired_workers: 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 });
|
||
let elements_per_page = 5;
|
||
Self {
|
||
workers: Arc::new(RwLock::new(rt.block_on(async {
|
||
db_oper
|
||
.get_workers(0, elements_per_page, false)
|
||
.await
|
||
.unwrap()
|
||
}))),
|
||
equipment: Arc::new(RwLock::new(rt.block_on(async {
|
||
db_oper
|
||
.get_equipment(0, elements_per_page, false)
|
||
.await
|
||
.unwrap()
|
||
}))),
|
||
salaries: Arc::new(RwLock::new(rt.block_on(async {
|
||
db_oper.get_salaries(0, elements_per_page).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,
|
||
},
|
||
Tab {
|
||
title: "Повышения".to_owned(),
|
||
tab_type: TabTypes::WorkerPromotion,
|
||
},
|
||
]),
|
||
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,
|
||
},
|
||
]),
|
||
recipe_tab: RecipeTabViewer::default(),
|
||
|
||
order_tabs: OrderTabViewer::default(),
|
||
order_tree: DockState::new(vec![
|
||
Tab {
|
||
title: "Входящие".to_owned(),
|
||
tab_type: TabTypes::IncomingOrderList,
|
||
},
|
||
Tab {
|
||
title: "Исходящие".to_owned(),
|
||
tab_type: TabTypes::OutcomingOrderList,
|
||
},
|
||
]),
|
||
rt,
|
||
is_dark_theme: true,
|
||
interface_scale_ratio: 1.2,
|
||
|
||
equipment_modal_state: EquipmentModalState {
|
||
is_open: false,
|
||
can_finish: false,
|
||
equipment: Equipment::default(),
|
||
status: ModalStatus::Add,
|
||
filters: EquipmentFilter::default(),
|
||
},
|
||
salary_modal_state: SalaryModalState {
|
||
is_open: false,
|
||
can_finish: false,
|
||
data: HashMap::from([
|
||
("id".to_owned(), String::new()),
|
||
("worker_id".to_owned(), String::new()),
|
||
("salary_size".to_owned(), String::new()),
|
||
("comment".to_owned(), String::new()),
|
||
]),
|
||
status: ModalStatus::Add,
|
||
workers: HashSet::new(),
|
||
},
|
||
is_admin: false,
|
||
reporter: Reporter {},
|
||
|
||
equipment_page: 0,
|
||
salaries_page: 0,
|
||
workers_page: 0,
|
||
only_hired_workers: false,
|
||
}
|
||
}
|
||
}
|
||
impl MainTabViewer {
|
||
fn update_salary(&mut self) {
|
||
self.salaries = Arc::new(RwLock::new(self.rt.block_on(async {
|
||
self.db_oper
|
||
.get_salaries(
|
||
self.salaries_page * misc::ELEMENTS_PER_PAGE,
|
||
misc::ELEMENTS_PER_PAGE,
|
||
)
|
||
.await
|
||
.unwrap()
|
||
})));
|
||
}
|
||
fn update_equipment(&mut self) {
|
||
self.equipment = Arc::new(RwLock::new(self.rt.block_on(async {
|
||
self.db_oper
|
||
.get_equipment(
|
||
self.equipment_page * misc::ELEMENTS_PER_PAGE,
|
||
misc::ELEMENTS_PER_PAGE,
|
||
self.equipment_modal_state.filters.without_written_offs,
|
||
)
|
||
.await
|
||
.unwrap()
|
||
})));
|
||
}
|
||
fn update_workers(&mut self) {
|
||
self.workers = Arc::new(RwLock::new(self.rt.block_on(async {
|
||
self.db_oper
|
||
.get_workers(
|
||
self.workers_page * misc::ELEMENTS_PER_PAGE,
|
||
ELEMENTS_PER_PAGE,
|
||
self.only_hired_workers,
|
||
)
|
||
.await
|
||
.unwrap()
|
||
})));
|
||
}
|
||
fn get_salary(&self) -> Salary {
|
||
Salary {
|
||
id: self
|
||
.salary_modal_state
|
||
.data
|
||
.get("id")
|
||
.unwrap()
|
||
.parse::<i32>()
|
||
.unwrap(),
|
||
worker: self
|
||
.workers
|
||
.read()
|
||
.iter()
|
||
.find(|w| {
|
||
&w.id.to_string() == self.salary_modal_state.data.get("worker_id").unwrap()
|
||
})
|
||
.unwrap()
|
||
.clone(),
|
||
salary_size: BigDecimal::from_str(
|
||
self.salary_modal_state.data.get("salary_size").unwrap(),
|
||
)
|
||
.unwrap(),
|
||
salary_date: chrono::NaiveDate::from_str(
|
||
self.salary_modal_state.data.get("salary_date").unwrap(),
|
||
)
|
||
.unwrap()
|
||
.and_hms_opt(0, 0, 0)
|
||
.unwrap()
|
||
.and_local_timezone(chrono::Local)
|
||
.unwrap(),
|
||
comment: self.salary_modal_state.data.get("comment").unwrap().clone(),
|
||
}
|
||
}
|
||
fn new(is_admin: bool) -> Self {
|
||
Self {
|
||
is_admin,
|
||
worker_tabs: WorkerTabViewer::new(is_admin),
|
||
material_tabs: MaterialTabViewer::new(is_admin),
|
||
recipe_tab: RecipeTabViewer::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.equipment = Equipment::default();
|
||
}
|
||
});
|
||
}
|
||
ui.horizontal(|ui| {
|
||
if ui
|
||
.add(egui::Checkbox::new(
|
||
&mut self.equipment_modal_state.filters.without_written_offs,
|
||
"Без списанных",
|
||
))
|
||
.changed()
|
||
{
|
||
self.update_equipment();
|
||
};
|
||
if ui.button("<-").clicked() {
|
||
if self.equipment_page > 0 {
|
||
self.equipment_page -= 1;
|
||
}
|
||
self.update_equipment();
|
||
}
|
||
ui.monospace(self.equipment_page.to_string());
|
||
if ui.button("->").clicked() {
|
||
if self.equipment.read().len() == 5 {
|
||
self.equipment_page += 1;
|
||
self.update_equipment();
|
||
}
|
||
}
|
||
});
|
||
|
||
TableBuilder::new(ui)
|
||
.striped(true)
|
||
.resizable(false)
|
||
.vscroll(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())
|
||
.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техобслуживания");
|
||
});
|
||
header.col(|ui| {
|
||
ui.heading("Списано");
|
||
});
|
||
})
|
||
.body(|mut body| {
|
||
body.ui_mut().style_mut().interaction.selectable_labels = false;
|
||
for eq in self.equipment.read().clone().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| {
|
||
ui.label(if eq.is_written_off { "Да" } else { "Нет" });
|
||
});
|
||
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.equipment = eq.clone();
|
||
}
|
||
}
|
||
});
|
||
if row.response().double_clicked() {
|
||
self.equipment_modal_state.is_open = true;
|
||
self.equipment_modal_state.status = ModalStatus::Edit;
|
||
self.equipment_modal_state.equipment = eq.clone();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
if self.equipment_modal_state.is_open {
|
||
egui::Modal::new(egui::Id::new("process_equipment_modal")).show(ui.ctx(), |ui| {
|
||
if ui.button("Закрыть").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape))
|
||
{
|
||
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("Название");
|
||
ui.add_sized(
|
||
Vec2::new(200.0, 20.0),
|
||
egui::TextEdit::singleline(
|
||
&mut self.equipment_modal_state.equipment.name,
|
||
)
|
||
.hint_text("Название оборудования"),
|
||
);
|
||
ui.end_row();
|
||
|
||
ui.label("Инв. номер");
|
||
ui.add(
|
||
egui::TextEdit::singleline(
|
||
&mut self.equipment_modal_state.equipment.inv_number,
|
||
)
|
||
.hint_text("НН-00"),
|
||
);
|
||
ui.end_row();
|
||
|
||
ui.label("Ответственный");
|
||
egui::ComboBox::new("process_equipment_combobox", "")
|
||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
||
.height(100.0)
|
||
.selected_text(
|
||
self.equipment_modal_state
|
||
.equipment
|
||
.worker
|
||
.full_name
|
||
.clone(),
|
||
)
|
||
.show_ui(ui, |ui| {
|
||
for wkrk in self.workers.read().clone().iter() {
|
||
ui.selectable_value(
|
||
&mut self.equipment_modal_state.equipment.worker,
|
||
wkrk.clone(),
|
||
&wkrk.full_name,
|
||
);
|
||
}
|
||
ui.horizontal(|ui| {
|
||
if ui.button("<-").clicked() {
|
||
if self.workers_page > 0 {
|
||
self.workers_page -= 1;
|
||
self.update_workers();
|
||
};
|
||
}
|
||
ui.monospace(self.workers_page.to_string());
|
||
if ui.button("->").clicked() {
|
||
if self.workers.read().len().to_i32().unwrap()
|
||
== misc::ELEMENTS_PER_PAGE
|
||
{
|
||
self.workers_page += 1;
|
||
self.update_workers();
|
||
}
|
||
}
|
||
})
|
||
});
|
||
ui.end_row();
|
||
|
||
ui.label("Дата последнего\nтехобслуживания");
|
||
let ndate = self
|
||
.equipment_modal_state
|
||
.equipment
|
||
.maintenance_date
|
||
.date_naive();
|
||
let mut date = misc::naivedate_to_jiff(ndate);
|
||
ui.add(egui_extras::DatePickerButton::new(&mut date));
|
||
if misc::naivedate_to_jiff(
|
||
self.equipment_modal_state
|
||
.equipment
|
||
.maintenance_date
|
||
.date_naive(),
|
||
) != date
|
||
{
|
||
self.equipment_modal_state.equipment.maintenance_date =
|
||
misc::jiff_to_naivedate(date)
|
||
.and_hms_opt(0, 0, 0)
|
||
.unwrap()
|
||
.and_local_timezone(chrono::Local)
|
||
.unwrap();
|
||
}
|
||
});
|
||
} else {
|
||
ui.label("Вы уверены, что хотите удалить:");
|
||
ui.strong(format!(
|
||
"{} | {}?",
|
||
self.equipment_modal_state.equipment.name,
|
||
self.equipment_modal_state.equipment.inv_number
|
||
));
|
||
}
|
||
let btext = match &self.equipment_modal_state.status {
|
||
ModalStatus::Add => "Добавить",
|
||
ModalStatus::Edit => "Сохранить",
|
||
ModalStatus::Remove => "Списать",
|
||
};
|
||
match &self.equipment_modal_state.status {
|
||
ModalStatus::Remove => {
|
||
self.equipment_modal_state.can_finish = true;
|
||
}
|
||
_ => {
|
||
self.equipment_modal_state.can_finish =
|
||
self.equipment_modal_state.equipment.name.len() > 0
|
||
&& self.equipment_modal_state.equipment.inv_number.len() > 0
|
||
&& self.equipment_modal_state.equipment.worker.id != 0;
|
||
}
|
||
}
|
||
let resp = ui.add_enabled(
|
||
self.equipment_modal_state.can_finish,
|
||
egui::Button::new(btext),
|
||
);
|
||
if resp.clicked() {
|
||
match &self.equipment_modal_state.status {
|
||
ModalStatus::Add => self.rt.block_on(async {
|
||
self.db_oper
|
||
.add_equipment(self.equipment_modal_state.equipment.clone())
|
||
.await
|
||
.unwrap();
|
||
}),
|
||
ModalStatus::Edit => self.rt.block_on(async {
|
||
self.db_oper
|
||
.update_equipment(self.equipment_modal_state.equipment.clone())
|
||
.await
|
||
.unwrap();
|
||
}),
|
||
ModalStatus::Remove => {
|
||
self.rt.block_on(async {
|
||
self.db_oper
|
||
.write_off_equipment(self.equipment_modal_state.equipment.id)
|
||
.await
|
||
.unwrap();
|
||
});
|
||
}
|
||
}
|
||
self.equipment_modal_state.is_open = false;
|
||
self.update_equipment();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
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_order(&mut self, ui: &mut egui::Ui) {
|
||
let id = ui.make_persistent_id("OrderMenu");
|
||
DockArea::new(&mut self.order_tree)
|
||
.id(id)
|
||
.show_inside(ui, &mut self.order_tabs);
|
||
}
|
||
fn show_salary(&mut self, ui: &mut egui::Ui) {
|
||
/*
|
||
* Чё надо:
|
||
* 1. Список платежей.
|
||
*/
|
||
|
||
if self.is_admin && ui.button("Добавить").clicked() {
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("id".to_owned(), String::new());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("worker_id".to_owned(), String::new());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("salary_size".to_owned(), String::new());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("salary_date".to_owned(), String::new());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("comment".to_owned(), String::new());
|
||
self.salary_modal_state.workers.clear();
|
||
self.salary_modal_state.is_open = true;
|
||
self.salary_modal_state.status = ModalStatus::Add;
|
||
}
|
||
TableBuilder::new(ui)
|
||
.striped(true)
|
||
.columns(Column::auto(), 5)
|
||
.sense(egui::Sense::click())
|
||
.vscroll(false)
|
||
.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 sal in (self.salaries.read().iter().clone()) {
|
||
body.row(25.0, |mut row| {
|
||
row.col(|ui| {
|
||
ui.label(&sal.worker.full_name);
|
||
});
|
||
row.col(|ui| {
|
||
ui.label(&sal.salary_size.to_string());
|
||
});
|
||
row.col(|ui| {
|
||
ui.label(&sal.salary_date.date_naive().to_string());
|
||
});
|
||
row.col(|ui| {
|
||
let text: String = sal.comment.chars().take(15).collect();
|
||
ui.label(text);
|
||
});
|
||
row.col(|ui| {
|
||
if ui.button("Удалить").clicked() {
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("id".to_owned(), sal.id.to_string());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("worker_id".to_owned(), sal.worker.id.to_string());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("salary_size".to_owned(), sal.salary_size.to_string());
|
||
self.salary_modal_state.data.insert(
|
||
"salary_date".to_owned(),
|
||
sal.salary_date.date_naive().to_string(),
|
||
);
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("comment".to_owned(), sal.comment.clone());
|
||
self.salary_modal_state.is_open = true;
|
||
self.salary_modal_state.status = ModalStatus::Remove;
|
||
}
|
||
});
|
||
if row.response().double_clicked() {
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("id".to_owned(), sal.id.to_string());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("worker_id".to_owned(), sal.worker.id.to_string());
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("salary_size".to_owned(), sal.salary_size.to_string());
|
||
self.salary_modal_state.data.insert(
|
||
"salary_date".to_owned(),
|
||
sal.salary_date.date_naive().to_string(),
|
||
);
|
||
self.salary_modal_state
|
||
.data
|
||
.insert("comment".to_owned(), sal.comment.clone());
|
||
self.salary_modal_state.is_open = true;
|
||
self.salary_modal_state.status = ModalStatus::Edit;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
if self.salary_modal_state.is_open {
|
||
egui::Modal::new(egui::Id::new("salary_modal")).show(ui.ctx(), |ui| {
|
||
ui.horizontal(|ui| {
|
||
if ui.button("Закрыть").clicked()
|
||
|| ui.input(|i| i.key_pressed(egui::Key::Escape))
|
||
{
|
||
self.salary_modal_state.is_open = false;
|
||
}
|
||
if ui.button("Отчёт").clicked() {
|
||
self.reporter.payment_report(self.get_salary().clone());
|
||
}
|
||
});
|
||
if self.salary_modal_state.status != ModalStatus::Remove {
|
||
egui::Grid::new("process_salary_grid").show(ui, |ui| {
|
||
let size = ui.available_size();
|
||
if self.salary_modal_state.status == ModalStatus::Add {
|
||
ui.label("Сотрудник(-и):");
|
||
egui::ComboBox::new("process_salary_grid_combobox", "")
|
||
.selected_text(format!(
|
||
"{} выбран(-о)",
|
||
self.salary_modal_state.workers.len()
|
||
))
|
||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
||
.show_ui(ui, |ui| {
|
||
for wrkr in self.workers.read().clone().iter() {
|
||
let mut checked =
|
||
self.salary_modal_state.workers.contains(wrkr);
|
||
let res = ui.add(egui::Checkbox::new(
|
||
&mut checked,
|
||
&wrkr.full_name,
|
||
));
|
||
if res.clicked() {
|
||
if checked {
|
||
self.salary_modal_state
|
||
.workers
|
||
.insert(wrkr.clone());
|
||
} else {
|
||
self.salary_modal_state.workers.remove(wrkr);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
ui.end_row();
|
||
}
|
||
|
||
ui.label("Размер платежа:");
|
||
ui.add_sized(
|
||
size,
|
||
egui::TextEdit::singleline(
|
||
self.salary_modal_state.data.get_mut("salary_size").unwrap(),
|
||
),
|
||
);
|
||
|
||
ui.end_row();
|
||
ui.label("Дата платежа:");
|
||
ui.add_sized(
|
||
size,
|
||
egui::TextEdit::singleline(
|
||
self.salary_modal_state.data.get_mut("salary_date").unwrap(),
|
||
)
|
||
.hint_text("1970-01-01"),
|
||
);
|
||
ui.end_row();
|
||
ui.label("Примечание:");
|
||
ui.add_sized(
|
||
size,
|
||
egui::TextEdit::multiline(
|
||
self.salary_modal_state.data.get_mut("comment").unwrap(),
|
||
),
|
||
);
|
||
ui.end_row();
|
||
});
|
||
} else {
|
||
let wname = self
|
||
.workers
|
||
.read()
|
||
.clone()
|
||
.iter()
|
||
.find(|w| {
|
||
&w.id.to_string()
|
||
== self.salary_modal_state.data.get("worker_id").unwrap()
|
||
})
|
||
.unwrap()
|
||
.full_name
|
||
.clone();
|
||
ui.label("Вы уверены, что хотите удалить запись о выплате:");
|
||
ui.label(format!(
|
||
"{}: {} | ${}",
|
||
self.salary_modal_state.data.get("salary_date").unwrap(),
|
||
wname,
|
||
self.salary_modal_state.data.get("salary_size").unwrap()
|
||
));
|
||
}
|
||
let btext = match self.salary_modal_state.status {
|
||
ModalStatus::Add => "Добавить",
|
||
ModalStatus::Edit => "Сохранить",
|
||
ModalStatus::Remove => "Удалить",
|
||
};
|
||
if ui.button(btext).clicked() {
|
||
match self.salary_modal_state.status {
|
||
ModalStatus::Add => {
|
||
self.rt.block_on(async {
|
||
for w in self.salary_modal_state.workers.iter() {
|
||
self.db_oper
|
||
.add_salary(
|
||
w.id,
|
||
chrono::NaiveDate::from_str(
|
||
self.salary_modal_state
|
||
.data
|
||
.get("salary_date")
|
||
.unwrap(),
|
||
)
|
||
.unwrap()
|
||
.and_hms_opt(0, 0, 0)
|
||
.unwrap()
|
||
.and_local_timezone(chrono::Local)
|
||
.unwrap(),
|
||
BigDecimal::from_str(
|
||
self.salary_modal_state
|
||
.data
|
||
.get("salary_size")
|
||
.unwrap(),
|
||
)
|
||
.unwrap(),
|
||
Some(
|
||
self.salary_modal_state
|
||
.data
|
||
.get("comment")
|
||
.unwrap()
|
||
.clone(),
|
||
),
|
||
)
|
||
.await
|
||
.ok();
|
||
}
|
||
});
|
||
}
|
||
ModalStatus::Edit => {
|
||
let sal = Salary {
|
||
id: self
|
||
.salary_modal_state
|
||
.data
|
||
.get("id")
|
||
.unwrap()
|
||
.parse::<i32>()
|
||
.unwrap(),
|
||
worker: self
|
||
.workers
|
||
.read()
|
||
.iter()
|
||
.find(|w| {
|
||
&w.id.to_string()
|
||
== self
|
||
.salary_modal_state
|
||
.data
|
||
.get("worker_id")
|
||
.unwrap()
|
||
})
|
||
.unwrap()
|
||
.clone(),
|
||
salary_size: BigDecimal::from_str(
|
||
self.salary_modal_state.data.get("salary_size").unwrap(),
|
||
)
|
||
.unwrap(),
|
||
salary_date: chrono::NaiveDate::parse_from_str(
|
||
self.salary_modal_state.data.get("salary_date").unwrap(),
|
||
"%Y-%m-%d",
|
||
)
|
||
.unwrap()
|
||
.and_hms_opt(0, 0, 0)
|
||
.unwrap()
|
||
.and_local_timezone(chrono::Local)
|
||
.unwrap(),
|
||
comment: self
|
||
.salary_modal_state
|
||
.data
|
||
.get("comment")
|
||
.unwrap()
|
||
.clone(),
|
||
};
|
||
self.rt.block_on(async {
|
||
self.db_oper.update_salary(sal).await;
|
||
});
|
||
}
|
||
ModalStatus::Remove => self.rt.block_on(async {
|
||
self.db_oper
|
||
.remove_salary(
|
||
self.salary_modal_state
|
||
.data
|
||
.get("id")
|
||
.unwrap()
|
||
.parse::<i32>()
|
||
.unwrap(),
|
||
)
|
||
.await;
|
||
}),
|
||
}
|
||
self.salary_modal_state.is_open = false;
|
||
self.update_salary();
|
||
}
|
||
});
|
||
}
|
||
/*
|
||
* 2. Возможность добавить платежи.
|
||
* 3. Возможность назначить платежи сразу нескольким сотрудникам.
|
||
* 4. Возможность отменить платежи.
|
||
*/
|
||
}
|
||
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;
|
||
}
|
||
}
|
||
|
||
fn show_recipe(&mut self, ui: &mut egui::Ui) {
|
||
self.recipe_tab.show_recipe_list(ui);
|
||
}
|
||
}
|
||
|
||
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);
|
||
}
|
||
TabTypes::Recipe => {
|
||
&self.show_recipe(ui);
|
||
}
|
||
TabTypes::Order => {
|
||
&self.show_order(ui);
|
||
}
|
||
_ => {
|
||
ui.label("Welcome to Avalonia!");
|
||
}
|
||
}
|
||
}
|
||
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)
|
||
}
|
||
}
|