Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion qracer/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,21 @@ def dashboard() -> None:
)
raise SystemExit(1)

app = QracerDashboard()
from qracer.alerts import AlertStore
from qracer.tasks import TaskStore

_, data_registry, provider_warnings = _build_registries()
for warn in provider_warnings:
click.echo(f" ⚠ {warn}")

alert_store = AlertStore(_user_dir() / "alerts.json")
task_store = TaskStore(_user_dir() / "tasks.json")

app = QracerDashboard(
data_registry=data_registry,
alert_store=alert_store,
task_store=task_store,
)
app.run()


Expand Down
42 changes: 40 additions & 2 deletions qracer/dashboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING

from textual.app import App, ComposeResult
from textual.containers import Horizontal
from textual.widget import Widget
from textual.widgets import Footer, Header

from qracer.dashboard.panels import (
Expand All @@ -15,16 +18,23 @@
OverviewPanel,
PortfolioPanel,
ProvidersSettingsPanel,
TasksPanel,
WatchlistPanel,
)
from qracer.dashboard.sidebar import Sidebar

if TYPE_CHECKING:
from qracer.alerts import AlertStore
from qracer.data.registry import DataRegistry
from qracer.tasks import TaskStore

# Map sidebar item IDs to panel classes.
_PANEL_MAP: dict[str, type] = {
"overview": OverviewPanel,
"portfolio": PortfolioPanel,
"watchlist": WatchlistPanel,
"alerts": AlertsPanel,
"tasks": TasksPanel,
"new-chat": NewChatPanel,
"history": HistoryPanel,
"general": GeneralSettingsPanel,
Expand All @@ -44,13 +54,41 @@ class QracerDashboard(App):
("2", "switch_panel('portfolio')", "Portfolio"),
("3", "switch_panel('watchlist')", "Watchlist"),
("4", "switch_panel('alerts')", "Alerts"),
("5", "switch_panel('tasks')", "Tasks"),
]

def __init__(
self,
*,
data_registry: DataRegistry | None = None,
alert_store: AlertStore | None = None,
task_store: TaskStore | None = None,
) -> None:
super().__init__()
self._data_registry = data_registry
self._alert_store = alert_store
self._task_store = task_store

def _build_panel(self, panel_cls: type, panel_id: str) -> Widget:
"""Instantiate a panel, injecting registries the panel accepts."""
if panel_cls is OverviewPanel:
return OverviewPanel(data_registry=self._data_registry, id=panel_id)
if panel_cls is PortfolioPanel:
return PortfolioPanel(data_registry=self._data_registry, id=panel_id)
if panel_cls is WatchlistPanel:
return WatchlistPanel(data_registry=self._data_registry, id=panel_id)
if panel_cls is AlertsPanel:
return AlertsPanel(alert_store=self._alert_store, id=panel_id)
if panel_cls is TasksPanel:
return TasksPanel(task_store=self._task_store, id=panel_id)
widget: Widget = panel_cls(id=panel_id)
return widget

def compose(self) -> ComposeResult:
yield Header()
with Horizontal(id="main-layout"):
yield Sidebar(id="sidebar")
yield OverviewPanel(id="main-panel")
yield self._build_panel(OverviewPanel, "main-panel")
yield Footer()

def on_mount(self) -> None:
Expand All @@ -68,7 +106,7 @@ def action_switch_panel(self, panel_id: str) -> None:
if panel_cls is None:
return
old = self.query_one("#main-panel")
new_panel = panel_cls(id="main-panel")
new_panel = self._build_panel(panel_cls, "main-panel")
old.remove()
self.query_one("#main-layout").mount(new_panel)
# Update sidebar highlight
Expand Down
Loading
Loading