[pdm-devel] [PATCH datacenter-manager 6/9] ui: refactor RemoteConfigPanel into own module

Dominik Csapak d.csapak at proxmox.com
Mon Jan 20 10:30:03 CET 2025


we'll use the 'remote' module for a more general component where we use
the RemoteConfigPanel

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 ui/src/remotes/config.rs | 392 +++++++++++++++++++++++++++++++++++++++
 ui/src/remotes/mod.rs    | 392 +--------------------------------------
 2 files changed, 394 insertions(+), 390 deletions(-)
 create mode 100644 ui/src/remotes/config.rs

diff --git a/ui/src/remotes/config.rs b/ui/src/remotes/config.rs
new file mode 100644
index 0000000..d9d16ae
--- /dev/null
+++ b/ui/src/remotes/config.rs
@@ -0,0 +1,392 @@
+use std::future::Future;
+use std::pin::Pin;
+use std::rc::Rc;
+
+use anyhow::Error;
+
+use proxmox_schema::property_string::PropertyString;
+
+use crate::remotes::edit_remote::EditRemote;
+//use pwt::widget::form::{Field, FormContext, InputType};
+
+use pdm_api_types::remotes::Remote;
+//use proxmox_schema::{property_string::PropertyString, ApiType};
+use proxmox_yew_comp::percent_encoding::percent_encode_component;
+
+//use pbs_api_types::CERT_FINGERPRINT_SHA256_SCHEMA;
+
+//use proxmox_schema::api_types::{CERT_FINGERPRINT_SHA256_SCHEMA, DNS_NAME_OR_IP_SCHEMA};
+
+use serde_json::Value;
+use yew::virtual_dom::{Key, VComp, VNode};
+
+use pwt::prelude::*;
+use pwt::state::{Selection, Store};
+use pwt::widget::data_table::{DataTable, DataTableColumn, DataTableHeader};
+//use pwt::widget::form::{delete_empty_values, Field, FormContext, InputType};
+use pwt::widget::{
+    //menu::{Menu, MenuButton, MenuItem},
+    Button,
+    Column,
+    Toolbar,
+    Tooltip,
+};
+//use pwt::widget::InputPanel;
+
+//use proxmox_yew_comp::EditWindow;
+use proxmox_yew_comp::{
+    ConfirmButton, LoadableComponent, LoadableComponentContext, LoadableComponentMaster,
+};
+
+use pdm_api_types::remotes::{NodeUrl, RemoteType};
+
+async fn load_remotes() -> Result<Vec<Remote>, Error> {
+    proxmox_yew_comp::http_get("/remotes", None).await
+}
+
+async fn delete_item(key: Key) -> Result<(), Error> {
+    let id = key.to_string();
+    let url = format!("/remotes/{}", percent_encode_component(&id));
+    proxmox_yew_comp::http_delete(&url, None).await?;
+    Ok(())
+}
+
+pub async fn create_remote(mut data: Value, remote_type: RemoteType) -> Result<(), Error> {
+    if data.get("nodes").is_none() {
+        let nodes = vec![PropertyString::new(NodeUrl {
+            hostname: data["hostname"].as_str().unwrap_or_default().to_string(),
+            fingerprint: data["fingerprint"].as_str().map(|fp| fp.to_string()),
+        })];
+        data["nodes"] = serde_json::to_value(nodes)?;
+    }
+    data["type"] = match remote_type {
+        RemoteType::Pve => "pve",
+        RemoteType::Pbs => "pbs",
+    }
+    .into();
+
+    let remote: Remote = serde_json::from_value(data.clone())?;
+
+    let mut params = serde_json::to_value(remote)?;
+    if let Some(token) = data["create-token"].as_str() {
+        params["create-token"] = token.into();
+    }
+
+    proxmox_yew_comp::http_post("/remotes", Some(params)).await
+}
+
+/*
+async fn update_item(form_ctx: FormContext) -> Result<(), Error> {
+    let data = form_ctx.get_submit_data();
+
+    let data = delete_empty_values(&data, &["fingerprint", "comment", "port"], true);
+
+    let name = form_ctx.read().get_field_text("name");
+
+    let url = format!("/config/remote/{}", percent_encode_component(&name));
+
+    proxmox_yew_comp::http_put(&url, Some(data)).await
+}
+*/
+
+#[derive(PartialEq, Properties)]
+pub struct RemoteConfigPanel;
+
+impl RemoteConfigPanel {
+    pub fn new() -> Self {
+        yew::props!(Self {})
+    }
+}
+
+#[derive(PartialEq)]
+pub enum ViewState {
+    Add(RemoteType),
+    Edit,
+}
+
+pub enum Msg {
+    SelectionChange,
+    RemoveItem,
+}
+
+pub struct PbsRemoteConfigPanel {
+    store: Store<Remote>,
+    selection: Selection,
+    remote_list_columns: Rc<Vec<DataTableHeader<Remote>>>,
+}
+
+impl LoadableComponent for PbsRemoteConfigPanel {
+    type Message = Msg;
+    type Properties = RemoteConfigPanel;
+    type ViewState = ViewState;
+
+    fn load(
+        &self,
+        _ctx: &LoadableComponentContext<Self>,
+    ) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
+        let store = self.store.clone();
+        Box::pin(async move {
+            let data = load_remotes().await?;
+            store.write().set_data(data);
+            Ok(())
+        })
+    }
+
+    fn create(ctx: &LoadableComponentContext<Self>) -> Self {
+        let store = Store::with_extract_key(|record: &Remote| Key::from(record.id.clone()));
+
+        let selection = Selection::new().on_select(ctx.link().callback(|_| Msg::SelectionChange));
+
+        let remote_list_columns = remote_list_columns();
+
+        Self {
+            store,
+            selection,
+            remote_list_columns,
+        }
+    }
+
+    fn update(&mut self, ctx: &LoadableComponentContext<Self>, msg: Self::Message) -> bool {
+        match msg {
+            Msg::SelectionChange => true,
+            Msg::RemoveItem => {
+                if let Some(key) = self.selection.selected_key() {
+                    let link = ctx.link();
+                    link.clone().spawn(async move {
+                        if let Err(err) = delete_item(key).await {
+                            link.show_error(tr!("Unable to delete item"), err, true);
+                        }
+                        link.send_reload();
+                    })
+                }
+                false
+            }
+        }
+    }
+
+    fn toolbar(&self, ctx: &LoadableComponentContext<Self>) -> Option<Html> {
+        let link = ctx.link();
+
+        let disabled = self.selection.is_empty();
+
+        let toolbar = Toolbar::new()
+            .class("pwt-overflow-hidden")
+            .class("pwt-border-bottom")
+            .with_child({
+                Button::new(tr!("Add Proxmox VE"))
+                    .icon_class("fa fa-building")
+                    .onclick(link.change_view_callback(|_| Some(ViewState::Add(RemoteType::Pve))))
+                // FIXME: add PBS support
+                //MenuButton::new(tr!("Add")).show_arrow(true).menu(
+                //    Menu::new()
+                //        .with_item(
+                //            MenuItem::new("Proxmox VE")
+                //                .icon_class("fa fa-building")
+                //                .on_select(link.change_view_callback(|_| {
+                //                    Some(ViewState::Add(RemoteType::Pve))
+                //                })),
+                //        )
+                //        .with_item(
+                //            MenuItem::new("Proxmox Backup Server")
+                //                .icon_class("fa fa-floppy-o")
+                //                .on_select(link.change_view_callback(|_| {
+                //                    Some(ViewState::Add(RemoteType::Pbs))
+                //                })),
+                //        ),
+                //)
+            })
+            .with_spacer()
+            .with_child(
+                Button::new(tr!("Edit"))
+                    .disabled(disabled)
+                    .onclick(link.change_view_callback(|_| Some(ViewState::Edit))),
+            )
+            .with_child(
+                ConfirmButton::new(tr!("Remove"))
+                    .confirm_message(tr!("Are you sure you want to remove this remote?"))
+                    .disabled(disabled)
+                    .on_activate(link.callback(|_| Msg::RemoveItem)),
+            )
+            .with_flex_spacer()
+            .with_child({
+                let loading = ctx.loading();
+                let link = ctx.link();
+                Button::refresh(loading).onclick(move |_| link.send_reload())
+            });
+
+        Some(toolbar.into())
+    }
+
+    fn main_view(&self, ctx: &LoadableComponentContext<Self>) -> Html {
+        let columns = Rc::clone(&self.remote_list_columns);
+        let link = ctx.link();
+        DataTable::new(columns, self.store.clone())
+            .class(pwt::css::FlexFit)
+            .selection(self.selection.clone())
+            .on_row_dblclick(move |_: &mut _| {
+                link.change_view(Some(ViewState::Edit));
+            })
+            .into()
+    }
+
+    fn dialog_view(
+        &self,
+        ctx: &LoadableComponentContext<Self>,
+        view_state: &Self::ViewState,
+    ) -> Option<Html> {
+        match view_state {
+            ViewState::Add(ty) => Some(self.create_add_dialog(ctx, *ty)),
+            ViewState::Edit => self
+                .selection
+                .selected_key()
+                .map(|key| self.create_edit_dialog(ctx, key)),
+        }
+    }
+}
+
+/*
+fn add_remote_input_panel(_form_ctx: &FormContext) -> Html {
+    InputPanel::new()
+        .padding(4)
+        .with_field(tr!("Remote ID"), Field::new().name("id").required(true))
+        .with_right_field(
+            tr!("Fingerprint"),
+            Field::new()
+                .name("fingerprint")
+                .schema(&CERT_FINGERPRINT_SHA256_SCHEMA),
+        )
+        .with_field(
+            tr!("Server address"),
+            Field::new().name("server").required(true),
+        )
+        .with_field(
+            tr!("User/Token"),
+            Field::new()
+                .name("authid")
+                .schema(&pdm_api_types::Authid::API_SCHEMA)
+                .required(true),
+        )
+        .with_field(
+            tr!("Password/Secret"),
+            Field::new()
+                .name("token")
+                .input_type(InputType::Password)
+                .required(true),
+        )
+        .into()
+}
+*/
+
+impl PbsRemoteConfigPanel {
+    fn create_add_dialog(
+        &self,
+        ctx: &LoadableComponentContext<Self>,
+        remote_type: RemoteType,
+    ) -> Html {
+        super::AddWizard::new(remote_type)
+            .on_close(ctx.link().change_view_callback(|_| None))
+            .on_submit(move |ctx| create_remote(ctx, remote_type))
+            .into()
+
+        // EditWindow::new(tr!("Add") + ": " + &tr!("Remote"))
+        //     .renderer(add_remote_input_panel)
+        //     .on_submit(move |ctx: FormContext| create_item(ctx.get_submit_data(), remote_type))
+        //     .on_done(ctx.link().change_view_callback(|_| None))
+        //     .into()
+    }
+
+    fn create_edit_dialog(&self, ctx: &LoadableComponentContext<Self>, key: Key) -> Html {
+        EditRemote::new(&*key)
+            .on_done(ctx.link().change_view_callback(|_| None))
+            .into()
+    }
+}
+
+impl Into<VNode> for RemoteConfigPanel {
+    fn into(self) -> VNode {
+        let comp = VComp::new::<LoadableComponentMaster<PbsRemoteConfigPanel>>(Rc::new(self), None);
+        VNode::from(comp)
+    }
+}
+
+fn remote_list_columns() -> Rc<Vec<DataTableHeader<Remote>>> {
+    Rc::new(vec![
+        DataTableColumn::new(tr!("Remote ID"))
+            .width("200px")
+            .render(|item: &Remote| {
+                html! {
+                    &item.id
+                }
+            })
+            .sorter(|a: &Remote, b: &Remote| a.id.cmp(&b.id))
+            .sort_order(true)
+            .into(),
+        DataTableColumn::new(tr!("Type"))
+            .width("60px")
+            .render(|item: &Remote| {
+                html! {
+                    &item.ty
+                }
+            })
+            .sorter(|a: &Remote, b: &Remote| a.ty.cmp(&b.ty))
+            .into(),
+        DataTableColumn::new(tr!("AuthId"))
+            .width("200px")
+            .render(|item: &Remote| {
+                html! {
+                    &item.authid
+                }
+            })
+            .sorter(|a: &Remote, b: &Remote| a.authid.cmp(&b.authid))
+            .into(),
+        DataTableColumn::new(tr!("Nodes"))
+            .flex(1)
+            .render(|item: &Remote| {
+                if item.nodes.is_empty() {
+                    html! {tr!("None")}
+                } else {
+                    let nodes = item
+                        .nodes
+                        .iter()
+                        .map(|n| n.hostname.as_str())
+                        .collect::<Vec<_>>()
+                        .join(", ");
+                    let mut tip = Column::new();
+                    tip.add_children(item.nodes.iter().map(|n| {
+                        let text = match n.fingerprint.clone() {
+                            Some(fp) => format!("{} ({fp})", n.hostname),
+                            None => n.hostname.to_string(),
+                        };
+                        html! {<div>{text}</div>}
+                    }));
+                    Tooltip::new(nodes).rich_tip(tip).into()
+                }
+            })
+            .into(),
+        /*
+        DataTableColumn::new(tr!("Auth ID"))
+            .width("200px")
+            .render(|item: &Remote| html!{
+                item.config.auth_id.clone()
+            })
+            .sorter(|a: &Remote, b: &Remote| {
+                a.config.auth_id.cmp(&b.config.auth_id)
+            })
+            .into(),
+
+        DataTableColumn::new(tr!("Fingerprint"))
+            .width("200px")
+            .render(|item: &Remote| html!{
+                item.config.fingerprint.clone().unwrap_or(String::new())
+            })
+            .into(),
+
+        DataTableColumn::new(tr!("Comment"))
+            .flex(1)
+            .render(|item: &Remote| html!{
+                item.config.comment.clone().unwrap_or(String::new())
+            })
+            .into()
+            */
+    ])
+}
diff --git a/ui/src/remotes/mod.rs b/ui/src/remotes/mod.rs
index d11c5e0..f221777 100644
--- a/ui/src/remotes/mod.rs
+++ b/ui/src/remotes/mod.rs
@@ -1,5 +1,4 @@
 mod wizard_page_connect;
