feat(dashboard): added dashboard content and features (#7)
All checks were successful
CI / Format (push) Successful in 2s
CI / Clippy (push) Successful in 2m18s
CI / Security Audit (push) Successful in 1m40s
CI / Tests (push) Successful in 2m51s
CI / Deploy (push) Successful in 2s

Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com>
Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
2026-02-19 19:23:06 +00:00
parent a588be306a
commit 5399afd748
20 changed files with 3111 additions and 131 deletions

View File

@@ -0,0 +1,112 @@
use dioxus::prelude::*;
use crate::infrastructure::ollama::{get_ollama_status, OllamaStatus};
/// Right sidebar for the dashboard, showing Ollama status, trending topics,
/// and recent search history.
///
/// Appears when no article card is selected. Disappears when the user opens
/// the article detail split view.
///
/// # Props
///
/// * `ollama_url` - Ollama instance URL for status polling
/// * `trending` - Trending topic keywords extracted from recent news headlines
/// * `recent_searches` - Recent search topics stored in localStorage
/// * `on_topic_click` - Fires when a trending or recent topic is clicked
#[component]
pub fn DashboardSidebar(
ollama_url: String,
trending: Vec<String>,
recent_searches: Vec<String>,
on_topic_click: EventHandler<String>,
) -> Element {
// Fetch Ollama status once on mount.
// use_resource with no signal dependencies runs exactly once and
// won't re-fire on parent re-renders (unlike use_effect).
let url = ollama_url.clone();
let status_resource = use_resource(move || {
let u = url.clone();
async move {
get_ollama_status(u).await.unwrap_or(OllamaStatus {
online: false,
models: Vec::new(),
})
}
});
let current_status: OllamaStatus =
status_resource
.read()
.as_ref()
.cloned()
.unwrap_or(OllamaStatus {
online: false,
models: Vec::new(),
});
rsx! {
aside { class: "dashboard-sidebar",
// -- Ollama Status Section --
div { class: "sidebar-section",
h4 { class: "sidebar-section-title", "Ollama Status" }
div { class: "sidebar-status-row",
span { class: if current_status.online { "sidebar-status-dot sidebar-status-dot--online" } else { "sidebar-status-dot sidebar-status-dot--offline" } }
span { class: "sidebar-status-label",
if current_status.online {
"Online"
} else {
"Offline"
}
}
}
if !current_status.models.is_empty() {
div { class: "sidebar-model-list",
for model in current_status.models.iter() {
span { class: "sidebar-model-tag", "{model}" }
}
}
}
}
// -- Trending Topics Section --
if !trending.is_empty() {
div { class: "sidebar-section",
h4 { class: "sidebar-section-title", "Trending" }
for topic in trending.iter() {
{
let t = topic.clone();
rsx! {
button {
class: "sidebar-topic-link",
onclick: move |_| on_topic_click.call(t.clone()),
"{topic}"
}
}
}
}
}
}
// -- Recent Searches Section --
if !recent_searches.is_empty() {
div { class: "sidebar-section",
h4 { class: "sidebar-section-title", "Recent Searches" }
for search in recent_searches.iter() {
{
let s = search.clone();
rsx! {
button {
class: "sidebar-topic-link",
onclick: move |_| on_topic_click.call(s.clone()),
"{search}"
}
}
}
}
}
}
}
}
}