Compare commits
2 Commits
37d9ae5474
...
1c8aba9066
| Author | SHA1 | Date |
|---|---|---|
|
|
1c8aba9066 | |
|
|
0d558c1063 |
103
code/src/app.rs
103
code/src/app.rs
|
|
@ -306,6 +306,7 @@ impl MainTabViewer {
|
|||
is_admin,
|
||||
worker_tabs: WorkerTabViewer::new(is_admin),
|
||||
material_tabs: MaterialTabViewer::new(is_admin),
|
||||
recipe_tab: RecipeTabViewer::new(is_admin),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -493,8 +494,106 @@ impl MainTabViewer {
|
|||
};
|
||||
if ui.button(btext).clicked() {
|
||||
match self.equipment_modal_state.status {
|
||||
ModalStatus::Add => {}
|
||||
ModalStatus::Edit => {}
|
||||
ModalStatus::Add => self.rt.block_on(async {
|
||||
self.db_oper
|
||||
.add_equipment(Equipment {
|
||||
id: 0,
|
||||
name: self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("name")
|
||||
.unwrap_or(&String::new())
|
||||
.clone(),
|
||||
inv_number: self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("inv_number")
|
||||
.unwrap_or(&String::new())
|
||||
.clone(),
|
||||
maintenance_date: chrono::NaiveDate::from_str(
|
||||
self.equipment_modal_state
|
||||
.data
|
||||
.get("maintenance_date")
|
||||
.unwrap_or(&"1970.01.01".to_owned()),
|
||||
)
|
||||
.unwrap()
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.unwrap()
|
||||
.and_local_timezone(chrono::Local)
|
||||
.unwrap(),
|
||||
worker: self
|
||||
.workers
|
||||
.read()
|
||||
.clone()
|
||||
.iter()
|
||||
.find(|w| {
|
||||
&w.id.to_string()
|
||||
== self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("worker_id")
|
||||
.unwrap_or(&"0".to_owned())
|
||||
})
|
||||
.unwrap()
|
||||
.clone(),
|
||||
is_written_off: false,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}),
|
||||
ModalStatus::Edit => self.rt.block_on(async {
|
||||
self.db_oper
|
||||
.update_equipment(Equipment {
|
||||
id: self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("id")
|
||||
.unwrap()
|
||||
.parse::<i32>()
|
||||
.unwrap(),
|
||||
name: self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("name")
|
||||
.unwrap_or(&String::new())
|
||||
.clone(),
|
||||
inv_number: self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("inv_number")
|
||||
.unwrap_or(&String::new())
|
||||
.clone(),
|
||||
maintenance_date: chrono::NaiveDate::from_str(
|
||||
self.equipment_modal_state
|
||||
.data
|
||||
.get("maintenance_date")
|
||||
.unwrap_or(&"1970.01.01".to_owned()),
|
||||
)
|
||||
.unwrap()
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.unwrap()
|
||||
.and_local_timezone(chrono::Local)
|
||||
.unwrap(),
|
||||
worker: self
|
||||
.workers
|
||||
.read()
|
||||
.clone()
|
||||
.iter()
|
||||
.find(|w| {
|
||||
&w.id.to_string()
|
||||
== self
|
||||
.equipment_modal_state
|
||||
.data
|
||||
.get("worker_id")
|
||||
.unwrap_or(&"0".to_owned())
|
||||
})
|
||||
.unwrap()
|
||||
.clone(),
|
||||
is_written_off: false,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}),
|
||||
ModalStatus::Remove => {
|
||||
self.rt.block_on(async {
|
||||
self.db_oper
|
||||
|
|
|
|||
|
|
@ -78,6 +78,32 @@ impl DBOperator {
|
|||
}
|
||||
Ok(rets)
|
||||
}
|
||||
pub async fn add_equipment(&self, equipment: Equipment) -> Result<bool, sqlx::Error> {
|
||||
match sqlx::query("INSERT INTO `equipment` (id, inv_number, maintenance_date, worker_id, name, is_written_off) VALUES(0, ?, ?, ?, ?, 0);")
|
||||
.bind(equipment.inv_number)
|
||||
.bind(equipment.maintenance_date)
|
||||
.bind(equipment.worker.id)
|
||||
.bind(equipment.name)
|
||||
.execute(&self.pool)
|
||||
.await{
|
||||
Ok(_) => Ok(true),
|
||||
Err(_) => Ok(false)
|
||||
}
|
||||
}
|
||||
pub async fn update_equipment(&self, equipment: Equipment) -> Result<bool, sqlx::Error> {
|
||||
match sqlx::query("UPDATE Brewery.equipment SET inv_number=?, maintenance_date=?, worker_id=?, name=?, is_written_off=? WHERE id=?;")
|
||||
.bind(equipment.inv_number)
|
||||
.bind(equipment.maintenance_date)
|
||||
.bind(equipment.worker.id)
|
||||
.bind(equipment.name)
|
||||
.bind(equipment.is_written_off)
|
||||
.bind(equipment.id)
|
||||
.execute(&self.pool)
|
||||
.await{
|
||||
Ok(_) => Ok(true),
|
||||
Err(_) => Ok(false)
|
||||
}
|
||||
}
|
||||
pub async fn get_mcat(&self) -> Result<Vec<MaterialCategory>, sqlx::Error> {
|
||||
let rets = sqlx::query_as::<_, MaterialCategory>("SELECT * FROM `material_category`")
|
||||
.fetch_all(&self.pool)
|
||||
|
|
@ -410,9 +436,21 @@ impl DBOperator {
|
|||
.await
|
||||
.unwrap_or(Vec::new());
|
||||
|
||||
let ret: Vec<Recipe> = Vec::new();
|
||||
let mut ret: Vec<Recipe> = Vec::new();
|
||||
let elements = self.get_recipe_elements().await.unwrap_or(Vec::new());
|
||||
for pr in pre_ret.iter() {}
|
||||
for pr in pre_ret.iter() {
|
||||
let mut els: Vec<RecipeElement> = Vec::new();
|
||||
for elem in elements.clone() {
|
||||
if elem.recipe_id == pr.id {
|
||||
els.push(elem.clone());
|
||||
}
|
||||
}
|
||||
ret.push(Recipe {
|
||||
id: pr.id,
|
||||
name: pr.name.clone(),
|
||||
elements: els,
|
||||
});
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
pub async fn get_recipe_elements(&self) -> Result<Vec<RecipeElement>, sqlx::Error> {
|
||||
|
|
@ -420,7 +458,7 @@ impl DBOperator {
|
|||
|
||||
let mats = self.get_materials().await.unwrap();
|
||||
let eqs = self.get_equipment().await.unwrap();
|
||||
let ret: Vec<RecipeElement> = Vec::new();
|
||||
let mut ret: Vec<RecipeElement> = Vec::new();
|
||||
for re in pre_ret.unwrap().iter() {
|
||||
let mat = mats.iter().find(|m| m.id == re.material_id);
|
||||
let eq = eqs.iter().find(|e| e.id == re.equipment_id);
|
||||
|
|
@ -431,6 +469,7 @@ impl DBOperator {
|
|||
material: m.clone(),
|
||||
equipment: e.clone(),
|
||||
quantity: re.quantity,
|
||||
recipe_id: re.recipe_id,
|
||||
});
|
||||
}
|
||||
(_, _) => {}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub struct Salary {
|
|||
pub comment: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Hash)]
|
||||
#[derive(Default, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Equipment {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
|
|
@ -72,7 +72,7 @@ pub struct EquipmentRow {
|
|||
pub worker_id: i32,
|
||||
pub is_written_off: bool,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct Material {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
|
|
@ -86,16 +86,18 @@ pub struct MaterialRow {
|
|||
pub quantity: i32,
|
||||
pub category_id: i32,
|
||||
}
|
||||
#[derive(sqlx::FromRow, Default, Clone, PartialEq)]
|
||||
#[derive(sqlx::FromRow, Default, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MaterialCategory {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct RecipeElement {
|
||||
pub id: i32,
|
||||
pub material: Material,
|
||||
pub equipment: Equipment,
|
||||
pub quantity: i32,
|
||||
pub recipe_id: i32,
|
||||
}
|
||||
#[derive(FromRow)]
|
||||
pub struct RecipeElementRow {
|
||||
|
|
@ -103,7 +105,9 @@ pub struct RecipeElementRow {
|
|||
pub material_id: i32,
|
||||
pub equipment_id: i32,
|
||||
pub quantity: i32,
|
||||
pub recipe_id: i32,
|
||||
}
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Recipe {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
|
|
@ -161,6 +165,23 @@ pub struct SalaryModalState {
|
|||
pub status: ModalStatus,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RecipeModalState {
|
||||
pub is_open: bool,
|
||||
pub can_finish: bool,
|
||||
pub recipe: Recipe,
|
||||
pub elements: HashSet<RecipeElement>,
|
||||
pub status: ModalStatus,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ElementModalState {
|
||||
pub is_open: bool,
|
||||
pub can_finish: bool,
|
||||
pub element: RecipeElement,
|
||||
pub status: ModalStatus,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum ModalStatus {
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
use tokio::sync::RwLock;
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
|
||||
use crate::{
|
||||
database::DBOperator,
|
||||
models::{Equipment, Material, Recipe, RecipeElement},
|
||||
models::{
|
||||
ElementModalState, Equipment, Material, ModalStatus, Recipe, RecipeElement,
|
||||
RecipeModalState,
|
||||
},
|
||||
tab::{Tab, TabTypes},
|
||||
};
|
||||
use std::{default::Default, sync::Arc};
|
||||
use egui::{Sense, mutex::RwLock};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
default::Default,
|
||||
ops::Deref,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct RecipeTabViewer {
|
||||
is_admin: bool,
|
||||
|
|
@ -16,6 +25,9 @@ pub struct RecipeTabViewer {
|
|||
recipe_elements: Arc<RwLock<Vec<RecipeElement>>>,
|
||||
materials: Arc<RwLock<Vec<Material>>>,
|
||||
equipment: Arc<RwLock<Vec<Equipment>>>,
|
||||
|
||||
process_recipe_status: RecipeModalState,
|
||||
process_element_status: ElementModalState,
|
||||
}
|
||||
|
||||
impl RecipeTabViewer {
|
||||
|
|
@ -25,8 +37,156 @@ impl RecipeTabViewer {
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
fn new_element_modal(&mut self, ui: &mut egui::Ui) {
|
||||
egui::Modal::new(egui::Id::new("new_element_modal")).show(ui.ctx(), |ui| {
|
||||
if ui.button("Закрыть").clicked() {
|
||||
self.process_element_status.is_open = false;
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Использовать:");
|
||||
egui::ComboBox::new("material_select_combobox", "")
|
||||
.selected_text(&self.process_element_status.element.material.name)
|
||||
.show_ui(ui, |ui| {
|
||||
for mat in self.materials.read().clone().iter() {
|
||||
ui.selectable_value(
|
||||
&mut self.process_element_status.element.material,
|
||||
mat.clone(),
|
||||
&mat.name,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("В количестве:");
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.process_element_status.element.quantity)
|
||||
.range(0..=100),
|
||||
);
|
||||
ui.label("ед.");
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("С использованием:");
|
||||
egui::ComboBox::new("equipment_select_combobox", "")
|
||||
.selected_text(&self.process_element_status.element.equipment.name)
|
||||
.show_ui(ui, |ui| {
|
||||
for eq in self.equipment.read().clone().iter() {
|
||||
ui.selectable_value(
|
||||
&mut self.process_element_status.element.equipment,
|
||||
eq.clone(),
|
||||
&eq.name,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
if ui.button("Добавить").clicked(){
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn process_recipe_modal(&mut self, ui: &mut egui::Ui) {
|
||||
egui::Modal::new(egui::Id::new("process_recipe_modal")).show(ui.ctx(), |ui| {
|
||||
if ui.button("Закрыть").clicked() {
|
||||
self.process_recipe_status.is_open = false;
|
||||
}
|
||||
egui::Grid::new("process_recipe_grid").show(ui, |ui| {
|
||||
let size = ui.available_size();
|
||||
ui.label("Название:");
|
||||
ui.add_sized(
|
||||
size,
|
||||
egui::TextEdit::singleline(&mut self.process_recipe_status.recipe.name)
|
||||
.hint_text("Пиво"),
|
||||
);
|
||||
ui.end_row();
|
||||
|
||||
ui.label("Составляющие:");
|
||||
egui::ComboBox::new("recipe_element_combobox", "")
|
||||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
||||
.show_ui(ui, |ui| {
|
||||
if ui.button("Новый элемент").clicked() {
|
||||
self.process_element_status.is_open = true;
|
||||
self.process_element_status.status = ModalStatus::Add;
|
||||
}
|
||||
|
||||
for el in self.recipe_elements.read().clone().iter() {
|
||||
let resp = ui.checkbox(
|
||||
&mut self.process_recipe_status.elements.contains(el),
|
||||
&el.material.name,
|
||||
);
|
||||
if resp.clicked() {
|
||||
if self.process_recipe_status.elements.contains(el) {
|
||||
self.process_recipe_status.elements.remove(el);
|
||||
} else {
|
||||
self.process_recipe_status.elements.insert(el.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
if self.process_element_status.is_open {
|
||||
self.new_element_modal(ui);
|
||||
}
|
||||
}
|
||||
pub fn show_recipe_list(&mut self, ui: &mut egui::Ui) {
|
||||
ui.label("Goida");
|
||||
ui.horizontal(|ui| {
|
||||
if self.is_admin {
|
||||
if ui.button("Добавить").clicked() {
|
||||
self.process_recipe_status.is_open = true;
|
||||
self.process_recipe_status.recipe = Recipe::default();
|
||||
self.process_recipe_status.status = ModalStatus::Add;
|
||||
}
|
||||
}
|
||||
});
|
||||
TableBuilder::new(ui)
|
||||
.striped(true)
|
||||
.columns(Column::auto(), 3)
|
||||
.vscroll(false)
|
||||
.sense(Sense::click())
|
||||
.header(30.0, |mut head| {
|
||||
head.col(|mut ui| {
|
||||
ui.heading("Название");
|
||||
});
|
||||
head.col(|mut ui| {
|
||||
ui.heading("Элементы");
|
||||
});
|
||||
})
|
||||
.body(|mut body| {
|
||||
for rec in self.recipes.read().iter() {
|
||||
body.row(30.0, |mut row| {
|
||||
row.col(|ui| {
|
||||
ui.label(rec.name.clone());
|
||||
});
|
||||
row.col(|ui| {
|
||||
let text = match rec.elements.len() {
|
||||
0 => "Пусто",
|
||||
1 => "1 элемент",
|
||||
2..=4 => &format!("{} элемента", rec.elements.len()),
|
||||
_ => &format!("{} элементов", rec.elements.len()),
|
||||
};
|
||||
ui.label(text);
|
||||
});
|
||||
if self.is_admin {
|
||||
row.col(|ui| {
|
||||
if ui.button("Удалить").clicked() {
|
||||
self.process_recipe_status.is_open = true;
|
||||
self.process_recipe_status.recipe = rec.clone();
|
||||
self.process_recipe_status.status =
|
||||
crate::models::ModalStatus::Remove;
|
||||
}
|
||||
});
|
||||
}
|
||||
if row.response().clicked() {
|
||||
self.process_recipe_status.is_open = true;
|
||||
self.process_recipe_status.recipe = rec.clone();
|
||||
self.process_recipe_status.status = crate::models::ModalStatus::Edit;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if self.process_recipe_status.is_open {
|
||||
self.process_recipe_modal(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl egui_dock::TabViewer for RecipeTabViewer {
|
||||
|
|
@ -52,10 +212,38 @@ impl Default for RecipeTabViewer {
|
|||
fn default() -> Self {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let db_oper = rt.block_on(async { DBOperator::new().await });
|
||||
Self { is_admin: false,
|
||||
Self {
|
||||
is_admin: false,
|
||||
|
||||
recipes: Arc::new(RwLock::new(
|
||||
rt.block_on(async { db_oper.get_recipes().await.unwrap_or(Vec::new()) }),
|
||||
)),
|
||||
recipe_elements: Arc::new(RwLock::new(
|
||||
rt.block_on(async { db_oper.get_recipe_elements().await.unwrap_or(Vec::new()) }),
|
||||
)),
|
||||
materials: Arc::new(RwLock::new(
|
||||
rt.block_on(async { db_oper.get_materials().await.unwrap_or(Vec::new()) }),
|
||||
)),
|
||||
equipment: Arc::new(RwLock::new(
|
||||
rt.block_on(async { db_oper.get_equipment().await.unwrap_or(Vec::new()) }),
|
||||
)),
|
||||
|
||||
process_recipe_status: RecipeModalState {
|
||||
is_open: false,
|
||||
can_finish: false,
|
||||
recipe: Recipe::default(),
|
||||
status: crate::models::ModalStatus::Add,
|
||||
elements: HashSet::new(),
|
||||
},
|
||||
process_element_status: ElementModalState {
|
||||
is_open: false,
|
||||
can_finish: false,
|
||||
element: RecipeElement::default(),
|
||||
status: crate::models::ModalStatus::Add,
|
||||
},
|
||||
|
||||
rt,
|
||||
db_oper,
|
||||
recipes:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue