Skip to content

Commit f66fd7d

Browse files
committed
refactor: move component logic from mod.rs into dedicated view files
Module roots now contain only `pub mod` / `pub use` declarations. The view components themselves live in sibling `view.rs` (or `host.rs` for the modal host) files, matching the workspace convention that mod.rs holds no logic. Affected modules: modals, views/admin, views/explorer, views/graph_explorer, views/streams.
1 parent 1ad9abd commit f66fd7d

10 files changed

Lines changed: 237 additions & 216 deletions

File tree

nodedb-studio/src/modals/host.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! The modal host: renders whichever modal the shared `ModalKind` signal
2+
//! reports as open.
3+
4+
use dioxus::prelude::*;
5+
6+
use crate::components::modal::Modal;
7+
use crate::modals::new_connection::NewConnectionForm;
8+
use crate::modals::preferences::PreferencesPanes;
9+
use crate::state::ui::ModalKind;
10+
11+
/// Renders the currently-open modal (from the shared `ModalKind` signal), or
12+
/// nothing. Overlays everything; provided once near the app root.
13+
#[component]
14+
pub fn ModalHost() -> Element {
15+
let modal = use_context::<Signal<Option<ModalKind>>>();
16+
match *modal.read() {
17+
None => rsx! {},
18+
Some(ModalKind::NewConnection) => rsx! {
19+
Modal { title: "New connection", NewConnectionForm {} }
20+
},
21+
Some(ModalKind::Preferences) => rsx! {
22+
Modal { title: "Preferences", wide: true, PreferencesPanes {} }
23+
},
24+
}
25+
}

nodedb-studio/src/modals/mod.rs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,7 @@
11
//! Modal content + the host that decides which modal is open.
22
3+
pub mod host;
34
pub mod new_connection;
45
pub mod preferences;
56

6-
use dioxus::prelude::*;
7-
8-
use crate::components::modal::Modal;
9-
use crate::modals::new_connection::NewConnectionForm;
10-
use crate::modals::preferences::PreferencesPanes;
11-
use crate::state::ui::ModalKind;
12-
13-
/// Renders the currently-open modal (from the shared `ModalKind` signal), or
14-
/// nothing. Overlays everything; provided once near the app root.
15-
#[component]
16-
pub fn ModalHost() -> Element {
17-
let modal = use_context::<Signal<Option<ModalKind>>>();
18-
match *modal.read() {
19-
None => rsx! {},
20-
Some(ModalKind::NewConnection) => rsx! {
21-
Modal { title: "New connection", NewConnectionForm {} }
22-
},
23-
Some(ModalKind::Preferences) => rsx! {
24-
Modal { title: "Preferences", wide: true, PreferencesPanes {} }
25-
},
26-
}
27-
}
7+
pub use host::ModalHost;
Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
//! Admin. The `tab` route segment selects the sub-tab. The four cluster tabs
2-
//! (cluster/shards/nodes/raft) hide on single-node connections; if the active
3-
//! tab is cluster-gated on such a connection, it falls back to RBAC.
1+
//! Admin views.
42
53
pub mod audit;
64
pub mod cluster;
@@ -9,51 +7,6 @@ pub mod raft;
97
pub mod rbac;
108
pub mod rls;
119
pub mod shards;
10+
mod view;
1211

13-
use dioxus::prelude::*;
14-
15-
use crate::components::subnav::AdminSubnav;
16-
use crate::state::connection::ActiveConnection;
17-
use crate::views::admin::audit::AuditView;
18-
use crate::views::admin::cluster::ClusterView;
19-
use crate::views::admin::nodes::NodesView;
20-
use crate::views::admin::raft::RaftView;
21-
use crate::views::admin::rbac::RbacView;
22-
use crate::views::admin::rls::RlsView;
23-
use crate::views::admin::shards::ShardsView;
24-
25-
fn is_cluster_tab(tab: &str) -> bool {
26-
matches!(tab, "cluster" | "shards" | "nodes" | "raft")
27-
}
28-
29-
#[component]
30-
pub fn Admin(tab: String) -> Element {
31-
let active = use_context::<Signal<Option<ActiveConnection>>>();
32-
let cluster = active
33-
.read()
34-
.as_ref()
35-
.map(|c| c.capabilities.cluster)
36-
.unwrap_or(false);
37-
38-
// A cluster-gated tab on a single-node connection falls back to RBAC.
39-
let effective = if is_cluster_tab(&tab) && !cluster {
40-
"rbac".to_string()
41-
} else {
42-
tab.clone()
43-
};
44-
45-
rsx! {
46-
div { class: "view active",
47-
AdminSubnav { active: effective.clone(), cluster }
48-
match effective.as_str() {
49-
"cluster" => rsx! { ClusterView {} },
50-
"shards" => rsx! { ShardsView {} },
51-
"nodes" => rsx! { NodesView {} },
52-
"raft" => rsx! { RaftView {} },
53-
"rls" => rsx! { RlsView {} },
54-
"audit" => rsx! { AuditView {} },
55-
_ => rsx! { RbacView {} },
56-
}
57-
}
58-
}
59-
}
12+
pub use view::Admin;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Admin. The `tab` route segment selects the sub-tab. The four cluster tabs
2+
//! (cluster/shards/nodes/raft) hide on single-node connections; if the active
3+
//! tab is cluster-gated on such a connection, it falls back to RBAC.
4+
5+
use dioxus::prelude::*;
6+
7+
use crate::components::subnav::AdminSubnav;
8+
use crate::state::connection::ActiveConnection;
9+
use crate::views::admin::audit::AuditView;
10+
use crate::views::admin::cluster::ClusterView;
11+
use crate::views::admin::nodes::NodesView;
12+
use crate::views::admin::raft::RaftView;
13+
use crate::views::admin::rbac::RbacView;
14+
use crate::views::admin::rls::RlsView;
15+
use crate::views::admin::shards::ShardsView;
16+
17+
fn is_cluster_tab(tab: &str) -> bool {
18+
matches!(tab, "cluster" | "shards" | "nodes" | "raft")
19+
}
20+
21+
#[component]
22+
pub fn Admin(tab: String) -> Element {
23+
let active = use_context::<Signal<Option<ActiveConnection>>>();
24+
let cluster = active
25+
.read()
26+
.as_ref()
27+
.map(|c| c.capabilities.cluster)
28+
.unwrap_or(false);
29+
30+
// A cluster-gated tab on a single-node connection falls back to RBAC.
31+
let effective = if is_cluster_tab(&tab) && !cluster {
32+
"rbac".to_string()
33+
} else {
34+
tab.clone()
35+
};
36+
37+
rsx! {
38+
div { class: "view active",
39+
AdminSubnav { active: effective.clone(), cluster }
40+
match effective.as_str() {
41+
"cluster" => rsx! { ClusterView {} },
42+
"shards" => rsx! { ShardsView {} },
43+
"nodes" => rsx! { NodesView {} },
44+
"raft" => rsx! { RaftView {} },
45+
"rls" => rsx! { RlsView {} },
46+
"audit" => rsx! { AuditView {} },
47+
_ => rsx! { RbacView {} },
48+
}
49+
}
50+
}
51+
}
Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,7 @@
11
//! Explorer: the engine-aware collection browser.
2-
//!
3-
//! A single NodeDB connection exposes eight storage modes. The sidebar lists
4-
//! collections grouped by mode; selecting one swaps the main pane to that
5-
//! mode's purpose-built viewer. The selected collection is shared between the
6-
//! sidebar and the viewer pane via a signal owned here.
72
83
pub mod sidebar;
4+
mod view;
95
pub mod viewers;
106

