mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-14 11:59:18 +01:00
feat: added ways to delete metrics and sources
Deleting a metric or a source will prompt you for confirmation. No way I'm letting you delete data from archive without asking for confirmation
This commit is contained in:
parent
cdeb57b6b4
commit
d9a2fc0c8b
3 changed files with 117 additions and 48 deletions
|
@ -117,6 +117,13 @@ impl SQLiteDataStore {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete_values(&self, metric_id: i32) -> rusqlite::Result<usize> {
|
||||||
|
self.conn.execute(
|
||||||
|
"DELETE FROM points WHERE metric_id = ?",
|
||||||
|
params![metric_id]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_sources(&self) -> rusqlite::Result<Vec<Source>> {
|
pub fn load_sources(&self) -> rusqlite::Result<Vec<Source>> {
|
||||||
let mut sources: Vec<Source> = Vec::new();
|
let mut sources: Vec<Source> = Vec::new();
|
||||||
let mut statement = self.conn.prepare("SELECT * FROM sources ORDER BY position")?;
|
let mut statement = self.conn.prepare("SELECT * FROM sources ORDER BY position")?;
|
||||||
|
@ -190,9 +197,9 @@ impl SQLiteDataStore {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn delete_source(&self, id:i32) -> rusqlite::Result<usize> {
|
pub fn delete_source(&self, id:i32) -> rusqlite::Result<usize> {
|
||||||
// self.conn.execute("DELETE FROM sources WHERE id = ?", params![id])
|
self.conn.execute("DELETE FROM sources WHERE id = ?", params![id])
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn load_metrics(&self) -> rusqlite::Result<Vec<Metric>> {
|
pub fn load_metrics(&self) -> rusqlite::Result<Vec<Metric>> {
|
||||||
let mut metrics: Vec<Metric> = Vec::new();
|
let mut metrics: Vec<Metric> = Vec::new();
|
||||||
|
@ -277,9 +284,9 @@ impl SQLiteDataStore {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn delete_metric(&self, id:i32) -> rusqlite::Result<usize> {
|
pub fn delete_metric(&self, id:i32) -> rusqlite::Result<usize> {
|
||||||
// self.conn.execute("DELETE FROM metrics WHERE id = ?", params![id])
|
self.conn.execute("DELETE FROM metrics WHERE id = ?", params![id])
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn load_panels(&self) -> rusqlite::Result<Vec<Panel>> {
|
pub fn load_panels(&self) -> rusqlite::Result<Vec<Panel>> {
|
||||||
let mut panels: Vec<Panel> = Vec::new();
|
let mut panels: Vec<Panel> = Vec::new();
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
use eframe::{egui::{Ui, TextEdit, DragValue, Checkbox}};
|
use eframe::{egui::{Ui, TextEdit, DragValue, Checkbox}};
|
||||||
|
|
||||||
use crate::app::data::source::{Panel, Source, Metric};
|
use crate::app::data::source::Source;
|
||||||
|
|
||||||
use super::metric::{metric_edit_ui, metric_display_ui};
|
pub fn source_display_ui(ui: &mut Ui, source: &mut Source, _width: f32) {
|
||||||
|
|
||||||
pub fn source_display_ui(ui: &mut Ui, source: &mut Source, metrics: &Vec<Metric>, _width: f32) {
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_enabled(false, Checkbox::new(&mut source.enabled, ""));
|
ui.add_enabled(false, Checkbox::new(&mut source.enabled, ""));
|
||||||
ui.add_enabled(false, DragValue::new(&mut source.interval).clamp_range(1..=120));
|
ui.add_enabled(false, DragValue::new(&mut source.interval).clamp_range(1..=120));
|
||||||
ui.heading(&source.name).on_hover_text(&source.url);
|
ui.heading(&source.name).on_hover_text(&source.url);
|
||||||
});
|
});
|
||||||
for metric in metrics.iter() {
|
|
||||||
if metric.source_id == source.id {
|
|
||||||
metric_display_ui(ui, metric, ui.available_width());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_edit_ui(ui: &mut Ui, source: &mut Source, metrics: Option<&mut Vec<Metric>>, panels: &Vec<Panel>, width: f32) {
|
pub fn source_edit_ui(ui: &mut Ui, source: &mut Source, width: f32) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let text_width = width - 100.0;
|
let text_width = width - 100.0;
|
||||||
ui.checkbox(&mut source.enabled, "");
|
ui.checkbox(&mut source.enabled, "");
|
||||||
|
@ -31,11 +24,4 @@ pub fn source_edit_ui(ui: &mut Ui, source: &mut Source, metrics: Option<&mut Vec
|
||||||
.hint_text("url")
|
.hint_text("url")
|
||||||
.show(ui);
|
.show(ui);
|
||||||
});
|
});
|
||||||
if let Some(metrics) = metrics {
|
|
||||||
for metric in metrics.iter_mut() {
|
|
||||||
if metric.source_id == source.id {
|
|
||||||
metric_edit_ui(ui, metric, Some(panels), width - 10.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
126
src/app/mod.rs
126
src/app/mod.rs
|
@ -3,17 +3,19 @@ pub mod gui;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod worker;
|
pub mod worker;
|
||||||
|
|
||||||
|
use eframe::egui::Window;
|
||||||
use eframe::egui::{
|
use eframe::egui::{
|
||||||
collapsing_header::CollapsingState, global_dark_light_mode_switch, CentralPanel, Context,
|
collapsing_header::CollapsingState, global_dark_light_mode_switch, CentralPanel, Context,
|
||||||
Layout, ScrollArea, SidePanel, TopBottomPanel,
|
Layout, ScrollArea, SidePanel, TopBottomPanel,
|
||||||
};
|
};
|
||||||
use eframe::emath::Align;
|
use eframe::emath::{Align, Pos2};
|
||||||
|
use std::ops::Index;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use self::data::source::{Metric, Panel, Source};
|
use self::data::source::{Metric, Panel, Source};
|
||||||
use self::data::ApplicationState;
|
use self::data::ApplicationState;
|
||||||
use self::gui::metric::metric_edit_ui;
|
use self::gui::metric::{metric_edit_ui, metric_display_ui};
|
||||||
use self::gui::panel::{panel_body_ui, panel_edit_inline_ui, panel_title_ui};
|
use self::gui::panel::{panel_body_ui, panel_edit_inline_ui, panel_title_ui};
|
||||||
use self::gui::source::{source_display_ui, source_edit_ui};
|
use self::gui::source::{source_display_ui, source_edit_ui};
|
||||||
use self::util::human_size;
|
use self::util::human_size;
|
||||||
|
@ -24,6 +26,8 @@ pub struct App {
|
||||||
input_metric: Metric,
|
input_metric: Metric,
|
||||||
input_source: Source,
|
input_source: Source,
|
||||||
input_panel: Panel,
|
input_panel: Panel,
|
||||||
|
deleting_metric: Option<usize>,
|
||||||
|
deleting_source: Option<usize>,
|
||||||
edit: bool,
|
edit: bool,
|
||||||
sources: bool,
|
sources: bool,
|
||||||
padding: bool,
|
padding: bool,
|
||||||
|
@ -36,6 +40,8 @@ impl App {
|
||||||
input_metric: Metric::default(),
|
input_metric: Metric::default(),
|
||||||
input_panel: Panel::default(),
|
input_panel: Panel::default(),
|
||||||
input_source: Source::default(),
|
input_source: Source::default(),
|
||||||
|
deleting_metric: None,
|
||||||
|
deleting_source: None,
|
||||||
edit: false,
|
edit: false,
|
||||||
sources: true,
|
sources: true,
|
||||||
padding: false,
|
padding: false,
|
||||||
|
@ -125,6 +131,60 @@ impl eframe::App for App {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
if let Some(index) = self.deleting_metric {
|
||||||
|
Window::new(format!("Delete Metric #{}", index))
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.heading("Are you sure you want to delete this metric?");
|
||||||
|
ui.label("This will remove all its metrics and delete all points from archive. This action CANNOT BE UNDONE!");
|
||||||
|
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("yes").clicked() {
|
||||||
|
let store = self.data.storage.lock().expect("Storage Mutex poisoned");
|
||||||
|
let mut metrics = self.data.metrics.write().expect("Metrics RwLock poisoned");
|
||||||
|
store.delete_metric(metrics[index].id).expect("Failed deleting metric");
|
||||||
|
store.delete_values(metrics[index].id).expect("Failed deleting values");
|
||||||
|
metrics.remove(index);
|
||||||
|
self.deleting_metric = None;
|
||||||
|
}
|
||||||
|
if ui.button(" no ").clicked() {
|
||||||
|
self.deleting_metric = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(index) = self.deleting_source {
|
||||||
|
Window::new(format!("Delete Source #{}", index)).show(ctx, |ui| {
|
||||||
|
ui.heading("Are you sure you want to delete this source?");
|
||||||
|
ui.label("This will remove all its metrics and delete all points from archive. This action CANNOT BE UNDONE!");
|
||||||
|
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("YEAH").clicked() {
|
||||||
|
let store = self.data.storage.lock().expect("Storage Mutex poisoned");
|
||||||
|
let mut sources = self.data.sources.write().expect("sources RwLock poisoned");
|
||||||
|
let mut metrics = self.data.metrics.write().expect("Metrics RwLock poisoned");
|
||||||
|
let mut to_remove = Vec::new();
|
||||||
|
for j in 0..metrics.len() {
|
||||||
|
if metrics[j].source_id == self.input_source.id {
|
||||||
|
store.delete_values(metrics[j].id).expect("Failed deleting values");
|
||||||
|
store.delete_metric(metrics[j].id).expect("Failed deleting Metric");
|
||||||
|
to_remove.push(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index in to_remove {
|
||||||
|
metrics.remove(index);
|
||||||
|
}
|
||||||
|
store.delete_source(sources[index].id).expect("Failed deleting source");
|
||||||
|
sources.remove(index);
|
||||||
|
self.deleting_source = None;
|
||||||
|
}
|
||||||
|
if ui.button(" NO WAY ").clicked() {
|
||||||
|
self.deleting_source = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
if self.sources {
|
if self.sources {
|
||||||
let mut to_swap: Option<usize> = None;
|
let mut to_swap: Option<usize> = None;
|
||||||
// let mut to_delete: Option<usize> = None;
|
// let mut to_delete: Option<usize> = None;
|
||||||
|
@ -142,19 +202,19 @@ impl eframe::App for App {
|
||||||
let sources_count = sources.len();
|
let sources_count = sources.len();
|
||||||
ui.heading("Sources");
|
ui.heading("Sources");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
for (index, source) in sources.iter_mut().enumerate() {
|
for (i, source) in sources.iter_mut().enumerate() {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if self.edit {
|
if self.edit {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
if ui.small_button("+").clicked() {
|
if ui.small_button("+").clicked() {
|
||||||
if index > 0 {
|
if i > 0 {
|
||||||
to_swap = Some(index); // TODO kinda jank but is there a better way?
|
to_swap = Some(i); // TODO kinda jank but is there a better way?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.small_button("−").clicked() {
|
if ui.small_button("−").clicked() {
|
||||||
if index < sources_count - 1 {
|
if i < sources_count - 1 {
|
||||||
to_swap = Some(index + 1); // TODO kinda jank but is there a better way?
|
to_swap = Some(i + 1); // TODO kinda jank but is there a better way?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -163,26 +223,36 @@ impl eframe::App for App {
|
||||||
let remaining_width = ui.available_width();
|
let remaining_width = ui.available_width();
|
||||||
if self.edit {
|
if self.edit {
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
source_edit_ui(
|
ui.horizontal(|ui| {
|
||||||
ui,
|
source_edit_ui(
|
||||||
source,
|
ui,
|
||||||
Some(&mut *self.data.metrics.write().expect("Metrics RwLock poisoned")),
|
source,
|
||||||
&panels,
|
remaining_width - 34.0,
|
||||||
remaining_width,
|
);
|
||||||
);
|
if ui.small_button("×").clicked() {
|
||||||
|
self.deleting_metric = None;
|
||||||
|
self.deleting_source = Some(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (j, metric) in self.data.metrics.write().expect("Metrics RwLock poisoned").iter_mut().enumerate() {
|
||||||
|
if metric.source_id == source.id {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
metric_edit_ui(ui, metric, Some(&panels), remaining_width - 31.0);
|
||||||
|
if ui.small_button("×").clicked() {
|
||||||
|
self.deleting_source = None;
|
||||||
|
self.deleting_metric = Some(j);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
metric_edit_ui(
|
metric_edit_ui(
|
||||||
ui,
|
ui,
|
||||||
&mut self.input_metric,
|
&mut self.input_metric,
|
||||||
None,
|
None,
|
||||||
remaining_width - 10.0,
|
remaining_width - 30.0,
|
||||||
);
|
);
|
||||||
ui.add_space(5.0);
|
if ui.small_button(" + ").clicked() { // TODO find a better
|
||||||
if ui.button(" × ").clicked() {
|
|
||||||
self.input_metric = Metric::default();
|
|
||||||
}
|
|
||||||
ui.separator();
|
|
||||||
if ui.button(" + ").clicked() {
|
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
.data
|
.data
|
||||||
.add_metric(&self.input_metric, source)
|
.add_metric(&self.input_metric, source)
|
||||||
|
@ -190,6 +260,10 @@ impl eframe::App for App {
|
||||||
error!(target: "ui", "Error adding metric : {:?}", e);
|
error!(target: "ui", "Error adding metric : {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ui.add_space(1.0); // DAMN!
|
||||||
|
if ui.small_button("×").clicked() {
|
||||||
|
self.input_metric = Metric::default();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -198,9 +272,13 @@ impl eframe::App for App {
|
||||||
source_display_ui(
|
source_display_ui(
|
||||||
ui,
|
ui,
|
||||||
source,
|
source,
|
||||||
&metrics,
|
|
||||||
remaining_width,
|
remaining_width,
|
||||||
);
|
);
|
||||||
|
for metric in metrics.iter() {
|
||||||
|
if metric.source_id == source.id {
|
||||||
|
metric_display_ui(ui, metric, ui.available_width());
|
||||||
|
}
|
||||||
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -227,9 +305,7 @@ impl eframe::App for App {
|
||||||
source_edit_ui(
|
source_edit_ui(
|
||||||
ui,
|
ui,
|
||||||
&mut self.input_source,
|
&mut self.input_source,
|
||||||
None,
|
panel_width - 10.0,
|
||||||
&panels,
|
|
||||||
panel_width,
|
|
||||||
);
|
);
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
if self.padding {
|
if self.padding {
|
||||||
|
|
Loading…
Reference in a new issue