1#![allow(clippy::too_many_arguments)]
12#![allow(dead_code)]
13
14use crate::error::{MetricsError, Result};
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17use std::collections::HashMap;
18use std::sync::{Arc, Mutex, RwLock};
19use std::time::Instant;
20
21pub mod collaboration;
23pub mod core;
24pub mod data_sources;
25pub mod events;
26pub mod layout;
27pub mod rendering;
28pub mod widgets;
29
30pub use core::*;
32
33pub use widgets::{
35 BorderConfig, ChartType, DataBindingConfig, EventType, FontConfig, InputType,
36 InteractiveWidget, RenderContent, RenderContext, ShaderProgram, StyleConfig, WidgetConfig,
37 WidgetEvent, WidgetEventResponse, WidgetType,
38};
39
40pub use data_sources::{
42 ChangeType, ConnectionConfig, DataFormat, DataSource, DataSourceConfig, DataSourceManager,
43 DataSourceType, DataUpdate, ValidationConfig,
44};
45
46pub use events::{
48 DashboardEvent, EventAction, EventHandler, EventMetadata, EventPriority, EventResponse,
49 EventSystem, NotificationLevel,
50};
51
52pub use layout::{
54 ContainerConstraints, GridPosition, LayoutConstraints, LayoutManager, Margin, WidgetLayout,
55};
56
57pub use rendering::{
59 PerformanceMonitor, RenderStatistics, RenderingBackend, RenderingConfig, RenderingEngine,
60 UpdateManager, UpdateRequest, UpdateType,
61};
62
63pub use collaboration::{
65 CollaborationManager, Conflict, ConflictResolver, ConflictType, CursorPosition, Operation,
66 OperationType, Selection, SharedState, UserSession,
67};
68
69pub struct InteractiveDashboard {
71 config: DashboardConfig,
73 widgets: Arc<RwLock<HashMap<String, Box<dyn InteractiveWidget + Send + Sync>>>>,
75 data_sources: Arc<RwLock<HashMap<String, Box<dyn DataSource + Send + Sync>>>>,
77 event_system: Arc<Mutex<EventSystem>>,
79 layout_manager: Arc<Mutex<LayoutManager>>,
81 renderer: Arc<Mutex<Box<dyn RenderingEngine + Send + Sync>>>,
83 update_manager: Arc<Mutex<UpdateManager>>,
85 collaboration: Arc<Mutex<CollaborationManager>>,
87 state: Arc<RwLock<DashboardState>>,
89}
90
91impl std::fmt::Debug for InteractiveDashboard {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 f.debug_struct("InteractiveDashboard")
94 .field("config", &self.config)
95 .field(
96 "widgets",
97 &format!("{} widgets", self.widgets.read().unwrap().len()),
98 )
99 .field(
100 "data_sources",
101 &format!("{} data sources", self.data_sources.read().unwrap().len()),
102 )
103 .field("event_system", &"<event_system>")
104 .field("layout_manager", &"<layout_manager>")
105 .field("renderer", &"<renderer>")
106 .field("update_manager", &"<update_manager>")
107 .field("collaboration", &"<collaboration>")
108 .field("state", &"<state>")
109 .finish()
110 }
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, Default)]
115pub struct DashboardState {
116 pub view_state: ViewState,
118 pub filters: HashMap<String, Value>,
120 pub selections: Vec<String>,
122 pub viewport: ViewportState,
124 pub time_range: Option<TimeRange>,
126 pub custom_state: HashMap<String, Value>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct ViewState {
133 pub mode: ViewMode,
135 pub visible_widgets: Vec<String>,
137 pub z_order: Vec<String>,
139 pub layout_mode: String,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub enum ViewMode {
146 Normal,
148 FullScreen,
150 Presentation,
152 Edit,
154 Preview,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct ViewportState {
161 pub zoom: f64,
163 pub pan_x: f64,
165 pub pan_y: f64,
167 pub bounds: ViewportBounds,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ViewportBounds {
174 pub min_x: f64,
176 pub max_x: f64,
178 pub min_y: f64,
180 pub max_y: f64,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct TimeRange {
187 pub start: std::time::SystemTime,
189 pub end: std::time::SystemTime,
191 pub timezone: Option<String>,
193}
194
195impl InteractiveDashboard {
196 pub fn new(
198 config: DashboardConfig,
199 renderer: Box<dyn RenderingEngine + Send + Sync>,
200 ) -> Result<Self> {
201 let container_constraints = ContainerConstraints {
202 width: config.width as f64,
203 height: config.height as f64,
204 ..Default::default()
205 };
206
207 let layout_manager = LayoutManager::new(config.layout.clone(), container_constraints);
208 let event_system = EventSystem::new(events::EventSystemConfig::default());
209 let update_manager = UpdateManager::new(rendering::UpdateConfig::default());
210
211 let collaboration = if let Some(collab_config) = &config.collaboration_config {
212 CollaborationManager::new(collab_config.clone())
213 } else {
214 CollaborationManager::new(CollaborationConfig {
216 enabled: false,
217 auth: core::AuthConfig {
218 method: core::AuthMethod::None,
219 session_timeout: std::time::Duration::from_secs(3600),
220 guest_access: true,
221 required_permissions: Vec::new(),
222 },
223 sharing: core::ShareConfig {
224 public_sharing: false,
225 default_permission: core::PermissionLevel::ReadOnly,
226 link_expiration: None,
227 password_protection: false,
228 },
229 sync: core::SyncConfig {
230 sync_interval: std::time::Duration::from_secs(5),
231 conflict_resolution: core::ConflictResolution::LastWriterWins,
232 operational_transforms: false,
233 history_retention: std::time::Duration::from_secs(3600),
234 },
235 max_collaborators: 1,
236 })
237 };
238
239 Ok(Self {
240 config,
241 widgets: Arc::new(RwLock::new(HashMap::new())),
242 data_sources: Arc::new(RwLock::new(HashMap::new())),
243 event_system: Arc::new(Mutex::new(event_system)),
244 layout_manager: Arc::new(Mutex::new(layout_manager)),
245 renderer: Arc::new(Mutex::new(renderer)),
246 update_manager: Arc::new(Mutex::new(update_manager)),
247 collaboration: Arc::new(Mutex::new(collaboration)),
248 state: Arc::new(RwLock::new(DashboardState::default())),
249 })
250 }
251
252 pub fn add_widget(&self, widget: Box<dyn InteractiveWidget + Send + Sync>) -> Result<()> {
254 let widget_id = widget.id().to_string();
255 let widget_config = widget.config().clone();
256
257 self.widgets
259 .write()
260 .unwrap()
261 .insert(widget_id.clone(), widget);
262
263 self.layout_manager
265 .lock()
266 .unwrap()
267 .add_widget(&widget_config)?;
268
269 let mut state = self.state.write().unwrap();
271 state.view_state.visible_widgets.push(widget_id.clone());
272 state.view_state.z_order.push(widget_id);
273
274 Ok(())
275 }
276
277 pub fn remove_widget(&self, widget_id: &str) -> Result<()> {
279 self.widgets.write().unwrap().remove(widget_id);
281
282 self.layout_manager
284 .lock()
285 .unwrap()
286 .remove_widget(widget_id)?;
287
288 let mut state = self.state.write().unwrap();
290 state
291 .view_state
292 .visible_widgets
293 .retain(|id| id != widget_id);
294 state.view_state.z_order.retain(|id| id != widget_id);
295
296 Ok(())
297 }
298
299 pub fn register_data_source(&self, source: Box<dyn DataSource + Send + Sync>) -> Result<()> {
301 let source_id = source.id().to_string();
302 self.data_sources.write().unwrap().insert(source_id, source);
303 Ok(())
304 }
305
306 pub fn handle_interaction(&self, event: WidgetEvent) -> Result<()> {
308 let dashboard_event = DashboardEvent {
310 id: event.id.clone(),
311 event_type: format!("{:?}", event.event_type),
312 source: event.source_widget,
313 target: event.target,
314 timestamp: event.timestamp,
315 data: event.data,
316 metadata: EventMetadata::default(),
317 };
318
319 self.event_system
321 .lock()
322 .unwrap()
323 .queue_event(dashboard_event)?;
324
325 Ok(())
326 }
327
328 pub fn update_state(&self, updates: HashMap<String, Value>) -> Result<()> {
330 let mut state = self.state.write().unwrap();
331
332 for (key, value) in updates {
333 state.custom_state.insert(key, value);
334 }
335
336 Ok(())
337 }
338
339 pub fn render(&self, context: &RenderContext) -> Result<()> {
341 let widgets = self.widgets.read().unwrap();
342 let renderer = self.renderer.lock().unwrap();
343
344 renderer.clear([0.0, 0.0, 0.0, 1.0])?;
346
347 let state = self.state.read().unwrap();
349 let mut visible_widgets: Vec<_> = state
350 .view_state
351 .z_order
352 .iter()
353 .filter(|id| state.view_state.visible_widgets.contains(id))
354 .filter_map(|id| widgets.get(id))
355 .collect();
356
357 visible_widgets.sort_by_key(|widget| widget.config().z_index);
359
360 for widget in visible_widgets {
362 if let Ok(widget_render) = widget.render(context) {
363 renderer.render_widget(&widget_render, context)?;
364 }
365 }
366
367 renderer.present()?;
369
370 Ok(())
371 }
372
373 pub fn process_updates(&self) -> Result<()> {
375 let _responses = self.event_system.lock().unwrap().process_events()?;
377
378 let _update_requests = self.update_manager.lock().unwrap().process_updates()?;
380
381 if self
383 .config
384 .collaboration_config
385 .as_ref()
386 .is_some_and(|c| c.enabled)
387 {
388 let _resolved_operations = self.collaboration.lock().unwrap().resolve_conflicts()?;
389 }
390
391 Ok(())
392 }
393
394 pub fn config(&self) -> &DashboardConfig {
396 &self.config
397 }
398
399 pub fn state(&self) -> DashboardState {
401 self.state.read().unwrap().clone()
402 }
403
404 pub fn export_config(&self) -> Result<Value> {
406 serde_json::to_value(&self.config).map_err(|e| MetricsError::InvalidInput(e.to_string()))
407 }
408
409 pub fn import_config(&mut self, config_data: Value) -> Result<()> {
411 self.config = serde_json::from_value(config_data)
412 .map_err(|e| MetricsError::InvalidInput(e.to_string()))?;
413 Ok(())
414 }
415}
416
417impl Default for ViewState {
418 fn default() -> Self {
419 Self {
420 mode: ViewMode::Normal,
421 visible_widgets: Vec::new(),
422 z_order: Vec::new(),
423 layout_mode: "grid".to_string(),
424 }
425 }
426}
427
428impl Default for ViewportState {
429 fn default() -> Self {
430 Self {
431 zoom: 1.0,
432 pan_x: 0.0,
433 pan_y: 0.0,
434 bounds: ViewportBounds::default(),
435 }
436 }
437}
438
439impl Default for ViewportBounds {
440 fn default() -> Self {
441 Self {
442 min_x: 0.0,
443 max_x: 1200.0,
444 min_y: 0.0,
445 max_y: 800.0,
446 }
447 }
448}