-use proxmox_schema::property_string::PropertyString;
 use wizard_page_connect::WizardPageConnect;
 
 mod wizard_page_nodes;
@@ -19,392 +18,5 @@ pub use node_url_list::NodeUrlList;
 
 mod edit_remote;
 
-use std::future::Future;
-use std::pin::Pin;
-use std::rc::Rc;
-
-use anyhow::Error;
-use edit_remote::EditRemote;
-//use pwt::widget::form::{Field, FormContext, InputType};
-
-use pdm_api_types::remotes::Remote;
-//use proxmox_schema::{property_string::PropertyString, ApiType};
-use proxmox_yew_comp::percent_encoding::percent_encode_component;
-
-//use pbs_api_types::CERT_FINGERPRINT_SHA256_SCHEMA;
-
-//use proxmox_schema::api_types::{CERT_FINGERPRINT_SHA256_SCHEMA, DNS_NAME_OR_IP_SCHEMA};
-
-use serde_json::Value;
-use yew::virtual_dom::{Key, VComp, VNode};
-
-use pwt::prelude::*;
-use pwt::state::{Selection, Store};
-use pwt::widget::data_table::{DataTable, DataTableColumn, DataTableHeader};
-//use pwt::widget::form::{delete_empty_values, Field, FormContext, InputType};
-use pwt::widget::{
-    //menu::{Menu, MenuButton, MenuItem},
-    Button,
-    Column,
-    Toolbar,
-    Tooltip,
-};
-//use pwt::widget::InputPanel;
-
-//use proxmox_yew_comp::EditWindow;
-use proxmox_yew_comp::{
-    ConfirmButton, LoadableComponent, LoadableComponentContext, LoadableComponentMaster,
-};
-
-use pdm_api_types::remotes::{NodeUrl, RemoteType};
-
-async fn load_remotes() -> Result<Vec<Remote>, Error> {
-    proxmox_yew_comp::http_get("/remotes", None).await
-}
-
-async fn delete_item(key: Key) -> Result<(), Error> {
-    let id = key.to_string();
-    let url = format!("/remotes/{}", percent_encode_component(&id));
-    proxmox_yew_comp::http_delete(&url, None).await?;
-    Ok(())
-}
-
-pub(crate) async fn create_remote(mut data: Value, remote_type: RemoteType) -> Result<(), Error> {
-    if data.get("nodes").is_none() {
-        let nodes = vec![PropertyString::new(NodeUrl {
-            hostname: data["hostname"].as_str().unwrap_or_default().to_string(),
-            fingerprint: data["fingerprint"].as_str().map(|fp| fp.to_string()),
-        })];
-        data["nodes"] = serde_json::to_value(nodes)?;
-    }
-    data["type"] = match remote_type {
-        RemoteType::Pve => "pve",
-        RemoteType::Pbs => "pbs",
-    }
-    .into();
-
-    let remote: Remote = serde_json::from_value(data.clone())?;
-
-    let mut params = serde_json::to_value(remote)?;
-    if let Some(token) = data["create-token"].as_str() {
-        params["create-token"] = token.into();
-    }
-
-    proxmox_yew_comp::http_post("/remotes", Some(params)).await
-}
-
-/*
-async fn update_item(form_ctx: FormContext) -> Result<(), Error> {
-    let data = form_ctx.get_submit_data();
-
-    let data = delete_empty_values(&data, &["fingerprint", "comment", "port"], true);
-
-    let name = form_ctx.read().get_field_text("name");
-
-    let url = format!("/config/remote/{}", percent_encode_component(&name));
-
-    proxmox_yew_comp::http_put(&url, Some(data)).await
-}
-*/
-
-#[derive(PartialEq, Properties)]
-pub struct RemoteConfigPanel;
-
-impl RemoteConfigPanel {
-    pub fn new() -> Self {
-        yew::props!(Self {})
-    }
-}
-
-#[derive(PartialEq)]
-pub enum ViewState {
-    Add(RemoteType),
-    Edit,
-}
-
-pub enum Msg {
-    SelectionChange,
-    RemoveItem,
-}
-
-pub struct PbsRemoteConfigPanel {
-    store: Store<Remote>,
-    selection: Selection,
-    remote_list_columns: Rc<Vec<DataTableHeader<Remote>>>,
-}
-
-impl LoadableComponent for PbsRemoteConfigPanel {
-    type Message = Msg;
-    type Properties = RemoteConfigPanel;
-    type ViewState = ViewState;
-
-    fn load(
-        &self,
-        _ctx: &LoadableComponentContext<Self>,
-    ) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
-        let store = self.store.clone();
-        Box::pin(async move {
-            let data = load_remotes().await?;
-            store.write().set_data(data);
-            Ok(())
-        })
-    }
-
-    fn create(ctx: &LoadableComponentContext<Self>) -> Self {
-        let store = Store::with_extract_key(|record: &Remote| Key::from(record.id.clone()));
-
-        let selection = Selection::new().on_select(ctx.link().callback(|_| Msg::SelectionChange));
-
-        let remote_list_columns = remote_list_columns();
-
-        Self {
-            store,
-            selection,
-            remote_list_columns,
-        }
-    }
-
-    fn update(&mut self, ctx: &LoadableComponentContext<Self>, msg: Self::Message) -> bool {
-        match msg {
-            Msg::SelectionChange => true,
-            Msg::RemoveItem => {
-                if let Some(key) = self.selection.selected_key() {
-                    let link = ctx.link();
-                    link.clone().spawn(async move {
-                        if let Err(err) = delete_item(key).await {
-                            link.show_error(tr!("Unable to delete item"), err, true);
-                        }
-                        link.send_reload();
-                    })
-                }
-                false
-            }
-        }
-    }
-
-    fn toolbar(&self, ctx: &LoadableComponentContext<Self>) -> Option<Html> {
-        let link = ctx.link();
-
-        let disabled = self.selection.is_empty();
-
-        let toolbar = Toolbar::new()
-            .class("pwt-overflow-hidden")
-            .class("pwt-border-bottom")
-            .with_child({
-                Button::new(tr!("Add Proxmox VE"))
-                    .icon_class("fa fa-building")
-                    .onclick(link.change_view_callback(|_| Some(ViewState::Add(RemoteType::Pve))))
-                // FIXME: add PBS support
-                //MenuButton::new(tr!("Add")).show_arrow(true).menu(
-                //    Menu::new()
-                //        .with_item(
-                //            MenuItem::new("Proxmox VE")
-                //                .icon_class("fa fa-building")
-                //                .on_select(link.change_view_callback(|_| {
-                //                    Some(ViewState::Add(RemoteType::Pve))
-                //                })),
-                //        )
-                //        .with_item(
-                //            MenuItem::new("Proxmox Backup Server")
-                //                .icon_class("fa fa-floppy-o")
-                //                .on_select(link.change_view_callback(|_| {
-                //                    Some(ViewState::Add(RemoteType::Pbs))
-                //                })),
-                //        ),
-                //)
-            })
-            .with_spacer()
-            .with_child(
-                Button::new(tr!("Edit"))
-                    .disabled(disabled)
-                    .onclick(link.change_view_callback(|_| Some(ViewState::Edit))),
-            )
-            .with_child(
-                ConfirmButton::new(tr!("Remove"))
-                    .confirm_message(tr!("Are you sure you want to remove this remote?"))
-                    .disabled(disabled)
-                    .on_activate(link.callback(|_| Msg::RemoveItem)),
-            )
-            .with_flex_spacer()
-            .with_child({
-                let loading = ctx.loading();
-                let link = ctx.link();
-                Button::refresh(loading).onclick(move |_| link.send_reload())
-            });
-
-        Some(toolbar.into())
-    }
-
-    fn main_view(&self, ctx: &LoadableComponentContext<Self>) -> Html {
-        let columns = Rc::clone(&self.remote_list_columns);
-        let link = ctx.link();
-        DataTable::new(columns, self.store.clone())
-            .class(pwt::css::FlexFit)
-            .selection(self.selection.clone())
-            .on_row_dblclick(move |_: &mut _| {
-                link.change_view(Some(ViewState::Edit));
-            })
-            .into()
-    }
-
-    fn dialog_view(
-        &self,
-        ctx: &LoadableComponentContext<Self>,
-        view_state: &Self::ViewState,
-    ) -> Option<Html> {
-        match view_state {
-            ViewState::Add(ty) => Some(self.create_add_dialog(ctx, *ty)),
-            ViewState::Edit => self
-                .selection
-                .selected_key()
-                .map(|key| self.create_edit_dialog(ctx, key)),
-        }
-    }
-}
-
-/*
-fn add_remote_input_panel(_form_ctx: &FormContext) -> Html {
-    InputPanel::new()
-        .padding(4)
-        .with_field(tr!("Remote ID"), Field::new().name("id").required(true))
-        .with_right_field(
-            tr!("Fingerprint"),
-            Field::new()
-                .name("fingerprint")
-                .schema(&CERT_FINGERPRINT_SHA256_SCHEMA),
-        )
-        .with_field(
-            tr!("Server address"),
-            Field::new().name("server").required(true),
-        )
-        .with_field(
-            tr!("User/Token"),
-            Field::new()
-                .name("authid")
-                .schema(&pdm_api_types::Authid::API_SCHEMA)
-                .required(true),
-        )
-        .with_field(
-            tr!("Password/Secret"),
-            Field::new()
-                .name("token")
-                .input_type(InputType::Password)
-                .required(true),
-        )
-        .into()
-}
-*/
-
-impl PbsRemoteConfigPanel {
-    fn create_add_dialog(
-        &self,
-        ctx: &LoadableComponentContext<Self>,
-        remote_type: RemoteType,
-    ) -> Html {
-        AddWizard::new(remote_type)
-            .on_close(ctx.link().change_view_callback(|_| None))
-            .on_submit(move |ctx| create_remote(ctx, remote_type))
-            .into()
-
-        // EditWindow::new(tr!("Add") + ": " + &tr!("Remote"))
-        //     .renderer(add_remote_input_panel)
-        //     .on_submit(move |ctx: FormContext| create_item(ctx.get_submit_data(), remote_type))
-        //     .on_done(ctx.link().change_view_callback(|_| None))
-        //     .into()
-    }
-
-    fn create_edit_dialog(&self, ctx: &LoadableComponentContext<Self>, key: Key) -> Html {
-        EditRemote::new(&*key)
-            .on_done(ctx.link().change_view_callback(|_| None))
-            .into()
-    }
-}
-
-impl Into<VNode> for RemoteConfigPanel {
-    fn into(self) -> VNode {
-        let comp = VComp::new::<LoadableComponentMaster<PbsRemoteConfigPanel>>(Rc::new(self), None);
-        VNode::from(comp)
-    }
-}
-
-fn remote_list_columns() -> Rc<Vec<DataTableHeader<Remote>>> {
-    Rc::new(vec![
-        DataTableColumn::new(tr!("Remote ID"))
-            .width("200px")
-            .render(|item: &Remote| {
-                html! {
-                    &item.id
-                }
-            })
-            .sorter(|a: &Remote, b: &Remote| a.id.cmp(&b.id))
-            .sort_order(true)
-            .into(),
-        DataTableColumn::new(tr!("Type"))
-            .width("60px")
-            .render(|item: &Remote| {
-                html! {
-                    &item.ty
-                }
-            })
-            .sorter(|a: &Remote, b: &Remote| a.ty.cmp(&b.ty))
-            .into(),
-        DataTableColumn::new(tr!("AuthId"))
-            .width("200px")
-            .render(|item: &Remote| {
-                html! {
-                    &item.authid
-                }
-            })
-            .sorter(|a: &Remote, b: &Remote| a.authid.cmp(&b.authid))
-            .into(),
-        DataTableColumn::new(tr!("Nodes"))
-            .flex(1)
-            .render(|item: &Remote| {
-                if item.nodes.is_empty() {
-                    html! {tr!("None")}
-                } else {
-                    let nodes = item
-                        .nodes
-                        .iter()
-                        .map(|n| n.hostname.as_str())
-                        .collect::<Vec<_>>()
-                        .join(", ");
-                    let mut tip = Column::new();
-                    tip.add_children(item.nodes.iter().map(|n| {
-                        let text = match n.fingerprint.clone() {
-                            Some(fp) => format!("{} ({fp})", n.hostname),
-                            None => n.hostname.to_string(),
-                        };
-                        html! {<div>{text}</div>}
-                    }));
-                    Tooltip::new(nodes).rich_tip(tip).into()
-                }
-            })
-            .into(),
-        /*
-        DataTableColumn::new(tr!("Auth ID"))
-            .width("200px")
-            .render(|item: &Remote| html!{
-                item.config.auth_id.clone()
-            })
-            .sorter(|a: &Remote, b: &Remote| {
-                a.config.auth_id.cmp(&b.config.auth_id)
-            })
-            .into(),
-
-        DataTableColumn::new(tr!("Fingerprint"))
-            .width("200px")
-            .render(|item: &Remote| html!{
-                item.config.fingerprint.clone().unwrap_or(String::new())
-            })
-            .into(),
-
-        DataTableColumn::new(tr!("Comment"))
-            .flex(1)
-            .render(|item: &Remote| html!{
-                item.config.comment.clone().unwrap_or(String::new())
-            })
-            .into()
-            */
-    ])
-}
+mod config;
+pub use config::{create_remote, RemoteConfigPanel};
-- 
2.39.5





More information about the pdm-devel mailing list