11-
use dioxus::prelude::*;
12-
13-
use crate::models::collection::StorageMode;
14-
use crate::views::explorer::sidebar::ExplorerSidebar;
15-
use crate::views::explorer::viewers::document::DocumentViewer;
16-
use crate::views::explorer::viewers::fts::FtsViewer;
17-
use crate::views::explorer::viewers::graph::GraphViewer;
18-
use crate::views::explorer::viewers::kv::KvViewer;
19-
use crate::views::explorer::viewers::spatial::SpatialViewer;
20-
use crate::views::explorer::viewers::strict::StrictViewer;
21-
use crate::views::explorer::viewers::timeseries::TimeseriesViewer;
22-
use crate::views::explorer::viewers::vector::VectorViewer;
23-
24-
/// The currently-selected collection (name + its storage mode).
25-
#[derive(Clone, PartialEq)]
26-
pub struct Selected {
27-
pub name: String,
28-
pub mode: StorageMode,
29-
}
30-
31-
#[component]
32-
pub fn Explorer() -> Element {
33-
let selected = use_signal(|| Selected {
34-
name: "events".to_string(),
35-
mode: StorageMode::Document,
36-
});
37-
let sel = selected.read().clone();
38-
39-
rsx! {
40-
div { class: "view active",
41-
div { class: "explorer",
42-
ExplorerSidebar { selected }
43-
div { class: "explorer-main",
44-
div { class: "viewer-header",
45-
h2 {
46-
span { "{sel.name}" }
47-
span { class: "sub", "{sel.mode.key()}" }
48-
}
49-
div { class: "viewer-actions",
50-
button { class: "btn small", "Schema" }
51-
button { class: "btn small", "Indexes" }
52-
button { class: "btn small", "Export" }
53-
button { class: "btn small primary", "+ Insert" }
54-
}
55-
}
56-
div { class: "viewer-body",
57-
match sel.mode {
58-
StorageMode::Document => rsx! { DocumentViewer {} },
59-
StorageMode::Strict => rsx! { StrictViewer {} },
60-
StorageMode::Vector => rsx! { VectorViewer {} },
61-
StorageMode::Graph => rsx! { GraphViewer {} },
62-
StorageMode::Timeseries => rsx! { TimeseriesViewer {} },
63-
StorageMode::Kv => rsx! { KvViewer {} },
64-
StorageMode::Spatial => rsx! { SpatialViewer {} },
65-
StorageMode::Fts => rsx! { FtsViewer {} },
66-
}
67-
}
68-
}
69-
}
70-
}
71-
}
72-
}
7+
pub use view::{Explorer, Selected};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//! Explorer: the engine-aware collection browser.
2+
//!
3+
//! A single NodeDB connection exposes eight storage modes. The sidebar lists
4+
//! collections grouped by mode; selecting one swaps the main pane to that
5+
//! mode's purpose-built viewer. The selected collection is shared between the
6+
//! sidebar and the viewer pane via a signal owned here.
7+
8+
use dioxus::prelude::*;
9+
10+
use crate::models::collection::StorageMode;
11+
use crate::views::explorer::sidebar::ExplorerSidebar;
12+
use crate::views::explorer::viewers::document::DocumentViewer;
13+
use crate::views::explorer::viewers::fts::FtsViewer;
14+
use crate::views::explorer::viewers::graph::GraphViewer;
15+
use crate::views::explorer::viewers::kv::KvViewer;
16+
use crate::views::explorer::viewers::spatial::SpatialViewer;
17+
use crate::views::explorer::viewers::strict::StrictViewer;
18+
use crate::views::explorer::viewers::timeseries::TimeseriesViewer;
19+
use crate::views::explorer::viewers::vector::VectorViewer;
20+
21+
/// The currently-selected collection (name + its storage mode).
22+
#[derive(Clone, PartialEq)]
23+
pub struct Selected {
24+
pub name: String,
25+
pub mode: StorageMode,
26+
}
27+
28+
#[component]
29+
pub fn Explorer() -> Element {
30+
let selected = use_signal(|| Selected {
31+
name: "events".to_string(),
32+
mode: StorageMode::Document,
33+
});
34+
let sel = selected.read().clone();
35+
36+
rsx! {
37+
div { class: "view active",
38+
div { class: "explorer",
39+
ExplorerSidebar { selected }
40+
div { class: "explorer-main",
41+
div { class: "viewer-header",
42+
h2 {
43+
span { "{sel.name}" }
44+
span { class: "sub", "{sel.mode.key()}" }
45+
}
46+
div { class: "viewer-actions",
47+
button { class: "btn small", "Schema" }
48+
button { class: "btn small", "Indexes" }
49+
button { class: "btn small", "Export" }
50+
button { class: "btn small primary", "+ Insert" }
51+
}
52+
}
53+
div { class: "viewer-body",
54+
match sel.mode {
55+
StorageMode::Document => rsx! { DocumentViewer {} },
56+
StorageMode::Strict => rsx! { StrictViewer {} },
57+
StorageMode::Vector => rsx! { VectorViewer {} },
58+
StorageMode::Graph => rsx! { GraphViewer {} },
59+
StorageMode::Timeseries => rsx! { TimeseriesViewer {} },
60+
StorageMode::Kv => rsx! { KvViewer {} },
61+
StorageMode::Spatial => rsx! { SpatialViewer {} },
62+
StorageMode::Fts => rsx! { FtsViewer {} },
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}
Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,8 @@
1-
//! Graph Explorer: a top bar with a Cypher / Builder / GraphRAG mode switch
2-
//! over a shared canvas. The mode is local component state.
1+
//! Graph Explorer views.
32
43
pub mod builder;
54
pub mod cypher;
65
pub mod graphrag;
6+
mod view;
77

8-
use dioxus::prelude::*;
9-
10-
use crate::views::graph_explorer::builder::GxBuilder;
11-
use crate::views::graph_explorer::cypher::GxCypher;
12-
use crate::views::graph_explorer::graphrag::GxGraphrag;
13-
14-
#[component]
15-
pub fn GraphExplorer() -> Element {
16-
let mut mode = use_signal(|| "cypher".to_string());
17-
let m = mode.read().clone();
18-
19-
rsx! {
20-
div { class: "view active",
21-
div { class: "gx-frame",
22-
div { class: "gx-top",
23-
div { class: "segmented",
24-
button { class: if m == "cypher" { "active" } else { "" }, onclick: move |_| mode.set("cypher".to_string()), "Cypher" }
25-
button { class: if m == "builder" { "active" } else { "" }, onclick: move |_| mode.set("builder".to_string()), "Builder" }
26-
button { class: if m == "graphrag" { "active" } else { "" }, onclick: move |_| mode.set("graphrag".to_string()), "GraphRAG" }
27-
}
28-
div { style: "font-family: var(--font-mono); font-size: 11px; color: var(--text-tertiary);",
29-
"graph: social_graph · 3.2M nodes · 18.4M edges"
30-
}
31-
div { style: "margin-left: auto; display: flex; gap: 6px;",
32-
button { class: "btn small", "Layout: force" }
33-
button { class: "btn small", "Limit: 50" }
34-
button { class: "btn small primary", "▸ Run" }
35-
}
36-
}
37-
match m.as_str() {
38-
"builder" => rsx! { GxBuilder {} },
39-
"graphrag" => rsx! { GxGraphrag {} },
40-
_ => rsx! { GxCypher {} },
41-
}
42-
}
43-
}
44-
}
45-
}
8+
pub use view::GraphExplorer;

0 commit comments

Comments
 (0)