* [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in
@ 2025-11-27 14:03 Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 1/4] ui: dashboard: subscriptions: refactor subscriptions show logic Dominik Csapak
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-11-27 14:03 UTC (permalink / raw)
To: pdm-devel
add a force refresh button
improve display of subscription details grid
Dominik Csapak (4):
ui: dashboard: subscriptions: refactor subscriptions show logic
ui: dashboard: subscriptions list: update store when data changes
ui: dashboard: subscriptions list: improve display of subscription
state
ui: dashboard: subscriptions details: add a 'force refresh' button
ui/src/dashboard/subscription_info.rs | 27 ++--
ui/src/dashboard/subscriptions_list.rs | 167 +++++++++++++++++--------
ui/src/dashboard/view.rs | 51 +++++---
ui/src/load_result.rs | 6 +
4 files changed, 167 insertions(+), 84 deletions(-)
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 1/4] ui: dashboard: subscriptions: refactor subscriptions show logic
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
@ 2025-11-27 14:03 ` Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 2/4] ui: dashboard: subscriptions list: update store when data changes Dominik Csapak
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-11-27 14:03 UTC (permalink / raw)
To: pdm-devel
Instead of creating the dialog in the click callback and passing it
around, just to clone it again in the view. Simply set a boolean flag
and create the dialog in the view method if needed.
This is more in line with what we do with most dialogs and saves us a
few headaches later (since the dialog will be rendered on every `view`
call instead of just once).
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/dashboard/view.rs | 34 +++++++++++++++-------------------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/ui/src/dashboard/view.rs b/ui/src/dashboard/view.rs
index 65697d38..2adcee53 100644
--- a/ui/src/dashboard/view.rs
+++ b/ui/src/dashboard/view.rs
@@ -3,7 +3,6 @@ use std::rc::Rc;
use anyhow::Error;
use futures::join;
use js_sys::Date;
-use pwt::widget::Dialog;
use serde_json::{json, Value};
use yew::virtual_dom::{VComp, VNode};
@@ -93,7 +92,7 @@ pub enum Msg {
Reload(bool), // force
ConfigWindow(bool), // show
UpdateConfig(RefreshConfig),
- ShowSubscriptionsDialog(Option<Dialog>),
+ ShowSubscriptionsDialog(bool),
LayoutUpdate(ViewLayout),
UpdateResult(Result<(), Error>),
}
@@ -109,7 +108,7 @@ struct ViewComp {
load_finished_time: Option<f64>,
show_config_window: bool,
show_create_wizard: Option<RemoteType>,
- subscriptions_dialog: Option<Dialog>,
+ subscriptions_dialog: bool,
editing_state: SharedState<Vec<EditingMessage>>,
update_result: LoadResult<(), Error>,
@@ -151,13 +150,8 @@ fn render_widget(
WidgetType::PbsDatastores => create_pbs_datastores_panel(status),
WidgetType::Subscription => create_subscription_panel(
subscriptions.clone(),
- link.clone().callback(move |_| {
- let dialog = create_subscriptions_dialog(
- subscriptions.clone(),
- link.callback(|_| Msg::ShowSubscriptionsDialog(None)),
- );
- Msg::ShowSubscriptionsDialog(dialog)
- }),
+ link.clone()
+ .callback(move |_| Msg::ShowSubscriptionsDialog(true)),
),
WidgetType::Sdn => create_sdn_panel(status),
WidgetType::Leaderboard { leaderboard_type } => {
@@ -337,7 +331,7 @@ impl Component for ViewComp {
loading: true,
show_config_window: false,
show_create_wizard: None,
- subscriptions_dialog: None,
+ subscriptions_dialog: false,
editing_state: SharedState::new(Vec::new()),
update_result: LoadResult::new(),
@@ -480,13 +474,8 @@ impl Component for ViewComp {
.with_child(
create_subscription_panel(
subs.clone(),
- link.clone().callback(move |_| {
- let on_dialog_close =
- link.callback(|_| Msg::ShowSubscriptionsDialog(None));
- let dialog =
- create_subscriptions_dialog(subs.clone(), on_dialog_close);
- Msg::ShowSubscriptionsDialog(dialog)
- }),
+ link.clone()
+ .callback(move |_| Msg::ShowSubscriptionsDialog(true)),
)
.flex(1.0),
),
@@ -558,7 +547,14 @@ impl Component for ViewComp {
.on_submit(move |ctx| crate::remotes::create_remote(ctx, remote_type))
}));
- view.add_optional_child(self.subscriptions_dialog.clone());
+ view.add_optional_child(if self.subscriptions_dialog {
+ create_subscriptions_dialog(
+ self.render_args.subscriptions.clone(),
+ ctx.link().callback(|_| Msg::ShowSubscriptionsDialog(false)),
+ )
+ } else {
+ None
+ });
let view_context = ViewContext {
name: props.view.clone(),
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 2/4] ui: dashboard: subscriptions list: update store when data changes
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 1/4] ui: dashboard: subscriptions: refactor subscriptions show logic Dominik Csapak
@ 2025-11-27 14:03 ` Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 3/4] ui: dashboard: subscriptions list: improve display of subscription state Dominik Csapak
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-11-27 14:03 UTC (permalink / raw)
To: pdm-devel
by refactoring the tree creation and calling it from `changed` too.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/dashboard/subscriptions_list.rs | 32 ++++++++++++++++++--------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/ui/src/dashboard/subscriptions_list.rs b/ui/src/dashboard/subscriptions_list.rs
index 77e5f876..5037a9de 100644
--- a/ui/src/dashboard/subscriptions_list.rs
+++ b/ui/src/dashboard/subscriptions_list.rs
@@ -79,17 +79,13 @@ impl ExtractPrimaryKey for SubscriptionTreeEntry {
}
}
-impl Component for PdmSubscriptionsList {
- type Message = ();
- type Properties = SubscriptionsList;
-
- fn create(ctx: &yew::Context<Self>) -> Self {
- let subscriptions = sort_subscriptions(&ctx.props().subscriptions);
-
- let store = TreeStore::new().view_root(false);
+impl PdmSubscriptionsList {
+ fn update_store_data(&self, ctx: &yew::Context<Self>) {
+ let mut store = self.store.write();
let mut tree = KeyedSlabTree::new();
let mut root = tree.set_root(SubscriptionTreeEntry::Root);
root.set_expanded(true);
+ let subscriptions = sort_subscriptions(&ctx.props().subscriptions);
for remote in subscriptions {
let mut remote_node = root.append(SubscriptionTreeEntry::Remote(RemoteEntry {
@@ -114,9 +110,25 @@ impl Component for PdmSubscriptionsList {
}
}
}
+ store.update_root_tree(tree);
+ }
+}
+
+impl Component for PdmSubscriptionsList {
+ type Message = ();
+ type Properties = SubscriptionsList;
+
+ fn create(ctx: &yew::Context<Self>) -> Self {
+ let this = Self {
+ store: TreeStore::new().view_root(false),
+ };
+ this.update_store_data(ctx);
+ this
+ }
- store.write().update_root_tree(tree);
- Self { store }
+ fn changed(&mut self, ctx: &yew::Context<Self>, _old_props: &Self::Properties) -> bool {
+ self.update_store_data(ctx);
+ true
}
fn view(&self, _ctx: &yew::Context<Self>) -> Html {
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 3/4] ui: dashboard: subscriptions list: improve display of subscription state
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 1/4] ui: dashboard: subscriptions: refactor subscriptions show logic Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 2/4] ui: dashboard: subscriptions list: update store when data changes Dominik Csapak
@ 2025-11-27 14:03 ` Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button Dominik Csapak
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-11-27 14:03 UTC (permalink / raw)
To: pdm-devel
by doing the following:
* display standalone hosts inline, displaying the remote and hostname
* add an icon to the top level elements
* adding a red 'x' for clusters/nodes without a subscription
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/dashboard/subscriptions_list.rs | 135 +++++++++++++++++--------
1 file changed, 93 insertions(+), 42 deletions(-)
diff --git a/ui/src/dashboard/subscriptions_list.rs b/ui/src/dashboard/subscriptions_list.rs
index 5037a9de..b0a96eb6 100644
--- a/ui/src/dashboard/subscriptions_list.rs
+++ b/ui/src/dashboard/subscriptions_list.rs
@@ -17,7 +17,7 @@ use pwt::{
tr,
widget::{
data_table::{DataTable, DataTableColumn, DataTableHeader},
- Fa, Row,
+ Container, Fa, Row,
},
};
@@ -48,6 +48,7 @@ struct NodeEntry {
remote: String,
name: String,
level: SubscriptionLevel,
+ standalone: bool,
}
#[derive(Clone, PartialEq)]
@@ -88,25 +89,42 @@ impl PdmSubscriptionsList {
let subscriptions = sort_subscriptions(&ctx.props().subscriptions);
for remote in subscriptions {
- let mut remote_node = root.append(SubscriptionTreeEntry::Remote(RemoteEntry {
- name: remote.remote.clone(),
- state: remote.state.clone(),
- error: remote.error.clone(),
- }));
-
- if let Some(node_status) = remote.node_status.as_ref() {
- if node_status.is_empty() {
- continue;
- }
-
- for (node_name, info) in node_status {
+ match remote.node_status {
+ Some(node_status) if node_status.len() == 1 => {
+ let (node_name, info) = node_status.into_iter().next().unwrap();
if let Some(info) = info {
- remote_node.append(SubscriptionTreeEntry::Node(NodeEntry {
+ root.append(SubscriptionTreeEntry::Node(NodeEntry {
remote: remote.remote.clone(),
name: node_name.clone(),
- level: info.level.clone(),
+ level: info.level,
+ standalone: true,
}));
}
+ continue;
+ }
+ _ => {
+ let mut remote_node = root.append(SubscriptionTreeEntry::Remote(RemoteEntry {
+ name: remote.remote.clone(),
+ state: remote.state.clone(),
+ error: remote.error.clone(),
+ }));
+
+ if let Some(node_status) = remote.node_status.as_ref() {
+ if node_status.is_empty() {
+ continue;
+ }
+
+ for (node_name, info) in node_status {
+ if let Some(info) = info {
+ remote_node.append(SubscriptionTreeEntry::Node(NodeEntry {
+ remote: remote.remote.clone(),
+ name: node_name.clone(),
+ level: info.level,
+ standalone: false,
+ }));
+ }
+ }
+ }
}
}
}
@@ -146,10 +164,17 @@ fn columns(
.render(|entry: &SubscriptionTreeEntry| {
let row = Row::new().class(AlignItems::Center).gap(2);
match entry {
- SubscriptionTreeEntry::Remote(remote) => row.with_child(remote.name.clone()).into(),
+ SubscriptionTreeEntry::Remote(remote) => row
+ .with_child(Fa::new("server").fixed_width())
+ .with_child(remote.name.clone())
+ .into(),
SubscriptionTreeEntry::Node(node) => row
- .with_child(Fa::new("server"))
- .with_child(node.name.clone())
+ .with_child(Fa::new("building").fixed_width())
+ .with_child(if node.standalone {
+ format!("{} - {}", node.remote, node.name)
+ } else {
+ node.name.clone()
+ })
.into(),
SubscriptionTreeEntry::Root => row.into(),
}
@@ -157,37 +182,63 @@ fn columns(
.sorter(|a: &SubscriptionTreeEntry, b: &SubscriptionTreeEntry| a.name().cmp(b.name()))
.into();
+ fn render_subscription_state(state: &RemoteSubscriptionState) -> Row {
+ let icon = match state {
+ RemoteSubscriptionState::Mixed => Fa::from(Status::Warning),
+ RemoteSubscriptionState::Active => Fa::from(Status::Success),
+ RemoteSubscriptionState::None => Fa::from(Status::Error),
+ _ => Fa::from(Status::Unknown),
+ };
+
+ let text = match state {
+ RemoteSubscriptionState::None => "None",
+ RemoteSubscriptionState::Unknown => "Unknown",
+ RemoteSubscriptionState::Mixed => "Mixed",
+ RemoteSubscriptionState::Active => "Active",
+ };
+
+ Row::new()
+ .class(AlignItems::Center)
+ .gap(2)
+ .with_child(icon)
+ .with_child(Container::from_tag("span").with_child(text))
+ }
+
+ fn render_subscription_level(level: SubscriptionLevel) -> &'static str {
+ match level {
+ SubscriptionLevel::None => "None",
+ SubscriptionLevel::Basic => "Basic",
+ SubscriptionLevel::Community => "Community",
+ SubscriptionLevel::Premium => "Premium",
+ SubscriptionLevel::Standard => "Standard",
+ SubscriptionLevel::Unknown => "Unknown",
+ }
+ }
+
let subscription_column = DataTableColumn::new(tr!("Subscription"))
.render(|entry: &SubscriptionTreeEntry| match entry {
SubscriptionTreeEntry::Node(node) => {
- let text = match node.level {
- SubscriptionLevel::None => "None",
- SubscriptionLevel::Basic => "Basic",
- SubscriptionLevel::Community => "Community",
- SubscriptionLevel::Premium => "Premium",
- SubscriptionLevel::Standard => "Standard",
- SubscriptionLevel::Unknown => "Unknown",
- };
- text.into()
+ if node.standalone {
+ let (sub_state, text) = match node.level {
+ SubscriptionLevel::None => (RemoteSubscriptionState::None, None),
+ SubscriptionLevel::Unknown => (RemoteSubscriptionState::Unknown, None),
+ other => (
+ RemoteSubscriptionState::Active,
+ Some(render_subscription_level(other)),
+ ),
+ };
+ render_subscription_state(&sub_state)
+ .with_optional_child(text)
+ .into()
+ } else {
+ render_subscription_level(node.level).into()
+ }
}
SubscriptionTreeEntry::Remote(remote) => {
if let Some(error) = &remote.error {
- html! { <span class="pwt-font-label-small">{error}</span> }.into()
+ html! { <span class="pwt-font-label-small">{error}</span> }
} else {
- let icon = match remote.state {
- RemoteSubscriptionState::Mixed => Fa::from(Status::Warning),
- RemoteSubscriptionState::Active => Fa::from(Status::Success),
- _ => Fa::from(Status::Unknown),
- };
-
- let text = match remote.state {
- RemoteSubscriptionState::None => "None",
- RemoteSubscriptionState::Unknown => "Unknown",
- RemoteSubscriptionState::Mixed => "Mixed",
- RemoteSubscriptionState::Active => "Active",
- };
-
- Row::new().gap(2).with_child(icon).with_child(text).into()
+ render_subscription_state(&remote.state).into()
}
}
SubscriptionTreeEntry::Root => "".into(),
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
` (2 preceding siblings ...)
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 3/4] ui: dashboard: subscriptions list: improve display of subscription state Dominik Csapak
@ 2025-11-27 14:03 ` Dominik Csapak
2025-11-27 16:13 ` Michael Köppl
2025-11-27 16:13 ` [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Michael Köppl
2025-11-27 21:12 ` [pdm-devel] applied: " Thomas Lamprecht
5 siblings, 1 reply; 9+ messages in thread
From: Dominik Csapak @ 2025-11-27 14:03 UTC (permalink / raw)
To: pdm-devel
This simply calls the subscription api with 'max-age: 0', so we force
the backend to fetch all subscriptions.
This is useful when one adds a subscription to the remotes and does not
want to wait for the automatic refresh.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/dashboard/subscription_info.rs | 27 +++++++++++-----------
ui/src/dashboard/view.rs | 33 ++++++++++++++++++++-------
ui/src/load_result.rs | 6 +++++
3 files changed, 45 insertions(+), 21 deletions(-)
diff --git a/ui/src/dashboard/subscription_info.rs b/ui/src/dashboard/subscription_info.rs
index fed2b2c7..d6d13d8c 100644
--- a/ui/src/dashboard/subscription_info.rs
+++ b/ui/src/dashboard/subscription_info.rs
@@ -136,19 +136,20 @@ impl From<SubscriptionInfo> for VNode {
pub fn create_subscriptions_dialog(
subs: SharedState<LoadResult<Vec<RemoteSubscriptions>, Error>>,
on_dialog_close: Callback<()>,
-) -> Option<Dialog> {
- if let Some(subs) = subs.read().data.clone() {
- let dialog = Dialog::new(tr!("Your Subscriptions"))
- .resizable(true)
- .width(500)
- .height(400)
- .min_width(200)
- .min_height(50)
- .with_child(SubscriptionsList::new(subs.clone()))
- .on_close(on_dialog_close);
- return Some(dialog);
- }
- None
+ on_refresh: Callback<MouseEvent>,
+) -> Dialog {
+ let loading = !subs.read().has_data();
+ let subs = subs.read().data.clone();
+ let subs = subs.unwrap_or_default();
+ Dialog::new(tr!("Your Subscriptions"))
+ .with_tool(Button::refresh(loading).on_activate(on_refresh))
+ .resizable(true)
+ .width(500)
+ .height(400)
+ .min_width(200)
+ .min_height(50)
+ .with_child(SubscriptionsList::new(subs))
+ .on_close(on_dialog_close)
}
pub fn create_subscription_panel(
diff --git a/ui/src/dashboard/view.rs b/ui/src/dashboard/view.rs
index 2adcee53..2791277b 100644
--- a/ui/src/dashboard/view.rs
+++ b/ui/src/dashboard/view.rs
@@ -95,6 +95,7 @@ pub enum Msg {
ShowSubscriptionsDialog(bool),
LayoutUpdate(ViewLayout),
UpdateResult(Result<(), Error>),
+ ForceSubscriptionUpdate,
}
struct ViewComp {
@@ -425,6 +426,22 @@ impl Component for ViewComp {
Msg::UpdateResult(res) => {
self.update_result.update(res);
}
+ Msg::ForceSubscriptionUpdate => {
+ let link = ctx.link().clone();
+ let view = ctx.props().view.clone();
+ self.render_args.subscriptions.write().clear();
+ self.async_pool.spawn(async move {
+ let mut params = json!({
+ "verbose": true,
+ "max-age": 0,
+ });
+ if let Some(view) = view {
+ params["view"] = view.to_string().into();
+ }
+ let res = http_get("/resources/subscription", Some(params)).await;
+ link.send_message(Msg::LoadingResult(LoadingResult::SubscriptionInfo(res)));
+ });
+ }
}
true
}
@@ -547,14 +564,14 @@ impl Component for ViewComp {
.on_submit(move |ctx| crate::remotes::create_remote(ctx, remote_type))
}));
- view.add_optional_child(if self.subscriptions_dialog {
- create_subscriptions_dialog(
- self.render_args.subscriptions.clone(),
- ctx.link().callback(|_| Msg::ShowSubscriptionsDialog(false)),
- )
- } else {
- None
- });
+ view.add_optional_child(
+ self.subscriptions_dialog
+ .then_some(create_subscriptions_dialog(
+ self.render_args.subscriptions.clone(),
+ ctx.link().callback(|_| Msg::ShowSubscriptionsDialog(false)),
+ ctx.link().callback(|_| Msg::ForceSubscriptionUpdate),
+ )),
+ );
let view_context = ViewContext {
name: props.view.clone(),
diff --git a/ui/src/load_result.rs b/ui/src/load_result.rs
index 4f3e6d5a..55436fd1 100644
--- a/ui/src/load_result.rs
+++ b/ui/src/load_result.rs
@@ -33,6 +33,12 @@ impl<T, E> LoadResult<T, E> {
pub fn has_data(&self) -> bool {
self.data.is_some() || self.error.is_some()
}
+
+ /// Clears both data and the error from the result.
+ pub fn clear(&mut self) {
+ self.data = None;
+ self.error = None;
+ }
}
impl<T, E> Default for LoadResult<T, E> {
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button Dominik Csapak
@ 2025-11-27 16:13 ` Michael Köppl
2025-11-27 21:10 ` Thomas Lamprecht
0 siblings, 1 reply; 9+ messages in thread
From: Michael Köppl @ 2025-11-27 16:13 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion; +Cc: pdm-devel
1 nit inline
On Thu Nov 27, 2025 at 3:03 PM CET, Dominik Csapak wrote:
> pub fn create_subscription_panel(
> diff --git a/ui/src/dashboard/view.rs b/ui/src/dashboard/view.rs
> index 2adcee53..2791277b 100644
> --- a/ui/src/dashboard/view.rs
> +++ b/ui/src/dashboard/view.rs
> @@ -95,6 +95,7 @@ pub enum Msg {
> ShowSubscriptionsDialog(bool),
> LayoutUpdate(ViewLayout),
> UpdateResult(Result<(), Error>),
> + ForceSubscriptionUpdate,
> }
>
> struct ViewComp {
> @@ -425,6 +426,22 @@ impl Component for ViewComp {
> Msg::UpdateResult(res) => {
> self.update_result.update(res);
> }
> + Msg::ForceSubscriptionUpdate => {
> + let link = ctx.link().clone();
> + let view = ctx.props().view.clone();
> + self.render_args.subscriptions.write().clear();
nit: Clearing here means that if fetching the subscriptions takes longer
for some reason, the user will look at an empty box until the data could
be fetched. Could it make sense to move clearing this to after the
request is done by perhaps tracking the loading state in some way?
> + self.async_pool.spawn(async move {
> + let mut params = json!({
> + "verbose": true,
> + "max-age": 0,
> + });
> + if let Some(view) = view {
> + params["view"] = view.to_string().into();
> + }
> + let res = http_get("/resources/subscription", Some(params)).await;
here
> + link.send_message(Msg::LoadingResult(LoadingResult::SubscriptionInfo(res)));
> + });
> + }
> }
> true
> }
> @@ -547,14 +564,14 @@ impl Component for ViewComp {
> .on_submit(move |ctx| crate::remotes::create_remote(ctx, remote_type))
> }));
>
> - view.add_optional_child(if self.subscriptions_dialog {
> - create_subscriptions_dialog(
> - self.render_args.subscriptions.clone(),
> - ctx.link().callback(|_| Msg::ShowSubscriptionsDialog(false)),
> - )
> - } else {
> - None
> - });
> + view.add_optional_child(
> + self.subscriptions_dialog
> + .then_some(create_subscriptions_dialog(
> + self.render_args.subscriptions.clone(),
> + ctx.link().callback(|_| Msg::ShowSubscriptionsDialog(false)),
> + ctx.link().callback(|_| Msg::ForceSubscriptionUpdate),
> + )),
> + );
>
> let view_context = ViewContext {
> name: props.view.clone(),
> diff --git a/ui/src/load_result.rs b/ui/src/load_result.rs
> index 4f3e6d5a..55436fd1 100644
> --- a/ui/src/load_result.rs
> +++ b/ui/src/load_result.rs
> @@ -33,6 +33,12 @@ impl<T, E> LoadResult<T, E> {
> pub fn has_data(&self) -> bool {
> self.data.is_some() || self.error.is_some()
> }
> +
> + /// Clears both data and the error from the result.
> + pub fn clear(&mut self) {
> + self.data = None;
> + self.error = None;
> + }
> }
>
> impl<T, E> Default for LoadResult<T, E> {
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
` (3 preceding siblings ...)
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button Dominik Csapak
@ 2025-11-27 16:13 ` Michael Köppl
2025-11-27 21:12 ` [pdm-devel] applied: " Thomas Lamprecht
5 siblings, 0 replies; 9+ messages in thread
From: Michael Köppl @ 2025-11-27 16:13 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion; +Cc: pdm-devel
Quickly tested this by:
- adding a standalone host without a subscription
- adding a cluster without subscriptions
- adding a PBS instance
- mocking nodes with subscription to see if they are displayed correctly
- stopping nodes to see if connection errors are handled properly and
errors are shown accordingly
Noticed that:
- If a PVE node goes offline, it is still displayed but will show an
error message in the subscription tree. However, if a PBS instance
goes offline, it simply disappears entirely from the tree.
Also had a look at the code and noted 1 nit on the individual patch.
Other than the issue noted above, the changes look good to me.
With the above issue addressed, consider this:
Reviewed-by: Michael Köppl <m.koeppl@proxmox.com>
Tested-by: Michael Köppl <m.koeppl@proxmox.com>
On Thu Nov 27, 2025 at 3:03 PM CET, Dominik Csapak wrote:
> add a force refresh button
> improve display of subscription details grid
>
> Dominik Csapak (4):
> ui: dashboard: subscriptions: refactor subscriptions show logic
> ui: dashboard: subscriptions list: update store when data changes
> ui: dashboard: subscriptions list: improve display of subscription
> state
> ui: dashboard: subscriptions details: add a 'force refresh' button
>
> ui/src/dashboard/subscription_info.rs | 27 ++--
> ui/src/dashboard/subscriptions_list.rs | 167 +++++++++++++++++--------
> ui/src/dashboard/view.rs | 51 +++++---
> ui/src/load_result.rs | 6 +
> 4 files changed, 167 insertions(+), 84 deletions(-)
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button
2025-11-27 16:13 ` Michael Köppl
@ 2025-11-27 21:10 ` Thomas Lamprecht
0 siblings, 0 replies; 9+ messages in thread
From: Thomas Lamprecht @ 2025-11-27 21:10 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Michael Köppl
Cc: pdm-devel
Am 27.11.25 um 17:13 schrieb Michael Köppl:
> 1 nit inline
>
> On Thu Nov 27, 2025 at 3:03 PM CET, Dominik Csapak wrote:
>> pub fn create_subscription_panel(
>> diff --git a/ui/src/dashboard/view.rs b/ui/src/dashboard/view.rs
>> index 2adcee53..2791277b 100644
>> --- a/ui/src/dashboard/view.rs
>> +++ b/ui/src/dashboard/view.rs
>> @@ -95,6 +95,7 @@ pub enum Msg {
>> ShowSubscriptionsDialog(bool),
>> LayoutUpdate(ViewLayout),
>> UpdateResult(Result<(), Error>),
>> + ForceSubscriptionUpdate,
>> }
>>
>> struct ViewComp {
>> @@ -425,6 +426,22 @@ impl Component for ViewComp {
>> Msg::UpdateResult(res) => {
>> self.update_result.update(res);
>> }
>> + Msg::ForceSubscriptionUpdate => {
>> + let link = ctx.link().clone();
>> + let view = ctx.props().view.clone();
>> + self.render_args.subscriptions.write().clear();
>
> nit: Clearing here means that if fetching the subscriptions takes longer
> for some reason, the user will look at an empty box until the data could
> be fetched. Could it make sense to move clearing this to after the
> request is done by perhaps tracking the loading state in some way?
Agree in general, but...
>
>> + self.async_pool.spawn(async move {
>> + let mut params = json!({
>> + "verbose": true,
>> + "max-age": 0,
>> + });
>> + if let Some(view) = view {
>> + params["view"] = view.to_string().into();
>> + }
>> + let res = http_get("/resources/subscription", Some(params)).await;
>
> here
... the create_subscriptions_dialog uses the existence of data as heuristic to
determine if it should show that the component is loading:
let loading = !subs.read().has_data();
So this needs adaption to still look OK, shouldn't be that hard, but OTOH it's
also not pressing for now IMO.
>
>> + link.send_message(Msg::LoadingResult(LoadingResult::SubscriptionInfo(res)));
>> + });
>> + }
>> }
>> true
>> }
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pdm-devel] applied: [PATCH datacenter-manager 0/4] improve subscription display in
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
` (4 preceding siblings ...)
2025-11-27 16:13 ` [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Michael Köppl
@ 2025-11-27 21:12 ` Thomas Lamprecht
5 siblings, 0 replies; 9+ messages in thread
From: Thomas Lamprecht @ 2025-11-27 21:12 UTC (permalink / raw)
To: pdm-devel, Dominik Csapak
On Thu, 27 Nov 2025 15:03:44 +0100, Dominik Csapak wrote:
> add a force refresh button
> improve display of subscription details grid
>
> Dominik Csapak (4):
> ui: dashboard: subscriptions: refactor subscriptions show logic
> ui: dashboard: subscriptions list: update store when data changes
> ui: dashboard: subscriptions list: improve display of subscription
> state
> ui: dashboard: subscriptions details: add a 'force refresh' button
>
> [...]
Applied, thanks!
[1/4] ui: dashboard: subscriptions: refactor subscriptions show logic
commit: 196c5e1fce3034855cce48909a3ec4246eda62d4
[2/4] ui: dashboard: subscriptions list: update store when data changes
commit: c2fd607675a2c29f9500d139023ea1190b197790
[3/4] ui: dashboard: subscriptions list: improve display of subscription state
commit: e1106a88fcea722d315f993a8ae21d08a7f122e9
[4/4] ui: dashboard: subscriptions details: add a 'force refresh' button
commit: fe0448170334208ce7c1a1c83f15d6da66ed90f2
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-11-27 21:12 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-27 14:03 [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 1/4] ui: dashboard: subscriptions: refactor subscriptions show logic Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 2/4] ui: dashboard: subscriptions list: update store when data changes Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 3/4] ui: dashboard: subscriptions list: improve display of subscription state Dominik Csapak
2025-11-27 14:03 ` [pdm-devel] [PATCH datacenter-manager 4/4] ui: dashboard: subscriptions details: add a 'force refresh' button Dominik Csapak
2025-11-27 16:13 ` Michael Köppl
2025-11-27 21:10 ` Thomas Lamprecht
2025-11-27 16:13 ` [pdm-devel] [PATCH datacenter-manager 0/4] improve subscription display in Michael Köppl
2025-11-27 21:12 ` [pdm-devel] applied: " Thomas Lamprecht
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox