use crate::cases::state::{GlobalScene, SceneState, SCENE};
use crate::explorer::state::{DashboardState, PATHS};
use anyhow::Error;
use rate_ui::shared_object::{DataChanged, SharedObject};
use rate_ui::widget::{Context, NotificationHandler, Widget, WidgetRuntime};
use rill_protocol::io::provider::EntryId;
use yew::{html, Html};
pub type Shield = WidgetRuntime<ShieldWidget>;
pub struct ShieldWidget {
scene: SharedObject<SceneState>,
paths: SharedObject<DashboardState>,
}
impl Default for ShieldWidget {
fn default() -> Self {
Self {
scene: SCENE.with(SharedObject::clone),
paths: PATHS.with(SharedObject::clone),
}
}
}
#[derive(Clone)]
pub enum Msg {
ChangeScene(GlobalScene),
ToLayout(EntryId),
ToDashboard(EntryId, EntryId),
}
impl Widget for ShieldWidget {
type Event = Msg;
type Tag = ();
type Properties = ();
type Meta = ();
fn init(&mut self, ctx: &mut Context<Self>) {
self.scene.subscribe(ctx);
self.paths.subscribe(ctx);
}
fn on_event(&mut self, event: Self::Event, _ctx: &mut Context<Self>) {
match event {
Msg::ChangeScene(scene) => {
self.scene.write().global_scene = scene;
}
Msg::ToLayout(entry_id) => {
let mut scene = self.scene.write();
scene.global_scene = GlobalScene::Cases;
scene.selected_layout = Some(entry_id);
}
Msg::ToDashboard(package, entry_id) => {
let mut scene = self.scene.write();
scene.global_scene = GlobalScene::Explorer;
let mut paths = self.paths.write();
paths.selection.selected_package = Some(package);
paths.selection.selected_dashboard = Some(entry_id);
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let scene_state = self.scene.read();
let paths_state = self.paths.read();
let to_layouts = ctx.event(Msg::ChangeScene(GlobalScene::Cases));
let to_explorer = ctx.event(Msg::ChangeScene(GlobalScene::Explorer));
let cases = if !scene_state.layouts.is_empty() {
html! {
<div>
<h3 class="mt-4 mb-3 pointer" onclick=to_layouts>{ "Cases" }</h3>
<div class="d-flex flex-row flex-wrap">
{ for scene_state.layouts.keys().map(|entry| self.render_layout_card(entry, ctx)) }
</div>
</div>
}
} else {
Html::default()
};
html! {
<div class="flex-grow-1">
<div class="container mt-5">
<h2 class="fw-bold"><span class="text-primary">{ "Live" }</span>{ " Dashboard" }</h2>
{ cases }
<h3 class="mt-4 mb-3 pointer" onclick=to_explorer>{ "Explorer" }</h3>
{ for paths_state.structure.get_packages().map(|entry| self.render_package(entry, ctx)) }
</div>
</div>
}
}
}
const CARD_CLASS: &str = "card me-3 mb-3 bg-primary text-white shadow-sm";
const CARD_BUTTON: &str = "btn btn-outline-primary stretched-link";
impl ShieldWidget {
fn render_package(&self, entry_id: &EntryId, ctx: &Context<Self>) -> Html {
let paths_state = self.paths.read();
html! {
<div>
<h4>{ entry_id }</h4>
<div class="d-flex flex-row flex-wrap">
{ for paths_state.structure.get_dashboards(entry_id).map(|entry| self.render_dashboard_card(entry_id, entry, ctx)) }
</div>
</div>
}
}
fn render_layout_card(&self, entry_id: &EntryId, ctx: &Context<Self>) -> Html {
let callback = ctx.event(Msg::ToLayout(entry_id.clone()));
html! {
<div class=CARD_CLASS>
<div class="card-body text-center" style="width: 16rem;">
<h5 class="card-title mb-3">{ entry_id }</h5>
<a class=CARD_BUTTON onclick=callback>{ "Open" }</a>
</div>
</div>
}
}
fn render_dashboard_card(
&self,
package: &EntryId,
entry_id: &EntryId,
ctx: &Context<Self>,
) -> Html {
let callback = ctx.event(Msg::ToDashboard(package.clone(), entry_id.clone()));
html! {
<div class=CARD_CLASS>
<div class="card-body text-center" style="width: 16rem;">
<h5 class="card-title mb-3">{ entry_id }</h5>
<a class=CARD_BUTTON onclick=callback>{ "Open" }</a>
</div>
</div>
}
}
}
impl NotificationHandler<DataChanged<SceneState>> for ShieldWidget {
fn handle(
&mut self,
_event: DataChanged<SceneState>,
ctx: &mut Context<Self>,
) -> Result<(), Error> {
ctx.redraw();
Ok(())
}
}
impl NotificationHandler<DataChanged<DashboardState>> for ShieldWidget {
fn handle(
&mut self,
_event: DataChanged<DashboardState>,
ctx: &mut Context<Self>,
) -> Result<(), Error> {
ctx.redraw();
Ok(())
}
}