мямямя мямямя

master
Алексей Алексей 2026-06-20 23:30:08 +10:00
parent 37d9ae5474
commit 0d558c1063
4 changed files with 236 additions and 13 deletions

View File

@ -306,6 +306,7 @@ impl MainTabViewer {
is_admin, is_admin,
worker_tabs: WorkerTabViewer::new(is_admin), worker_tabs: WorkerTabViewer::new(is_admin),
material_tabs: MaterialTabViewer::new(is_admin), material_tabs: MaterialTabViewer::new(is_admin),
recipe_tab: RecipeTabViewer::new(is_admin),
..Default::default() ..Default::default()
} }
} }

View File

@ -410,9 +410,21 @@ impl DBOperator {
.await .await
.unwrap_or(Vec::new()); .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()); 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) Ok(ret)
} }
pub async fn get_recipe_elements(&self) -> Result<Vec<RecipeElement>, sqlx::Error> { pub async fn get_recipe_elements(&self) -> Result<Vec<RecipeElement>, sqlx::Error> {
@ -420,7 +432,7 @@ impl DBOperator {
let mats = self.get_materials().await.unwrap(); let mats = self.get_materials().await.unwrap();
let eqs = self.get_equipment().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() { for re in pre_ret.unwrap().iter() {
let mat = mats.iter().find(|m| m.id == re.material_id); let mat = mats.iter().find(|m| m.id == re.material_id);
let eq = eqs.iter().find(|e| e.id == re.equipment_id); let eq = eqs.iter().find(|e| e.id == re.equipment_id);
@ -431,6 +443,7 @@ impl DBOperator {
material: m.clone(), material: m.clone(),
equipment: e.clone(), equipment: e.clone(),
quantity: re.quantity, quantity: re.quantity,
recipe_id: re.recipe_id,
}); });
} }
(_, _) => {} (_, _) => {}

View File

@ -54,7 +54,7 @@ pub struct Salary {
pub comment: String, pub comment: String,
} }
#[derive(Default, Clone, Hash)] #[derive(Default, Clone, Hash, PartialEq, Eq)]
pub struct Equipment { pub struct Equipment {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
@ -72,7 +72,7 @@ pub struct EquipmentRow {
pub worker_id: i32, pub worker_id: i32,
pub is_written_off: bool, pub is_written_off: bool,
} }
#[derive(Clone)] #[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct Material { pub struct Material {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
@ -86,16 +86,18 @@ pub struct MaterialRow {
pub quantity: i32, pub quantity: i32,
pub category_id: i32, pub category_id: i32,
} }
#[derive(sqlx::FromRow, Default, Clone, PartialEq)] #[derive(sqlx::FromRow, Default, Clone, PartialEq, Eq, Hash)]
pub struct MaterialCategory { pub struct MaterialCategory {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
} }
#[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct RecipeElement { pub struct RecipeElement {
pub id: i32, pub id: i32,
pub material: Material, pub material: Material,
pub equipment: Equipment, pub equipment: Equipment,
pub quantity: i32, pub quantity: i32,
pub recipe_id: i32,
} }
#[derive(FromRow)] #[derive(FromRow)]
pub struct RecipeElementRow { pub struct RecipeElementRow {
@ -103,7 +105,9 @@ pub struct RecipeElementRow {
pub material_id: i32, pub material_id: i32,
pub equipment_id: i32, pub equipment_id: i32,
pub quantity: i32, pub quantity: i32,
pub recipe_id: i32,
} }
#[derive(Default, Clone)]
pub struct Recipe { pub struct Recipe {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
@ -161,6 +165,23 @@ pub struct SalaryModalState {
pub status: ModalStatus, 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)] #[derive(Default, PartialEq)]
pub enum ModalStatus { pub enum ModalStatus {
#[default] #[default]

View File

@ -1,11 +1,20 @@
use tokio::sync::RwLock; use egui_extras::{Column, TableBuilder};
use crate::{ use crate::{
database::DBOperator, database::DBOperator,
models::{Equipment, Material, Recipe, RecipeElement}, models::{
ElementModalState, Equipment, Material, ModalStatus, Recipe, RecipeElement,
RecipeModalState,
},
tab::{Tab, TabTypes}, 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 { pub struct RecipeTabViewer {
is_admin: bool, is_admin: bool,
@ -16,6 +25,9 @@ pub struct RecipeTabViewer {
recipe_elements: Arc<RwLock<Vec<RecipeElement>>>, recipe_elements: Arc<RwLock<Vec<RecipeElement>>>,
materials: Arc<RwLock<Vec<Material>>>, materials: Arc<RwLock<Vec<Material>>>,
equipment: Arc<RwLock<Vec<Equipment>>>, equipment: Arc<RwLock<Vec<Equipment>>>,
process_recipe_status: RecipeModalState,
process_element_status: ElementModalState,
} }
impl RecipeTabViewer { impl RecipeTabViewer {
@ -25,8 +37,156 @@ impl RecipeTabViewer {
..Default::default() ..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) { 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 { impl egui_dock::TabViewer for RecipeTabViewer {
@ -52,10 +212,38 @@ impl Default for RecipeTabViewer {
fn default() -> Self { fn default() -> Self {
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
let db_oper = rt.block_on(async { DBOperator::new().await }); 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, rt,
db_oper, db_oper,
recipes:
} }
} }
} }