* [pdm-devel] [PATCH datacenter-manager v3] fix #6797: ui: add dialog to view remote subscriptions
@ 2025-11-05 12:45 Shan Shaji
2025-11-13 20:45 ` [pdm-devel] applied: " Thomas Lamprecht
0 siblings, 1 reply; 3+ messages in thread
From: Shan Shaji @ 2025-11-05 12:45 UTC (permalink / raw)
To: pdm-devel
When clicking on the subscription status panel, no details appeared that
will help the user to see the subscription details of remote/nodes. For
example if a remote had a "mixed" subscription state, users couldn't see
which remote had that state.
Fixed the issue by adding a subscription dialog. Now, when the users
click the subscription status panel, a dialog opens showing the remote
subscription state and the individual subscriptions for each node.
Signed-off-by: Shan Shaji <s.shaji@proxmox.com>
---
changes since v2:
- Rebased with master as there were conflicts.
- updated the dialog show implementation based on the latest master
changes.
changes since v1: Thanks @Shannon
- Removed unused imports.
- Removed the use multiple messages for showing dialog, and instead
used a single message that accepts `Option<Dialog>`. Also added
`Option<Dialog>` as a component property. Now, this property is
updated whenever the `ShowDialog` message is called.
- Reorder the module imports in `subscptions_list.rs` file.
ui/src/dashboard/mod.rs | 3 +
ui/src/dashboard/subscription_info.rs | 51 +++++-
ui/src/dashboard/subscriptions_list.rs | 206 +++++++++++++++++++++++++
ui/src/dashboard/view.rs | 2 +-
4 files changed, 254 insertions(+), 8 deletions(-)
create mode 100644 ui/src/dashboard/subscriptions_list.rs
diff --git a/ui/src/dashboard/mod.rs b/ui/src/dashboard/mod.rs
index c56a3fa..31581c1 100644
--- a/ui/src/dashboard/mod.rs
+++ b/ui/src/dashboard/mod.rs
@@ -8,6 +8,9 @@ pub use top_entities::create_top_entities_panel;
mod subscription_info;
pub use subscription_info::create_subscription_panel;
+mod subscriptions_list;
+pub use subscriptions_list::SubscriptionsList;
+
mod remote_panel;
pub use remote_panel::create_remote_panel;
diff --git a/ui/src/dashboard/subscription_info.rs b/ui/src/dashboard/subscription_info.rs
index cf8d726..a420601 100644
--- a/ui/src/dashboard/subscription_info.rs
+++ b/ui/src/dashboard/subscription_info.rs
@@ -9,7 +9,7 @@ use yew::{
use proxmox_yew_comp::Status;
use pwt::prelude::*;
-use pwt::widget::{Column, Container, Fa, Panel, Row};
+use pwt::widget::{Column, Container, Dialog, Fa, Panel, Row};
use pwt::{
css::{AlignItems, FlexFit, JustifyContent, TextAlign},
state::SharedState,
@@ -17,7 +17,7 @@ use pwt::{
use pdm_api_types::subscription::{RemoteSubscriptionState, RemoteSubscriptions};
-use crate::LoadResult;
+use crate::{dashboard::SubscriptionsList, LoadResult};
#[derive(Properties, PartialEq)]
pub struct SubscriptionInfo {
@@ -30,7 +30,13 @@ impl SubscriptionInfo {
}
}
-struct PdmSubscriptionInfo {}
+enum Msg {
+ ShowDialog(Option<Dialog>),
+}
+
+struct PdmSubscriptionInfo {
+ dialog: Option<Dialog>,
+}
fn render_subscription_status(subs: &[RemoteSubscriptions]) -> Row {
let mut none = 0;
@@ -97,19 +103,49 @@ fn render_subscription_status(subs: &[RemoteSubscriptions]) -> Row {
}
impl Component for PdmSubscriptionInfo {
- type Message = ();
+ type Message = Msg;
type Properties = SubscriptionInfo;
fn create(_ctx: &yew::Context<Self>) -> Self {
- Self {}
+ Self { dialog: None }
+ }
+
+ fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
+ match msg {
+ Msg::ShowDialog(dialog) => {
+ self.dialog = dialog;
+ true
+ }
+ }
}
fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
let props = ctx.props();
- Column::new()
+
+ let mut column = Column::new()
.class(FlexFit)
.class(JustifyContent::Center)
- .class(AlignItems::Center)
+ .class(AlignItems::Center);
+
+ if let Some(subs) = props.subs.as_ref() {
+ 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(ctx.link().callback(|_| Msg::ShowDialog(None)));
+
+ column = column
+ .onclick(
+ ctx.link()
+ .callback(move |_| Msg::ShowDialog(Some(dialog.clone()))),
+ )
+ .style("cursor", "pointer");
+ }
+
+ column
.with_optional_child(
props.subs.is_none().then_some(
Container::new()
@@ -123,6 +159,7 @@ impl Component for PdmSubscriptionInfo {
.as_ref()
.map(|subs| render_subscription_status(subs)),
)
+ .with_optional_child(self.dialog.clone())
.into()
}
}
diff --git a/ui/src/dashboard/subscriptions_list.rs b/ui/src/dashboard/subscriptions_list.rs
new file mode 100644
index 0000000..7a13c52
--- /dev/null
+++ b/ui/src/dashboard/subscriptions_list.rs
@@ -0,0 +1,206 @@
+use std::rc::Rc;
+
+use yew::{
+ html,
+ virtual_dom::{Key, VComp, VNode},
+ Html,
+ Component, Properties
+};
+
+use pdm_api_types::subscription::{
+ RemoteSubscriptionState, RemoteSubscriptions, SubscriptionLevel,
+};
+use proxmox_yew_comp::Status;
+use pwt::{
+ css::{AlignItems, Overflow},
+ props::{ContainerBuilder, ExtractPrimaryKey, WidgetBuilder},
+ state::{KeyedSlabTree, TreeStore},
+ tr,
+ widget::{
+ data_table::{DataTable, DataTableColumn, DataTableHeader},
+ Fa, Row,
+ },
+};
+
+#[derive(Properties, PartialEq)]
+pub struct SubscriptionsList {
+ subscriptions: Vec<RemoteSubscriptions>,
+}
+
+impl SubscriptionsList {
+ pub fn new(subscriptions: Vec<RemoteSubscriptions>) -> Self {
+ yew::props!(Self { subscriptions })
+ }
+}
+
+pub struct PdmSubscriptionsList {
+ store: TreeStore<SubscriptionTreeEntry>,
+}
+
+#[derive(Clone, PartialEq)]
+struct RemoteEntry {
+ name: String,
+ state: RemoteSubscriptionState,
+ error: Option<String>,
+}
+
+#[derive(Clone, PartialEq)]
+struct NodeEntry {
+ remote: String,
+ name: String,
+ level: SubscriptionLevel,
+}
+
+#[derive(Clone, PartialEq)]
+enum SubscriptionTreeEntry {
+ Root,
+ Remote(RemoteEntry),
+ Node(NodeEntry),
+}
+
+impl SubscriptionTreeEntry {
+ fn name(&self) -> &str {
+ match self {
+ SubscriptionTreeEntry::Root => "",
+ SubscriptionTreeEntry::Remote(remote_entry) => &remote_entry.name,
+ SubscriptionTreeEntry::Node(node_entry) => &node_entry.name,
+ }
+ }
+}
+
+impl ExtractPrimaryKey for SubscriptionTreeEntry {
+ fn extract_key(&self) -> Key {
+ match self {
+ SubscriptionTreeEntry::Root => Key::from("root"),
+ SubscriptionTreeEntry::Remote(remote) => Key::from(format!("{}", remote.name)),
+ SubscriptionTreeEntry::Node(node) => {
+ Key::from(format!("{}/{}", node.remote, node.name))
+ }
+ }
+ }
+}
+
+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);
+ let mut tree = KeyedSlabTree::new();
+ let mut root = tree.set_root(SubscriptionTreeEntry::Root);
+ root.set_expanded(true);
+
+ 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 {
+ if let Some(info) = info {
+ remote_node.append(SubscriptionTreeEntry::Node(NodeEntry {
+ remote: remote.remote.clone(),
+ name: node_name.clone(),
+ level: info.level.clone(),
+ }));
+ }
+ }
+ }
+ }
+
+ store.write().update_root_tree(tree);
+ Self { store }
+ }
+
+ fn view(&self, _ctx: &yew::Context<Self>) -> Html {
+ DataTable::new(columns(self.store.clone()), self.store.clone())
+ .class(Overflow::Auto)
+ .into()
+ }
+}
+
+fn columns(
+ store: TreeStore<SubscriptionTreeEntry>,
+) -> Rc<Vec<DataTableHeader<SubscriptionTreeEntry>>> {
+ let tree_column = DataTableColumn::new(tr!("Remote"))
+ .tree_column(store)
+ .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::Node(node) => row
+ .with_child(Fa::new("server"))
+ .with_child(node.name.clone())
+ .into(),
+ SubscriptionTreeEntry::Root => row.into(),
+ }
+ })
+ .sorter(|a: &SubscriptionTreeEntry, b: &SubscriptionTreeEntry| a.name().cmp(b.name()))
+ .into();
+
+ let subscription_column = DataTableColumn::new(tr!("Subcription"))
+ .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()
+ }
+ SubscriptionTreeEntry::Remote(remote) => {
+ if let Some(error) = &remote.error {
+ html! { <span class="pwt-font-label-small">{error}</span> }.into()
+ } 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()
+ }
+ }
+ SubscriptionTreeEntry::Root => "".into(),
+ })
+ .into();
+
+ Rc::new(vec![tree_column, subscription_column])
+}
+
+fn sort_subscriptions(subs: &[RemoteSubscriptions]) -> Vec<RemoteSubscriptions> {
+ let mut subscriptions = subs.to_vec();
+ subscriptions.sort_by_key(|rs| match rs.state {
+ RemoteSubscriptionState::None => 0,
+ RemoteSubscriptionState::Unknown => 1,
+ RemoteSubscriptionState::Mixed => 2,
+ RemoteSubscriptionState::Active => 3,
+ });
+ subscriptions
+}
+
+
+impl From<SubscriptionsList> for VNode {
+ fn from(val: SubscriptionsList) -> Self {
+ let comp = VComp::new::<PdmSubscriptionsList>(Rc::new(val), None);
+ VNode::from(comp)
+ }
+}
diff --git a/ui/src/dashboard/view.rs b/ui/src/dashboard/view.rs
index c781d99..aab8d26 100644
--- a/ui/src/dashboard/view.rs
+++ b/ui/src/dashboard/view.rs
@@ -179,7 +179,7 @@ impl ViewComp {
};
let subs_future = async {
- let res = http_get("/resources/subscription", None).await;
+ let res = http_get("/resources/subscription?verbose=true", None).await;
link.send_message(Msg::LoadingResult(LoadingResult::SubscriptionInfo(res)));
};
--
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] 3+ messages in thread
* [pdm-devel] applied: [PATCH datacenter-manager v3] fix #6797: ui: add dialog to view remote subscriptions
2025-11-05 12:45 [pdm-devel] [PATCH datacenter-manager v3] fix #6797: ui: add dialog to view remote subscriptions Shan Shaji
@ 2025-11-13 20:45 ` Thomas Lamprecht
2025-11-14 13:37 ` Shan Shaji
0 siblings, 1 reply; 3+ messages in thread
From: Thomas Lamprecht @ 2025-11-13 20:45 UTC (permalink / raw)
To: pdm-devel, Shan Shaji
On Wed, 05 Nov 2025 13:45:05 +0100, Shan Shaji wrote:
> When clicking on the subscription status panel, no details appeared that
> will help the user to see the subscription details of remote/nodes. For
> example if a remote had a "mixed" subscription state, users couldn't see
> which remote had that state.
>
> Fixed the issue by adding a subscription dialog. Now, when the users
> click the subscription status panel, a dialog opens showing the remote
> subscription state and the individual subscriptions for each node.
>
> [...]
Applied, thanks!
As future improvements it might be nice to:
- show single-node remotes directly without having to expand a tree node, i.e.
similar to what Lukas did for the Remotes -> Updates view.
- Allow filtering/searching
- Show a red error like icon if there is no subscription.
In the midterm we could also add this as panel in the Remotes tab.
And it might be actually OK to have this as "real" card widget in the
dashboard, i.e. either as dedicated one, or fusing it into the existing
subscription card - @Dominik might have an opinion here too.
[1/1] fix #6797: ui: add dialog to view remote subscriptions
commit: 6140069b955fff8c5157b9d820292995c6053011
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [pdm-devel] applied: [PATCH datacenter-manager v3] fix #6797: ui: add dialog to view remote subscriptions
2025-11-13 20:45 ` [pdm-devel] applied: " Thomas Lamprecht
@ 2025-11-14 13:37 ` Shan Shaji
0 siblings, 0 replies; 3+ messages in thread
From: Shan Shaji @ 2025-11-14 13:37 UTC (permalink / raw)
To: Thomas Lamprecht, pdm-devel
On Thu Nov 13, 2025 at 9:45 PM CET, Thomas Lamprecht wrote:
> On Wed, 05 Nov 2025 13:45:05 +0100, Shan Shaji wrote:
>> When clicking on the subscription status panel, no details appeared that
>> will help the user to see the subscription details of remote/nodes. For
>> example if a remote had a "mixed" subscription state, users couldn't see
>> which remote had that state.
>>
>> Fixed the issue by adding a subscription dialog. Now, when the users
>> click the subscription status panel, a dialog opens showing the remote
>> subscription state and the individual subscriptions for each node.
>>
>> [...]
>
> Applied, thanks!
>
> As future improvements it might be nice to:
> - show single-node remotes directly without having to expand a tree node, i.e.
> similar to what Lukas did for the Remotes -> Updates view.
> - Allow filtering/searching
> - Show a red error like icon if there is no subscription.
Thanks Thomas for the feedback. Will update this accordingly. For now i
will send a quick fix for the dialog opening when clicking on the link (www.proxmox.com).
> In the midterm we could also add this as panel in the Remotes tab.
> And it might be actually OK to have this as "real" card widget in the
> dashboard, i.e. either as dedicated one, or fusing it into the existing
> subscription card - @Dominik might have an opinion here too.
Will talk with Domink about this one. Thank You!
> [1/1] fix #6797: ui: add dialog to view remote subscriptions
> commit: 6140069b955fff8c5157b9d820292995c6053011
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-11-14 13:36 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-05 12:45 [pdm-devel] [PATCH datacenter-manager v3] fix #6797: ui: add dialog to view remote subscriptions Shan Shaji
2025-11-13 20:45 ` [pdm-devel] applied: " Thomas Lamprecht
2025-11-14 13:37 ` Shan Shaji
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox