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!(
98 "{} widgets",
99 self.widgets.read().expect("Operation failed").len()
100 ),
101 )
102 .field(
103 "data_sources",
104 &format!(
105 "{} data sources",
106 self.data_sources.read().expect("Operation failed").len()
107 ),
108 )
109 .field("event_system", &"<event_system>")
110 .field("layout_manager", &"<layout_manager>")
111 .field("renderer", &"<renderer>")
112 .field("update_manager", &"<update_manager>")
113 .field("collaboration", &"<collaboration>")
114 .field("state", &"<state>")
115 .finish()
116 }
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, Default)]
121pub struct DashboardState {
122 pub view_state: ViewState,
124 pub filters: HashMap<String, Value>,
126 pub selections: Vec<String>,
128 pub viewport: ViewportState,
130 pub time_range: Option<TimeRange>,
132 pub custom_state: HashMap<String, Value>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ViewState {
139 pub mode: ViewMode,
141 pub visible_widgets: Vec<String>,
143 pub z_order: Vec<String>,
145 pub layout_mode: String,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub enum ViewMode {
152 Normal,
154 FullScreen,
156 Presentation,
158 Edit,
160 Preview,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct ViewportState {
167 pub zoom: f64,
169 pub pan_x: f64,
171 pub pan_y: f64,
173 pub bounds: ViewportBounds,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ViewportBounds {
180 pub min_x: f64,
182 pub max_x: f64,
184 pub min_y: f64,
186 pub max_y: f64,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct TimeRange {
193 pub start: std::time::SystemTime,
195 pub end: std::time::SystemTime,
197 pub timezone: Option<String>,
199}
200
201impl InteractiveDashboard {
202 pub fn new(
204 config: DashboardConfig,
205 renderer: Box<dyn RenderingEngine + Send + Sync>,
206 ) -> Result<Self> {
207 let container_constraints = ContainerConstraints {
208 width: config.width as f64,
209 height: config.height as f64,
210 ..Default::default()
211 };
212
213 let layout_manager = LayoutManager::new(config.layout.clone(), container_constraints);
214 let event_system = EventSystem::new(events::EventSystemConfig::default());
215 let update_manager = UpdateManager::new(rendering::UpdateConfig::default());
216
217 let collaboration = if let Some(collab_config) = &config.collaboration_config {
218 CollaborationManager::new(collab_config.clone())
219 } else {
220 CollaborationManager::new(CollaborationConfig {
222 enabled: false,
223 auth: core::AuthConfig {
224 method: core::AuthMethod::None,
225 session_timeout: std::time::Duration::from_secs(3600),
226 guest_access: true,
227 required_permissions: Vec::new(),
228 },
229 sharing: core::ShareConfig {
230 public_sharing: false,
231 default_permission: core::PermissionLevel::ReadOnly,
232 link_expiration: None,
233 password_protection: false,
234 },
235 sync: core::SyncConfig {
236 sync_interval: std::time::Duration::from_secs(5),
237 conflict_resolution: core::ConflictResolution::LastWriterWins,
238 operational_transforms: false,
239 history_retention: std::time::Duration::from_secs(3600),
240 },
241 max_collaborators: 1,
242 })
243 };
244
245 Ok(Self {
246 config,
247 widgets: Arc::new(RwLock::new(HashMap::new())),
248 data_sources: Arc::new(RwLock::new(HashMap::new())),
249 event_system: Arc::new(Mutex::new(event_system)),
250 layout_manager: Arc::new(Mutex::new(layout_manager)),
251 renderer: Arc::new(Mutex::new(renderer)),
252 update_manager: Arc::new(Mutex::new(update_manager)),
253 collaboration: Arc::new(Mutex::new(collaboration)),
254 state: Arc::new(RwLock::new(DashboardState::default())),
255 })
256 }
257
258 pub fn add_widget(&self, widget: Box<dyn InteractiveWidget + Send + Sync>) -> Result<()> {
260 let widget_id = widget.id().to_string();
261 let widget_config = widget.config().clone();
262
263 self.widgets
265 .write()
266 .expect("Operation failed")
267 .insert(widget_id.clone(), widget);
268
269 self.layout_manager
271 .lock()
272 .expect("Operation failed")
273 .add_widget(&widget_config)?;
274
275 let mut state = self.state.write().expect("Operation failed");
277 state.view_state.visible_widgets.push(widget_id.clone());
278 state.view_state.z_order.push(widget_id);
279
280 Ok(())
281 }
282
283 pub fn remove_widget(&self, widget_id: &str) -> Result<()> {
285 self.widgets
287 .write()
288 .expect("Operation failed")
289 .remove(widget_id);
290
291 self.layout_manager
293 .lock()
294 .expect("Operation failed")
295 .remove_widget(widget_id)?;
296
297 let mut state = self.state.write().expect("Operation failed");
299 state
300 .view_state
301 .visible_widgets
302 .retain(|id| id != widget_id);
303 state.view_state.z_order.retain(|id| id != widget_id);
304
305 Ok(())
306 }
307
308 pub fn register_data_source(&self, source: Box<dyn DataSource + Send + Sync>) -> Result<()> {
310 let source_id = source.id().to_string();
311 self.data_sources
312 .write()
313 .expect("Operation failed")
314 .insert(source_id, source);
315 Ok(())
316 }
317
318 pub fn handle_interaction(&self, event: WidgetEvent) -> Result<()> {
320 let dashboard_event = DashboardEvent {
322 id: event.id.clone(),
323 event_type: format!("{:?}", event.event_type),
324 source: event.source_widget,
325 target: event.target,
326 timestamp: event.timestamp,
327 data: event.data,
328 metadata: EventMetadata::default(),
329 };
330
331 self.event_system
333 .lock()
334 .expect("Operation failed")
335 .queue_event(dashboard_event)?;
336
337 Ok(())
338 }
339
340 pub fn update_state(&self, updates: HashMap<String, Value>) -> Result<()> {
342 let mut state = self.state.write().expect("Operation failed");
343
344 for (key, value) in updates {
345 state.custom_state.insert(key, value);
346 }
347
348 Ok(())
349 }
350
351 pub fn render(&self, context: &RenderContext) -> Result<()> {
353 let widgets = self.widgets.read().expect("Operation failed");
354 let renderer = self.renderer.lock().expect("Operation failed");
355
356 renderer.clear([0.0, 0.0, 0.0, 1.0])?;
358
359 let state = self.state.read().expect("Operation failed");
361 let mut visible_widgets: Vec<_> = state
362 .view_state
363 .z_order
364 .iter()
365 .filter(|id| state.view_state.visible_widgets.contains(id))
366 .filter_map(|id| widgets.get(id))
367 .collect();
368
369 visible_widgets.sort_by_key(|widget| widget.config().z_index);
371
372 for widget in visible_widgets {
374 if let Ok(widget_render) = widget.render(context) {
375 renderer.render_widget(&widget_render, context)?;
376 }
377 }
378
379 renderer.present()?;
381
382 Ok(())
383 }
384
385 pub fn process_updates(&self) -> Result<()> {
387 let _responses = self
389 .event_system
390 .lock()
391 .expect("Operation failed")
392 .process_events()?;
393
394 let _update_requests = self
396 .update_manager
397 .lock()
398 .expect("Operation failed")
399 .process_updates()?;
400
401 if self
403 .config
404 .collaboration_config
405 .as_ref()
406 .is_some_and(|c| c.enabled)
407 {
408 let _resolved_operations = self
409 .collaboration
410 .lock()
411 .expect("Operation failed")
412 .resolve_conflicts()?;
413 }
414
415 Ok(())
416 }
417
418 pub fn config(&self) -> &DashboardConfig {
420 &self.config
421 }
422
423 pub fn state(&self) -> DashboardState {
425 self.state.read().expect("Operation failed").clone()
426 }
427
428 pub fn export_config(&self) -> Result<Value> {
430 serde_json::to_value(&self.config).map_err(|e| MetricsError::InvalidInput(e.to_string()))
431 }
432
433 pub fn import_config(&mut self, config_data: Value) -> Result<()> {
435 self.config = serde_json::from_value(config_data)
436 .map_err(|e| MetricsError::InvalidInput(e.to_string()))?;
437 Ok(())
438 }
439}
440
441impl Default for ViewState {
442 fn default() -> Self {
443 Self {
444 mode: ViewMode::Normal,
445 visible_widgets: Vec::new(),
446 z_order: Vec::new(),
447 layout_mode: "grid".to_string(),
448 }
449 }
450}
451
452impl Default for ViewportState {
453 fn default() -> Self {
454 Self {
455 zoom: 1.0,
456 pan_x: 0.0,
457 pan_y: 0.0,
458 bounds: ViewportBounds::default(),
459 }
460 }
461}
462
463impl Default for ViewportBounds {
464 fn default() -> Self {
465 Self {
466 min_x: 0.0,
467 max_x: 1200.0,
468 min_y: 0.0,
469 max_y: 800.0,
470 }
471 }
472}