public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Shannon Sterz" <s.sterz@proxmox.com>
To: "Dominik Csapak" <d.csapak@proxmox.com>
Cc: Proxmox Datacenter Manager development discussion
	<pdm-devel@lists.proxmox.com>
Subject: Re: [pdm-devel] [RFC PATCH datacenter-manager v2 16/16] ui: dashboard: use 'View' instead of the Dashboard
Date: Thu, 23 Oct 2025 13:19:56 +0200	[thread overview]
Message-ID: <DDPNTDSROMCZ.C5XNUI7XW1JL@proxmox.com> (raw)
In-Reply-To: <20251023083253.1038119-17-d.csapak@proxmox.com>

On Thu Oct 23, 2025 at 10:28 AM CEST, Dominik Csapak wrote:
> this uses our new `View` with a (currently) static configuration to
> replicate our Dashboard. Since all functionality of that is available
> in the View, the `Dashboard` struct can be removed.
>
> This should be functionally the same as before.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> changes from v1:
> * correctly mark as RFC
>
>  ui/src/dashboard/mod.rs  | 486 +--------------------------------------
>  ui/src/dashboard/view.rs |  61 ++++-
>  ui/src/lib.rs            |   2 +-
>  ui/src/main_menu.rs      |   5 +-
>  4 files changed, 76 insertions(+), 478 deletions(-)
>
> diff --git a/ui/src/dashboard/mod.rs b/ui/src/dashboard/mod.rs
> index 8a0bdad0..8e979417 100644
> --- a/ui/src/dashboard/mod.rs
> +++ b/ui/src/dashboard/mod.rs
> @@ -1,27 +1,6 @@
> -use std::rc::Rc;
> -
> -use anyhow::Error;
> -use futures::join;
> -use js_sys::Date;
> -use serde_json::json;
> -use yew::{
> -    virtual_dom::{VComp, VNode},
> -    Component,
> -};
> -
> -use proxmox_yew_comp::http_get;
> -use pwt::{
> -    css::{AlignItems, FlexDirection, FlexFit, FlexWrap, JustifyContent},
> -    prelude::*,
> -    props::StorageLocation,
> -    state::PersistentState,
> -    widget::{form::FormContext, Column, Container, Fa, Panel, Row},
> -    AsyncPool,
> -};
> -
> -use pdm_api_types::{remotes::RemoteType, resource::ResourcesStatus, TaskStatistics};
> -
> -use crate::{pve::GuestType, remotes::AddWizard};
> +use pwt::css;
> +use pwt::prelude::*;
> +use pwt::widget::{Column, Fa, Row};
>
>  mod top_entities;
>  pub use top_entities::create_top_entities_panel;
> @@ -39,477 +18,36 @@ mod guest_panel;
>  pub use guest_panel::create_guest_panel;
>
>  mod sdn_zone_panel;
> -use sdn_zone_panel::create_sdn_panel;
> +pub use sdn_zone_panel::create_sdn_panel;
>
>  mod status_row;
> -use status_row::DashboardStatusRow;
> +pub use status_row::DashboardStatusRow;
>
>  mod filtered_tasks;
>
>  mod tasks;
> -use tasks::{create_task_summary_panel, get_task_options};
> +pub use tasks::create_task_summary_panel;
>
>  pub mod types;
>
>  pub mod view;
>
>  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, FORCE_RELOAD_MAX_AGE_S, INITIAL_MAX_AGE_S,
> -};
> -
> -#[derive(Properties, PartialEq)]
> -pub struct Dashboard {}
> -
> -impl Dashboard {
> -    pub fn new() -> Self {
> -        yew::props!(Self {})
> -    }
> -}
> -
> -impl Default for Dashboard {
> -    fn default() -> Self {
> -        Self::new()
> -    }
> -}
> -
> -pub enum LoadingResult {
> -    Resources(Result<ResourcesStatus, Error>),
> -    TopEntities(Result<pdm_client::types::TopEntities, proxmox_client::Error>),
> -    TaskStatistics(Result<TaskStatistics, Error>),
> -    All,
> -}
> -
> -pub enum Msg {
> -    LoadingFinished(LoadingResult),
> -    CreateWizard(Option<RemoteType>),
> -    Reload,
> -    ForceReload,
> -    UpdateConfig(RefreshConfig),
> -    ConfigWindow(bool),
> -}
> -
> -struct StatisticsOptions {
> -    data: Option<TaskStatistics>,
> -    error: Option<Error>,
> -}
> -
> -pub struct PdmDashboard {
> -    status: Option<ResourcesStatus>,
> -    last_error: Option<Error>,
> -    top_entities: Option<pdm_client::types::TopEntities>,
> -    last_top_entities_error: Option<proxmox_client::Error>,
> -    statistics: StatisticsOptions,
> -    load_finished_time: Option<f64>,
> -    show_wizard: Option<RemoteType>,
> -    show_config_window: bool,
> -    async_pool: AsyncPool,
> -    config: PersistentState<RefreshConfig>,
> -}
> -
> -impl PdmDashboard {
> -    fn reload(&mut self, ctx: &yew::Context<Self>) {
> -        let max_age = if self.load_finished_time.is_some() {
> -            self.config.max_age.unwrap_or(DEFAULT_MAX_AGE_S)
> -        } else {
> -            INITIAL_MAX_AGE_S
> -        };
> -        self.do_reload(ctx, max_age)
> -    }
> -
> -    fn do_reload(&mut self, ctx: &yew::Context<Self>, max_age: u64) {
> -        let link = ctx.link().clone();
> -        let (_, since) = get_task_options(self.config.task_last_hours);
> -
> -        self.async_pool.spawn(async move {
> -            let client = crate::pdm_client();
> -
> -            let top_entities_future = {
> -                let link = link.clone();
> -                async move {
> -                    let res = client.get_top_entities().await;
> -                    link.send_message(Msg::LoadingFinished(LoadingResult::TopEntities(res)));
> -                }
> -            };
> -            let status_future = {
> -                let link = link.clone();
> -                async move {
> -                    let res: Result<ResourcesStatus, _> =
> -                        http_get("/resources/status", Some(json!({"max-age": max_age}))).await;
> -                    link.send_message(Msg::LoadingFinished(LoadingResult::Resources(res)));
> -                }
> -            };
> -
> -            let params = Some(json!({
> -                "since": since,
> -                "limit": 0,
> -            }));
> -
> -            // TODO replace with pdm client call
> -            let statistics_future = {
> -                let link = link.clone();
> -                async move {
> -                    let res: Result<TaskStatistics, _> =
> -                        http_get("/remote-tasks/statistics", params).await;
> -                    link.send_message(Msg::LoadingFinished(LoadingResult::TaskStatistics(res)));
> -                }
> -            };
> -            join!(top_entities_future, status_future, statistics_future);
> -            link.send_message(Msg::LoadingFinished(LoadingResult::All));
> -        });
> -    }
> -}
> -
> -impl Component for PdmDashboard {
> -    type Message = Msg;
> -    type Properties = Dashboard;
> -
> -    fn create(ctx: &yew::Context<Self>) -> Self {
> -        let config: PersistentState<RefreshConfig> =
> -            PersistentState::new(StorageLocation::local(refresh_config_id("dashboard")));
> -        let async_pool = AsyncPool::new();
> -
> -        let mut this = Self {
> -            status: None,
> -            last_error: None,
> -            top_entities: None,
> -            last_top_entities_error: None,
> -            statistics: StatisticsOptions {
> -                data: None,
> -                error: None,
> -            },
> -            load_finished_time: None,
> -            show_wizard: None,
> -            show_config_window: false,
> -            async_pool,
> -            config,
> -        };
> -
> -        this.reload(ctx);
> -
> -        this
> -    }
> -
> -    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
> -        match msg {
> -            Msg::LoadingFinished(res) => {
> -                match res {
> -                    LoadingResult::Resources(resources_status) => match resources_status {
> -                        Ok(status) => {
> -                            self.last_error = None;
> -                            self.status = Some(status);
> -                        }
> -                        Err(err) => self.last_error = Some(err),
> -                    },
> -                    LoadingResult::TopEntities(top_entities) => match top_entities {
> -                        Ok(data) => {
> -                            self.last_top_entities_error = None;
> -                            self.top_entities = Some(data);
> -                        }
> -                        Err(err) => self.last_top_entities_error = Some(err),
> -                    },
> -
> -                    LoadingResult::TaskStatistics(task_statistics) => match task_statistics {
> -                        Ok(statistics) => {
> -                            self.statistics.error = None;
> -                            self.statistics.data = Some(statistics);
> -                        }
> -                        Err(err) => self.statistics.error = Some(err),
> -                    },
> -                    LoadingResult::All => {
> -                        if self.load_finished_time.is_none() {
> -                            // immediately trigger a "normal" reload after the first load with the
> -                            // configured or default max-age to ensure users sees more current data.
> -                            ctx.link().send_message(Msg::Reload);
> -                        }
> -                        self.load_finished_time = Some(Date::now() / 1000.0);
> -                    }
> -                }
> -                true
> -            }
> -            Msg::CreateWizard(remote_type) => {
> -                self.show_wizard = remote_type;
> -                true
> -            }
> -            Msg::Reload => {
> -                self.reload(ctx);
> -                true
> -            }
> -            Msg::ForceReload => {
> -                self.do_reload(ctx, FORCE_RELOAD_MAX_AGE_S);
> -                true
> -            }
> -            Msg::ConfigWindow(show) => {
> -                self.show_config_window = show;
> -                true
> -            }
> -            Msg::UpdateConfig(dashboard_config) => {
> -                let (old_hours, _) = get_task_options(self.config.task_last_hours);
> -                self.config.update(dashboard_config);
> -                let (new_hours, _) = get_task_options(self.config.task_last_hours);
> -
> -                if old_hours != new_hours {
> -                    self.reload(ctx);
> -                }
> -
> -                self.show_config_window = false;
> -                true
> -            }
> -        }
> -    }
> -
> -    fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
> -        let (hours, since) = get_task_options(self.config.task_last_hours);
> -        let content = Column::new()
> -            .class(FlexFit)
> -            .with_child(
> -                Container::new()
> -                    .class("pwt-content-spacer-padding")
> -                    .class("pwt-content-spacer-colors")
> -                    .style("color", "var(--pwt-color)")
> -                    .style("background-color", "var(--pwt-color-background)")
> -                    .with_child(DashboardStatusRow::new(
> -                        self.load_finished_time,
> -                        self.config
> -                            .refresh_interval
> -                            .unwrap_or(DEFAULT_REFRESH_INTERVAL_S),
> -                        ctx.link()
> -                            .callback(|force| if force { Msg::ForceReload } else { Msg::Reload }),
> -                        ctx.link().callback(|_| Msg::ConfigWindow(true)),
> -                    )),
> -            )
> -            .with_child(
> -                Container::new()
> -                    .class("pwt-content-spacer")
> -                    .class(FlexDirection::Row)
> -                    .class(FlexWrap::Wrap)
> -                    .padding_top(0)
> -                    .with_child(
> -                        create_remote_panel(
> -                            self.status.clone(),
> -                            Some(
> -                                ctx.link()
> -                                    .callback(|_| Msg::CreateWizard(Some(RemoteType::Pve))),
> -                            ),
> -                            Some(
> -                                ctx.link()
> -                                    .callback(|_| Msg::CreateWizard(Some(RemoteType::Pbs))),
> -                            ),
> -                        )
> -                        .flex(1.0)
> -                        .width(300)
> -                        .min_height(175),
> -                    )
> -                    .with_child(
> -                        create_node_panel(Some(RemoteType::Pve), self.status.clone())
> -                            .flex(1.0)
> -                            .width(300),
> -                    )
> -                    .with_child(
> -                        create_guest_panel(Some(GuestType::Qemu), self.status.clone())
> -                            .flex(1.0)
> -                            .width(300),
> -                    )
> -                    .with_child(
> -                        create_guest_panel(Some(GuestType::Lxc), self.status.clone())
> -                            .flex(1.0)
> -                            .width(300),
> -                    )
> -                    // FIXME: add PBS support

this patch removes these FIXMEs without really addressing the issue. i
understand that this is just due to showcasing how the new view feature
can replace the dashboard, but it'd be nice to preserve them somewhere
imo.

> -                    //.with_child(self.create_node_panel(
> -                    //    "building-o",
> -                    //    tr!("Backup Server Nodes"),
> -                    //    &self.status.pbs_nodes,
> -                    //))
> -                    //.with_child(
> -                    //    Panel::new()
> -                    //        .flex(1.0)
> -                    //        .width(300)
> -                    //        .title(create_title_with_icon(
> -                    //            "floppy-o",
> -                    //            tr!("Backup Server Datastores"),
> -                    //        ))
> -                    //        .border(true)
> -                    //        .with_child(if self.loading {
> -                    //            Column::new()
> -                    //                .padding(4)
> -                    //                .class(FlexFit)
> -                    //                .class(JustifyContent::Center)
> -                    //                .class(AlignItems::Center)
> -                    //                .with_child(html! {<i class={"pwt-loading-icon"} />})
> -                    //        } else {
> -                    //            Column::new()
> -                    //                .padding(4)
> -                    //                .class(FlexFit)
> -                    //                .class(JustifyContent::Center)
> -                    //                .gap(2)
> -                    //                // FIXME: show more detailed status (usage?)
> -                    //                .with_child(
> -                    //                    Row::new()
> -                    //                        .gap(2)
> -                    //                        .with_child(
> -                    //                            StorageState::Available.to_fa_icon().fixed_width(),
> -                    //                        )
> -                    //                        .with_child(tr!("available"))
> -                    //                        .with_flex_spacer()
> -                    //                        .with_child(
> -                    //                            Container::from_tag("span").with_child(
> -                    //                                self.status.pbs_datastores.available,
> -                    //                            ),
> -                    //                        ),
> -                    //                )
> -                    //                .with_optional_child(
> -                    //                    (self.status.pbs_datastores.unknown > 0).then_some(
> -                    //                        Row::new()
> -                    //                            .gap(2)
> -                    //                            .with_child(
> -                    //                                StorageState::Unknown
> -                    //                                    .to_fa_icon()
> -                    //                                    .fixed_width(),
> -                    //                            )
> -                    //                            .with_child(tr!("unknown"))
> -                    //                            .with_flex_spacer()
> -                    //                            .with_child(
> -                    //                                Container::from_tag("span").with_child(
> -                    //                                    self.status.pbs_datastores.unknown,
> -                    //                                ),
> -                    //                            ),
> -                    //                    ),
> -                    //                )
> -                    //        }),
> -                    //)
> -                    .with_child(
> -                        create_subscription_panel()
> -                            .flex(1.0)
> -                            .width(500)
> -                            .min_height(150),
> -                    ),
> -            )
> -            .with_child(
> -                Container::new()
> -                    .class("pwt-content-spacer")
> -                    .class(FlexDirection::Row)
> -                    .class("pwt-align-content-start")
> -                    .padding_top(0)
> -                    .class(FlexWrap::Wrap)
> -                    //.min_height(175)
> -                    .with_child(
> -                        create_top_entities_panel(
> -                            self.top_entities.as_ref().map(|e| e.guest_cpu.clone()),
> -                            self.last_top_entities_error.as_ref(),
> -                            types::LeaderboardType::GuestCpu,
> -                        )
> -                        .flex(1.0)
> -                        .width(500)
> -                        .min_width(400),
> -                    )
> -                    .with_child(
> -                        create_top_entities_panel(
> -                            self.top_entities.as_ref().map(|e| e.node_cpu.clone()),
> -                            self.last_top_entities_error.as_ref(),
> -                            types::LeaderboardType::NodeCpu,
> -                        )
> -                        .flex(1.0)
> -                        .width(500)
> -                        .min_width(400),
> -                    )
> -                    .with_child(
> -                        create_top_entities_panel(
> -                            self.top_entities.as_ref().map(|e| e.node_memory.clone()),
> -                            self.last_top_entities_error.as_ref(),
> -                            types::LeaderboardType::NodeCpu,
> -                        )
> -                        .flex(1.0)
> -                        .width(500)
> -                        .min_width(400),
> -                    ),
> -            )
> -            .with_child(
> -                Container::new()
> -                    .class("pwt-content-spacer")
> -                    .class(FlexDirection::Row)
> -                    .class("pwt-align-content-start")
> -                    .style("padding-top", "0")
> -                    .class(pwt::css::Flex::Fill)
> -                    .class(FlexWrap::Wrap)
> -                    .with_child(
> -                        create_task_summary_panel(
> -                            self.statistics.data.clone(),
> -                            self.statistics.error.as_ref(),
> -                            None,
> -                            hours,
> -                            since,
> -                        )
> -                        .flex(1.0)
> -                        .width(500),
> -                    )
> -                    .with_child(
> -                        create_task_summary_panel(
> -                            self.statistics.data.clone(),
> -                            self.statistics.error.as_ref(),
> -                            Some(5),
> -                            hours,
> -                            since,
> -                        )
> -                        .flex(1.0)
> -                        .width(500),
> -                    )
> -                    .with_child(create_sdn_panel(self.status.clone()).flex(1.0).width(200)),
> -            );
> -
> -        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_config_window.then_some(
> -                    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: RefreshConfig =
> -                                        serde_json::from_value(ctx.get_submit_data())?;
> -                                    link.send_message(Msg::UpdateConfig(data));
> -                                    Ok(())
> -                                }
> -                            }
> -                        }),
> -                ),
> -            )
> -            .into()
> -    }
> -}
> -
> -impl From<Dashboard> for VNode {
> -    fn from(val: Dashboard) -> Self {
> -        let comp = VComp::new::<PdmDashboard>(Rc::new(val), None);
> -        VNode::from(comp)
> -    }
> -}
> +pub use refresh_config_edit::create_refresh_config_edit_window;
>
>  fn loading_column() -> Column {
>      Column::new()
>          .padding(4)
> -        .class(FlexFit)
> -        .class(JustifyContent::Center)
> -        .class(AlignItems::Center)
> +        .class(css::FlexFit)
> +        .class(css::JustifyContent::Center)
> +        .class(css::AlignItems::Center)
>          .with_child(html! {<i class={"pwt-loading-icon"} />})
>  }
>
>  /// Create a consistent title component for the given title and icon
> -pub fn create_title_with_icon(icon: &str, title: String) -> Html {
> +fn create_title_with_icon(icon: &str, title: String) -> Html {
>      Row::new()
> -        .class(AlignItems::Center)
> +        .class(css::AlignItems::Center)
>          .gap(2)
>          .with_child(Fa::new(icon))
>          .with_child(title)
> diff --git a/ui/src/dashboard/view.rs b/ui/src/dashboard/view.rs
> index 7159d4e8..6cea9f87 100644
> --- a/ui/src/dashboard/view.rs
> +++ b/ui/src/dashboard/view.rs
> @@ -446,7 +446,66 @@ async fn load_template() -> Result<ViewTemplate, Error> {
>            \"description\": \"some description\",
>            \"layout\": {
>              \"layout-type\": \"rows\",
> -            \"rows\": []
> +            \"rows\": [
> +              [
> +                {
> +                  \"flex\": 3.0,
> +                  \"widget-type\": \"remotes\",
> +                  \"show-wizard\": true
> +                },
> +                {
> +                  \"flex\": 3.0,
> +                  \"widget-type\": \"nodes\",
> +                  \"remote-type\": \"pve\"
> +                },
> +                {
> +                  \"flex\": 3.0,
> +                  \"widget-type\": \"guests\",
> +                  \"guest-type\": \"qemu\"
> +                },
> +                {
> +                  \"flex\": 3.0,
> +                  \"widget-type\": \"guests\",
> +                  \"guest-type\": \"lxc\"
> +                },
> +                {
> +                  \"flex\": 5.0,
> +                  \"widget-type\": \"subscription\"
> +                }
> +              ],
> +              [
> +                {
> +                  \"widget-type\": \"leaderboard\",
> +                  \"leaderboard-type\": \"guest-cpu\"
> +                },
> +                {
> +                  \"widget-type\": \"leaderboard\",
> +                  \"leaderboard-type\": \"node-cpu\"
> +                },
> +                {
> +                  \"widget-type\": \"leaderboard\",
> +                  \"leaderboard-type\": \"node-memory\"
> +                }
> +              ],
> +              [
> +                {
> +                  \"flex\": 5.0,
> +                  \"widget-type\": \"task-summary\",
> +                  \"grouping\": \"category\",
> +                  \"sorting\": \"default\"
> +                },
> +                {
> +                  \"flex\": 5.0,
> +                  \"widget-type\": \"task-summary\",
> +                  \"grouping\": \"remote\",
> +                  \"sorting\": \"failed-tasks\"
> +                },
> +                {
> +                  \"flex\": 2.0,
> +                  \"widget-type\": \"sdn\"
> +                }
> +              ]
> +            ]
>            }
>          }
>      ";
> diff --git a/ui/src/lib.rs b/ui/src/lib.rs
> index de76e1c0..f9af023d 100644
> --- a/ui/src/lib.rs
> +++ b/ui/src/lib.rs
> @@ -27,7 +27,7 @@ mod search_provider;
>  pub use search_provider::SearchProvider;
>
>  mod dashboard;
> -pub use dashboard::Dashboard;
> +
>  use yew_router::prelude::RouterScopeExt;
>
>  mod widget;
> diff --git a/ui/src/main_menu.rs b/ui/src/main_menu.rs
> index 7650b63f..b5044169 100644
> --- a/ui/src/main_menu.rs
> +++ b/ui/src/main_menu.rs
> @@ -13,11 +13,12 @@ use proxmox_yew_comp::{NotesView, XTermJs};
>
>  use pdm_api_types::remotes::RemoteType;
>
> +use crate::dashboard::view::View;
>  use crate::remotes::RemotesPanel;
>  use crate::sdn::evpn::EvpnPanel;
>  use crate::sdn::ZoneTree;
>  use crate::{
> -    AccessControl, CertificatesPanel, Dashboard, RemoteListCacheEntry, ServerAdministration,
> +    AccessControl, CertificatesPanel, RemoteListCacheEntry, ServerAdministration,
>      SystemConfiguration,
>  };
>
> @@ -141,7 +142,7 @@ impl Component for PdmMainMenu {
>              tr!("Dashboard"),
>              "dashboard",
>              Some("fa fa-tachometer"),
> -            move |_| Dashboard::new().into(),
> +            move |_| View::new("dashboard").into(),
>          );
>
>          register_view(



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


  reply	other threads:[~2025-10-23 11:20 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-23  8:28 [pdm-devel] [PATCH datacenter-manager v2 00/16] prepare ui for customizable views Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 01/16] ui: dashboard: refactor guest panel creation to its own module Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 02/16] ui: dashboard: refactor creating the node panel into " Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 03/16] ui: dashboard: refactor remote panel creation " Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 04/16] ui: dashboard: remote panel: make wizard menu optional Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 05/16] ui: dashboard: refactor sdn panel creation into its own module Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 06/16] ui: dashboard: refactor task summary panel creation to " Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 07/16] ui: dashboard: task summary: disable virtual scrolling Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 08/16] ui: dashboard: refactor subscription panel creation to its own module Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 09/16] ui: dashboard: refactor top entities " Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 10/16] ui: dashboard: refactor DashboardConfig editing/constants to their module Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 11/16] ui: dashboard: factor out task parameter calculation Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 12/16] ui: dashboard: remove unused remote list Dominik Csapak
2025-10-23  8:28 ` [pdm-devel] [PATCH datacenter-manager v2 13/16] ui: dashboard: status row: make loading less jarring Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz
2025-10-23  8:28 ` [pdm-devel] [RFC PATCH datacenter-manager v2 14/16] ui: introduce `LoadResult` helper type Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz
2025-10-23  8:28 ` [pdm-devel] [RFC PATCH datacenter-manager v2 15/16] ui: dashboard: implement 'View' Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz
2025-10-23 11:44     ` Dominik Csapak
2025-10-23 11:48       ` Dominik Csapak
2025-10-24 10:17         ` Shannon Sterz
2025-10-23  8:28 ` [pdm-devel] [RFC PATCH datacenter-manager v2 16/16] ui: dashboard: use 'View' instead of the Dashboard Dominik Csapak
2025-10-23 11:19   ` Shannon Sterz [this message]
2025-10-23 11:20 ` [pdm-devel] [PATCH datacenter-manager v2 00/16] prepare ui for customizable views Shannon Sterz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DDPNTDSROMCZ.C5XNUI7XW1JL@proxmox.com \
    --to=s.sterz@proxmox.com \
    --cc=d.csapak@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal