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 2A38E1FF16E for ; Mon, 20 Jan 2025 10:30:48 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 59FDB16325; Mon, 20 Jan 2025 10:30:47 +0100 (CET) From: Dominik Csapak To: pdm-devel@lists.proxmox.com Date: Mon, 20 Jan 2025 10:30:04 +0100 Message-Id: <20250120093006.927014-15-d.csapak@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250120093006.927014-1-d.csapak@proxmox.com> References: <20250120093006.927014-1-d.csapak@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -1.232 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 SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_DBL_SPAM 2.5 Contains a spam URL listed in the Spamhaus DBL blocklist [tasks.rs] Subject: [pdm-devel] [PATCH datacenter-manager 7/9] ui: remotes: add tasks to global remote panel 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" this replaces the previous "global" remotes panel (that was just the configuration) with a TabPanel that contains the configuration and the tasks for all remotes. Signed-off-by: Dominik Csapak --- ui/src/main_menu.rs | 7 +- ui/src/remotes/mod.rs | 35 +++++++++ ui/src/remotes/tasks.rs | 153 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 ui/src/remotes/tasks.rs diff --git a/ui/src/main_menu.rs b/ui/src/main_menu.rs index 364ba94..4f40d2c 100644 --- a/ui/src/main_menu.rs +++ b/ui/src/main_menu.rs @@ -16,9 +16,10 @@ use proxmox_yew_comp::{NotesView, XTermJs}; use pdm_api_types::remotes::RemoteType; +use crate::remotes::RemotesPanel; use crate::{ - AccessControl, CertificatesPanel, Dashboard, RemoteConfigPanel, RemoteList, - ServerAdministration, SystemConfiguration, + AccessControl, CertificatesPanel, Dashboard, RemoteList, ServerAdministration, + SystemConfiguration, }; /* @@ -279,7 +280,7 @@ impl Component for PdmMainMenu { Container::new() .class("pwt-content-spacer") .class(pwt::css::FlexFit) - .with_child(RemoteConfigPanel::new()) + .with_child(html! {}) .into() }, remote_submenu, diff --git a/ui/src/remotes/mod.rs b/ui/src/remotes/mod.rs index f221777..a2f471c 100644 --- a/ui/src/remotes/mod.rs +++ b/ui/src/remotes/mod.rs @@ -20,3 +20,38 @@ mod edit_remote; mod config; pub use config::{create_remote, RemoteConfigPanel}; + +mod tasks; + +use yew::{function_component, Html}; + +use pwt::{ + props::{ContainerBuilder, StorageLocation}, + state::NavigationContainer, + widget::{MiniScrollMode, TabBarItem, TabPanel}, +}; + +#[function_component(RemotesPanel)] +pub fn system_configuration() -> Html { + let panel = TabPanel::new() + .state_id(StorageLocation::session("RemotesPanelState")) + .class(pwt::css::FlexFit) + .router(true) + .scroll_mode(MiniScrollMode::Arrow) + .with_item_builder( + TabBarItem::new() + .key("configuration") + .label("Configuration") + .icon_class("fa fa-cogs"), + |_| RemoteConfigPanel::new().into(), + ) + .with_item_builder( + TabBarItem::new() + .key("tasks") + .label("Tasks") + .icon_class("fa fa-book"), + |_| tasks::RemoteTaskList::new().into(), + ); + + NavigationContainer::new().with_child(panel).into() +} diff --git a/ui/src/remotes/tasks.rs b/ui/src/remotes/tasks.rs new file mode 100644 index 0000000..6474035 --- /dev/null +++ b/ui/src/remotes/tasks.rs @@ -0,0 +1,153 @@ +use std::rc::Rc; + +use yew::{ + html, + virtual_dom::{VComp, VNode}, + Component, Properties, +}; + +use pdm_api_types::RemoteUpid; +use pdm_client::types::PveUpid; + +use proxmox_yew_comp::{ + common_api_types::TaskListItem, + utils::{format_task_description, format_upid, render_epoch_short}, + TaskViewer, Tasks, +}; +use pwt::{ + css::FlexFit, + props::{ContainerBuilder, WidgetBuilder}, + tr, + widget::{ + data_table::{DataTableColumn, DataTableHeader}, + Column, + }, +}; + +#[derive(PartialEq, Properties)] +pub struct RemoteTaskList; +impl RemoteTaskList { + pub fn new() -> Self { + yew::props!(Self {}) + } +} + +pub struct PbsRemoteTaskList { + columns: Rc>>, + upid: Option<(String, Option)>, +} + +fn columns() -> Rc>> { + Rc::new(vec![ + DataTableColumn::new(tr!("Start Time")) + .width("130px") + .render(|item: &TaskListItem| render_epoch_short(item.starttime).into()) + .into(), + DataTableColumn::new(tr!("End Time")) + .width("130px") + .render(|item: &TaskListItem| match item.endtime { + Some(endtime) => render_epoch_short(endtime).into(), + None => html! {}, + }) + .into(), + DataTableColumn::new(tr!("User name")) + .width("150px") + .render(|item: &TaskListItem| { + html! {&item.user} + }) + .into(), + DataTableColumn::new(tr!("Remote")) + .width("150px") + .render( + |item: &TaskListItem| match item.upid.parse::() { + Ok(remote) => html! {remote.remote()}, + Err(_) => html! {{"-"}}, + }, + ) + .into(), + DataTableColumn::new(tr!("Node")) + .width("150px") + .render(|item: &TaskListItem| { + html! {&item.node} + }) + .into(), + DataTableColumn::new(tr!("Description")) + .flex(1) + .render(move |item: &TaskListItem| { + if let Ok(remote_upid) = item.upid.parse::() { + match remote_upid.upid.parse::() { + Ok(upid) => { + format_task_description(&upid.worker_type, upid.worker_id.as_deref()) + } + Err(_) => format_upid(&remote_upid.upid), + } + } else { + format_upid(&item.upid) + } + .into() + }) + .into(), + DataTableColumn::new(tr!("Status")) + .width("200px") + .render(|item: &TaskListItem| { + let text = item.status.as_deref().unwrap_or(""); + html! {text} + }) + .into(), + ]) +} + +impl Component for PbsRemoteTaskList { + type Message = Option<(String, Option)>; + type Properties = RemoteTaskList; + + fn create(_ctx: &yew::Context) -> Self { + Self { + columns: columns(), + upid: None, + } + } + + fn update(&mut self, _ctx: &yew::Context, msg: Self::Message) -> bool { + self.upid = msg; + true + } + + fn view(&self, ctx: &yew::Context) -> yew::Html { + let task = self + .upid + .as_ref() + .and_then(|(upid, endtime)| upid.parse::().ok().map(|upid| (upid, endtime))) + .map(|(remote_upid, endtime)| { + // TODO PBS + let base_url = format!("/pve/remotes/{}/tasks", remote_upid.remote()); + TaskViewer::new(remote_upid.to_string()) + .endtime(endtime) + .base_url(base_url) + .on_close({ + let link = ctx.link().clone(); + move |_| link.send_message(None) + }) + }); + Column::new() + .class(FlexFit) + .with_child( + Tasks::new() + .base_url("/remote-tasks/list") + .on_show_task({ + let link = ctx.link().clone(); + move |(upid_str, endtime)| link.send_message(Some((upid_str, endtime))) + }) + .columns(self.columns.clone()), + ) + .with_optional_child(task) + .into() + } +} + +impl From for VNode { + fn from(val: RemoteTaskList) -> Self { + let comp = VComp::new::(Rc::new(val), None); + VNode::from(comp) + } +} -- 2.39.5 _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel