From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 6CF881FF165 for ; Thu, 23 Oct 2025 10:33:06 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C4BC23962; Thu, 23 Oct 2025 10:33:33 +0200 (CEST) From: Dominik Csapak To: pdm-devel@lists.proxmox.com Date: Thu, 23 Oct 2025 10:28:14 +0200 Message-ID: <20251023083253.1038119-11-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251023083253.1038119-1-d.csapak@proxmox.com> References: <20251023083253.1038119-1-d.csapak@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -1.022 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_MAILER 2 Automated Mailer Tag Left in Email PROLO_LEO1 0.1 Meta Catches all Leo drug variations so far SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pdm-devel] [PATCH datacenter-manager v2 10/16] ui: dashboard: refactor DashboardConfig editing/constants to their module X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Datacenter Manager development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" makes it easier to use outside the dashboard struct. Since it's not really a 'dashboard configuration' but rather 'refresh configuration', rename it as such. Signed-off-by: Dominik Csapak --- ui/src/dashboard/mod.rs | 129 +++++------------------- ui/src/dashboard/refresh_config_edit.rs | 107 ++++++++++++++++++++ 2 files changed, 130 insertions(+), 106 deletions(-) create mode 100644 ui/src/dashboard/refresh_config_edit.rs diff --git a/ui/src/dashboard/mod.rs b/ui/src/dashboard/mod.rs index bb6b4049..fc2e150a 100644 --- a/ui/src/dashboard/mod.rs +++ b/ui/src/dashboard/mod.rs @@ -1,30 +1,25 @@ -use std::{collections::HashMap, rc::Rc}; +use std::rc::Rc; use anyhow::Error; use futures::join; use js_sys::Date; -use serde::{Deserialize, Serialize}; use serde_json::json; use yew::{ virtual_dom::{VComp, VNode}, Component, }; -use proxmox_yew_comp::{http_get, EditWindow}; +use proxmox_yew_comp::http_get; use pwt::{ css::{AlignItems, FlexDirection, FlexFit, FlexWrap, JustifyContent}, prelude::*, props::StorageLocation, state::PersistentState, - widget::{ - form::{DisplayField, FormContext, Number}, - Column, Container, Fa, InputPanel, Panel, Row, - }, + widget::{form::FormContext, Column, Container, Fa, Panel, Row}, AsyncPool, }; use pdm_api_types::{remotes::RemoteType, resource::ResourcesStatus, TaskStatistics}; -use proxmox_client::ApiResponseData; use crate::{pve::GuestType, remotes::AddWizard, RemoteList}; @@ -56,28 +51,15 @@ use tasks::create_task_summary_panel; pub mod types; -/// The initial 'max-age' parameter in seconds. The backend polls every 15 minutes, so to increase -/// the chance of showing some data quickly use that as max age at the very first load. -pub const INITIAL_MAX_AGE_S: u64 = 900; -/// The 'max-age' parameter in seconds for when user forces a reload. Do not use 0 as the data will -/// never be realtime anyway, with 5s we get very current data while avoiding that one or more -/// "fidgety" users put unbounded load onto the remotes. -pub const FORCE_RELOAD_MAX_AGE_S: u64 = 3; - -/// The default 'max-age' parameter in seconds. The backend polls every 15 minutes, but if a user -/// has the dashboard active for a longer time it's beneficial to refresh a bit more often, forcing -/// new data twice a minute is a good compromise. -pub const DEFAULT_MAX_AGE_S: u64 = 30; - -/// The default refresh interval, we poll more frequently than the default max-age to quicker show -/// any new data that was gathered either by the backend polling tasks or by a manual update -/// triggered by another user. -pub const DEFAULT_REFRESH_INTERVAL_S: u32 = 10; - -/// The default hours to show for task summaries. Use 2 days to ensure that all tasks from yesterday -/// are included independent from the time a user checks the dashboard on the current day. -pub const DEFAULT_TASK_SUMMARY_HOURS: u32 = 48; +mod refresh_config_edit; +pub use refresh_config_edit::{ + create_refresh_config_edit_window, refresh_config_id, RefreshConfig, +}; +use refresh_config_edit::{ + DEFAULT_MAX_AGE_S, DEFAULT_REFRESH_INTERVAL_S, DEFAULT_TASK_SUMMARY_HOURS, + FORCE_RELOAD_MAX_AGE_S, INITIAL_MAX_AGE_S, +}; #[derive(Properties, PartialEq)] pub struct Dashboard {} @@ -94,17 +76,6 @@ impl Default for Dashboard { } } -#[derive(Serialize, Deserialize, Default, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct DashboardConfig { - #[serde(skip_serializing_if = "Option::is_none")] - refresh_interval: Option, - #[serde(skip_serializing_if = "Option::is_none")] - max_age: Option, - #[serde(skip_serializing_if = "Option::is_none")] - task_last_hours: Option, -} - pub enum LoadingResult { Resources(Result), TopEntities(Result), @@ -118,7 +89,7 @@ pub enum Msg { CreateWizard(Option), Reload, ForceReload, - UpdateConfig(DashboardConfig), + UpdateConfig(RefreshConfig), ConfigWindow(bool), } @@ -141,7 +112,7 @@ pub struct PdmDashboard { show_config_window: bool, _context_listener: ContextHandle, async_pool: AsyncPool, - config: PersistentState, + config: PersistentState, } impl PdmDashboard { @@ -197,7 +168,7 @@ impl PdmDashboard { }); } - fn get_task_options(config: &PersistentState) -> (u32, i64) { + fn get_task_options(config: &PersistentState) -> (u32, i64) { let hours = config.task_last_hours.unwrap_or(DEFAULT_TASK_SUMMARY_HOURS); let since = (Date::now() / 1000.0) as i64 - (hours * 60 * 60) as i64; (hours, since) @@ -209,8 +180,8 @@ impl Component for PdmDashboard { type Properties = Dashboard; fn create(ctx: &yew::Context) -> Self { - let config: PersistentState = - PersistentState::new(StorageLocation::local("dashboard-config")); + let config: PersistentState = + PersistentState::new(StorageLocation::local(refresh_config_id("dashboard"))); let async_pool = AsyncPool::new(); let (remote_list, _context_listener) = ctx @@ -520,75 +491,21 @@ impl Component for PdmDashboard { Panel::new() .class(FlexFit) .with_child(content) - .with_optional_child( - self.show_wizard.map(|remote_type| { - AddWizard::new(remote_type) - .on_close(ctx.link().callback(|_| Msg::CreateWizard(None))) - .on_submit(move |ctx| { - crate::remotes::create_remote(ctx, remote_type) - }) - }), - ) + .with_optional_child(self.show_wizard.map(|remote_type| { + AddWizard::new(remote_type) + .on_close(ctx.link().callback(|_| Msg::CreateWizard(None))) + .on_submit(move |ctx| crate::remotes::create_remote(ctx, remote_type)) + })) .with_optional_child( self.show_config_window.then_some( - EditWindow::new(tr!("Dashboard Configuration")) - .submit_text(tr!("Save")) - .loader({ - || { - let data: PersistentState = PersistentState::new( - StorageLocation::local("dashboard-config"), - ); - - async move { - let data = serde_json::to_value(data.into_inner())?; - Ok(ApiResponseData { - attribs: HashMap::new(), - data, - }) - } - } - }) - .renderer(|_ctx: &FormContext| { - InputPanel::new() - .width(600) - .padding(2) - .with_field( - tr!("Refresh Interval (seconds)"), - Number::new() - .name("refresh-interval") - .min(5u64) - .step(5) - .placeholder(DEFAULT_REFRESH_INTERVAL_S.to_string()), - ) - .with_field( - tr!("Max Age (seconds)"), - Number::new() - .name("max-age") - .min(0u64) - .step(5) - .placeholder(DEFAULT_MAX_AGE_S.to_string()), - ) - .with_field( - "", - DisplayField::new() - .key("max-age-explanation") - .value(tr!("If a response from a remote is older than 'Max Age', it will be updated on the next refresh."))) - .with_field( - tr!("Task Summary Time Range (last hours)"), - Number::new() - .name("task-last-hours") - .min(0u64) - .placeholder(DEFAULT_TASK_SUMMARY_HOURS.to_string()), - ) - .into() - }) + create_refresh_config_edit_window("dashboard") .on_close(ctx.link().callback(|_| Msg::ConfigWindow(false))) .on_submit({ let link = ctx.link().clone(); move |ctx: FormContext| { let link = link.clone(); async move { - let data: DashboardConfig = + let data: RefreshConfig = serde_json::from_value(ctx.get_submit_data())?; link.send_message(Msg::UpdateConfig(data)); Ok(()) diff --git a/ui/src/dashboard/refresh_config_edit.rs b/ui/src/dashboard/refresh_config_edit.rs new file mode 100644 index 00000000..9bd0c884 --- /dev/null +++ b/ui/src/dashboard/refresh_config_edit.rs @@ -0,0 +1,107 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use pwt::prelude::*; +use pwt::props::StorageLocation; +use pwt::state::PersistentState; +use pwt::widget::form::{DisplayField, FormContext, Number}; +use pwt::widget::InputPanel; + +use proxmox_client::ApiResponseData; +use proxmox_yew_comp::EditWindow; + +/// The initial 'max-age' parameter in seconds. The backend polls every 15 minutes, so to increase +/// the chance of showing some data quickly use that as max age at the very first load. +pub const INITIAL_MAX_AGE_S: u64 = 900; + +/// The 'max-age' parameter in seconds for when user forces a reload. Do not use 0 as the data will +/// never be realtime anyway, with 5s we get very current data while avoiding that one or more +/// "fidgety" users put unbounded load onto the remotes. +pub const FORCE_RELOAD_MAX_AGE_S: u64 = 3; + +/// The default 'max-age' parameter in seconds. The backend polls every 15 minutes, but if a user +/// has the dashboard active for a longer time it's beneficial to refresh a bit more often, forcing +/// new data twice a minute is a good compromise. +pub const DEFAULT_MAX_AGE_S: u64 = 30; + +/// The default refresh interval, we poll more frequently than the default max-age to quicker show +/// any new data that was gathered either by the backend polling tasks or by a manual update +/// triggered by another user. +pub const DEFAULT_REFRESH_INTERVAL_S: u32 = 10; + +/// The default hours to show for task summaries. Use 2 days to ensure that all tasks from yesterday +/// are included independent from the time a user checks the dashboard on the current day. +pub const DEFAULT_TASK_SUMMARY_HOURS: u32 = 48; + +#[derive(Serialize, Deserialize, Default, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct RefreshConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_interval: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_age: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub task_last_hours: Option, +} + +/// Get a consistent id for use in a local storage +pub fn refresh_config_id(id: &str) -> String { + format!("view-{id}-config") +} + +pub fn create_refresh_config_edit_window(id: &str) -> EditWindow { + let id = refresh_config_id(id); + EditWindow::new(tr!("Refresh Configuration")) + .submit_text(tr!("Save")) + .loader({ + move || { + let id = id.clone(); + let data: PersistentState = PersistentState::new( + StorageLocation::local(id), + ); + + async move { + let data = serde_json::to_value(data.into_inner())?; + Ok(ApiResponseData { + attribs: HashMap::new(), + data, + }) + } + } + }) + .renderer(|_ctx: &FormContext| { + InputPanel::new() + .width(600) + .padding(2) + .with_field( + tr!("Refresh Interval (seconds)"), + Number::new() + .name("refresh-interval") + .min(5u64) + .step(5) + .placeholder(DEFAULT_REFRESH_INTERVAL_S.to_string()), + ) + .with_field( + tr!("Max Age (seconds)"), + Number::new() + .name("max-age") + .min(0u64) + .step(5) + .placeholder(DEFAULT_MAX_AGE_S.to_string()), + ) + .with_field( + "", + DisplayField::new() + .key("max-age-explanation") + .value(tr!("If a response from a remote is older than 'Max Age', it will be updated on the next refresh."))) + .with_field( + tr!("Task Summary Time Range (last hours)"), + Number::new() + .name("task-last-hours") + .min(0u64) + .placeholder(DEFAULT_TASK_SUMMARY_HOURS.to_string()), + ) + .into() + }) +} -- 2.47.3 _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel