Skip to main content

trustformers_debug/
team_dashboard.rs

1//! Team collaboration dashboard for debugging sessions
2//!
3//! This module provides a real-time dashboard for team collaboration,
4//! showing active debugging sessions, shared reports, and team activity.
5
6use crate::collaboration::CollaborationManager;
7use anyhow::Result;
8use chrono::{DateTime, Duration, Utc};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use uuid::Uuid;
12
13/// Team dashboard for collaborative debugging
14#[derive(Debug, Clone)]
15pub struct TeamDashboard {
16    /// Dashboard configuration
17    config: DashboardConfig,
18    /// Real-time activity feed
19    activity_feed: Vec<ActivityEvent>,
20    /// Team metrics
21    metrics: TeamMetrics,
22    /// Active sessions tracking
23    active_sessions: HashMap<Uuid, SessionActivity>,
24    /// Notification system
25    notifications: NotificationSystem,
26}
27
28/// Dashboard configuration
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct DashboardConfig {
31    /// Refresh interval in seconds
32    pub refresh_interval: u64,
33    /// Maximum activities to show
34    pub max_activities: usize,
35    /// Enable real-time updates
36    pub real_time_updates: bool,
37    /// Show detailed metrics
38    pub show_detailed_metrics: bool,
39    /// Custom widgets configuration
40    pub widgets: Vec<WidgetConfig>,
41    /// Theme settings
42    pub theme: DashboardTheme,
43}
44
45/// Widget configuration for dashboard
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct WidgetConfig {
48    /// Widget identifier
49    pub id: String,
50    /// Widget type
51    pub widget_type: WidgetType,
52    /// Widget position
53    pub position: WidgetPosition,
54    /// Widget size
55    pub size: WidgetSize,
56    /// Widget settings
57    pub settings: HashMap<String, serde_json::Value>,
58    /// Visibility
59    pub visible: bool,
60}
61
62/// Dashboard theme
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct DashboardTheme {
65    /// Primary color
66    pub primary_color: String,
67    /// Secondary color
68    pub secondary_color: String,
69    /// Background color
70    pub background_color: String,
71    /// Text color
72    pub text_color: String,
73    /// Dark mode enabled
74    pub dark_mode: bool,
75}
76
77/// Activity event in the team
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ActivityEvent {
80    /// Event identifier
81    pub id: Uuid,
82    /// Event type
83    pub event_type: ActivityType,
84    /// Actor (team member who performed the action)
85    pub actor: Uuid,
86    /// Target (what was affected)
87    pub target: ActivityTarget,
88    /// Event timestamp
89    pub timestamp: DateTime<Utc>,
90    /// Event description
91    pub description: String,
92    /// Additional metadata
93    pub metadata: HashMap<String, serde_json::Value>,
94}
95
96/// Team metrics for dashboard
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct TeamMetrics {
99    /// Active members count
100    pub active_members: usize,
101    /// Reports shared today
102    pub reports_today: usize,
103    /// Annotations added today
104    pub annotations_today: usize,
105    /// Comments posted today
106    pub comments_today: usize,
107    /// Average response time (in minutes)
108    pub avg_response_time: f64,
109    /// Collaboration score
110    pub collaboration_score: f64,
111    /// Top contributors
112    pub top_contributors: Vec<ContributorMetric>,
113    /// Activity trends
114    pub activity_trends: ActivityTrends,
115}
116
117/// Individual contributor metrics
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct ContributorMetric {
120    /// Team member ID
121    pub member_id: Uuid,
122    /// Member name
123    pub name: String,
124    /// Reports contributed
125    pub reports_count: usize,
126    /// Annotations made
127    pub annotations_count: usize,
128    /// Comments posted
129    pub comments_count: usize,
130    /// Activity score
131    pub activity_score: f64,
132}
133
134/// Activity trends over time
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ActivityTrends {
137    /// Daily activity counts for the last 7 days
138    pub daily_activity: Vec<DailyActivity>,
139    /// Weekly activity summary
140    pub weekly_summary: WeeklyActivity,
141    /// Growth rate percentage
142    pub growth_rate: f64,
143}
144
145/// Daily activity metrics
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct DailyActivity {
148    /// Date
149    pub date: DateTime<Utc>,
150    /// Number of reports
151    pub reports: usize,
152    /// Number of annotations
153    pub annotations: usize,
154    /// Number of comments
155    pub comments: usize,
156    /// Active users
157    pub active_users: usize,
158}
159
160/// Weekly activity summary
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct WeeklyActivity {
163    /// Total reports this week
164    pub total_reports: usize,
165    /// Total annotations this week
166    pub total_annotations: usize,
167    /// Total comments this week
168    pub total_comments: usize,
169    /// Peak activity day
170    pub peak_day: String,
171    /// Average daily active users
172    pub avg_daily_active_users: f64,
173}
174
175/// Session activity tracking
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct SessionActivity {
178    /// Session ID
179    pub session_id: Uuid,
180    /// Session type
181    pub session_type: String,
182    /// Participants
183    pub participants: Vec<Uuid>,
184    /// Start time
185    pub started_at: DateTime<Utc>,
186    /// Last activity time
187    pub last_activity: DateTime<Utc>,
188    /// Activity count
189    pub activity_count: usize,
190    /// Current status
191    pub status: String,
192}
193
194/// Notification system for real-time updates
195#[derive(Debug, Clone)]
196pub struct NotificationSystem {
197    /// Pending notifications
198    notifications: Vec<DashboardNotification>,
199    /// Notification settings
200    settings: NotificationSettings,
201}
202
203/// Dashboard notification
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct DashboardNotification {
206    /// Notification ID
207    pub id: Uuid,
208    /// Notification type
209    pub notification_type: NotificationType,
210    /// Title
211    pub title: String,
212    /// Message
213    pub message: String,
214    /// Priority level
215    pub priority: NotificationPriority,
216    /// Timestamp
217    pub timestamp: DateTime<Utc>,
218    /// Target users
219    pub target_users: Vec<Uuid>,
220    /// Read status
221    pub read_by: Vec<Uuid>,
222    /// Action buttons
223    pub actions: Vec<NotificationAction>,
224}
225
226/// Notification settings
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct NotificationSettings {
229    /// Enable browser notifications
230    pub browser_notifications: bool,
231    /// Enable sound alerts
232    pub sound_alerts: bool,
233    /// Auto-dismiss time (seconds)
234    pub auto_dismiss_time: u64,
235    /// Max notifications to show
236    pub max_notifications: usize,
237}
238
239/// Activity types
240#[derive(Debug, Clone, Serialize, Deserialize)]
241pub enum ActivityType {
242    ReportShared,
243    ReportUpdated,
244    AnnotationAdded,
245    CommentPosted,
246    SessionStarted,
247    SessionEnded,
248    MemberJoined,
249    MemberLeft,
250    IssueResolved,
251    Custom(String),
252}
253
254/// Activity targets
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub enum ActivityTarget {
257    Report(Uuid),
258    Annotation(Uuid),
259    Comment(Uuid),
260    Session(Uuid),
261    Member(Uuid),
262    Custom(String),
263}
264
265/// Widget types
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub enum WidgetType {
268    ActivityFeed,
269    TeamMetrics,
270    ActiveSessions,
271    RecentReports,
272    TopContributors,
273    ActivityChart,
274    NotificationCenter,
275    QuickActions,
276    Custom(String),
277}
278
279/// Widget position
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct WidgetPosition {
282    /// X coordinate
283    pub x: u32,
284    /// Y coordinate
285    pub y: u32,
286    /// Grid column
287    pub col: u32,
288    /// Grid row
289    pub row: u32,
290}
291
292/// Widget size
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct WidgetSize {
295    /// Width in grid units
296    pub width: u32,
297    /// Height in grid units
298    pub height: u32,
299    /// Minimum width
300    pub min_width: u32,
301    /// Minimum height
302    pub min_height: u32,
303}
304
305/// Notification types
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub enum NotificationType {
308    NewReport,
309    NewAnnotation,
310    NewComment,
311    SessionInvite,
312    IssueAssigned,
313    SystemAlert,
314    Custom(String),
315}
316
317/// Notification priority
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub enum NotificationPriority {
320    Low,
321    Normal,
322    High,
323    Urgent,
324}
325
326/// Notification action
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct NotificationAction {
329    /// Action label
330    pub label: String,
331    /// Action type
332    pub action_type: String,
333    /// Action data
334    pub data: HashMap<String, serde_json::Value>,
335}
336
337impl TeamDashboard {
338    /// Create a new team dashboard
339    pub fn new(config: DashboardConfig) -> Self {
340        Self {
341            config,
342            activity_feed: Vec::new(),
343            metrics: TeamMetrics::default(),
344            active_sessions: HashMap::new(),
345            notifications: NotificationSystem::new(),
346        }
347    }
348
349    /// Update dashboard with collaboration data
350    pub fn update_from_collaboration(
351        &mut self,
352        collaboration: &CollaborationManager,
353    ) -> Result<()> {
354        // Update metrics
355        self.update_metrics(collaboration)?;
356
357        // Update activity feed
358        self.update_activity_feed(collaboration)?;
359
360        // Update active sessions
361        self.update_active_sessions(collaboration)?;
362
363        Ok(())
364    }
365
366    /// Add activity event
367    pub fn add_activity_event(
368        &mut self,
369        event_type: ActivityType,
370        actor: Uuid,
371        target: ActivityTarget,
372        description: String,
373    ) -> Uuid {
374        let event_id = Uuid::new_v4();
375        let event = ActivityEvent {
376            id: event_id,
377            event_type,
378            actor,
379            target,
380            timestamp: Utc::now(),
381            description,
382            metadata: HashMap::new(),
383        };
384
385        self.activity_feed.insert(0, event);
386
387        // Limit activity feed size
388        if self.activity_feed.len() > self.config.max_activities {
389            self.activity_feed.truncate(self.config.max_activities);
390        }
391
392        event_id
393    }
394
395    /// Get dashboard data for rendering
396    pub fn get_dashboard_data(&self) -> DashboardData {
397        DashboardData {
398            config: self.config.clone(),
399            activity_feed: self.activity_feed.clone(),
400            metrics: self.metrics.clone(),
401            active_sessions: self.active_sessions.values().cloned().collect(),
402            notifications: self.notifications.get_unread_notifications(),
403            last_updated: Utc::now(),
404        }
405    }
406
407    /// Send notification to team members
408    pub fn send_notification(
409        &mut self,
410        notification_type: NotificationType,
411        title: String,
412        message: String,
413        priority: NotificationPriority,
414        target_users: Vec<Uuid>,
415    ) -> Uuid {
416        self.notifications.send_notification(
417            notification_type,
418            title,
419            message,
420            priority,
421            target_users,
422        )
423    }
424
425    /// Mark notification as read
426    pub fn mark_notification_read(&mut self, notification_id: Uuid, user_id: Uuid) -> Result<()> {
427        self.notifications.mark_read(notification_id, user_id)
428    }
429
430    /// Get team activity summary
431    pub fn get_activity_summary(&self, days: u32) -> ActivitySummary {
432        let cutoff_date = Utc::now() - Duration::days(days as i64);
433
434        let recent_activities: Vec<_> = self
435            .activity_feed
436            .iter()
437            .filter(|activity| activity.timestamp >= cutoff_date)
438            .collect();
439
440        let total_activities = recent_activities.len();
441        let unique_contributors: std::collections::HashSet<_> =
442            recent_activities.iter().map(|a| a.actor).collect();
443
444        ActivitySummary {
445            total_activities,
446            unique_contributors: unique_contributors.len(),
447            activity_by_type: self.count_activities_by_type(&recent_activities),
448            most_active_day: self.find_most_active_day(&recent_activities),
449            period_days: days,
450        }
451    }
452
453    /// Update team metrics
454    fn update_metrics(&mut self, collaboration: &CollaborationManager) -> Result<()> {
455        let stats = collaboration.get_collaboration_stats();
456
457        // Calculate activity trends
458        let trends = self.calculate_activity_trends();
459
460        // Calculate collaboration score
461        let collaboration_score = self.calculate_collaboration_score(&stats);
462
463        self.metrics = TeamMetrics {
464            active_members: stats.team_size,
465            reports_today: self.count_today_activities(ActivityType::ReportShared),
466            annotations_today: self.count_today_activities(ActivityType::AnnotationAdded),
467            comments_today: self.count_today_activities(ActivityType::CommentPosted),
468            avg_response_time: self.calculate_avg_response_time(),
469            collaboration_score,
470            top_contributors: self.calculate_top_contributors(),
471            activity_trends: trends,
472        };
473
474        Ok(())
475    }
476
477    /// Update activity feed from collaboration data
478    fn update_activity_feed(&mut self, _collaboration: &CollaborationManager) -> Result<()> {
479        // This would typically sync with collaboration manager's events
480        // For now, we'll just update the existing feed
481        Ok(())
482    }
483
484    /// Update active sessions tracking
485    fn update_active_sessions(&mut self, _collaboration: &CollaborationManager) -> Result<()> {
486        // Update session activity tracking
487        let now = Utc::now();
488
489        // Remove stale sessions (inactive for more than 1 hour)
490        let stale_cutoff = now - Duration::hours(1);
491        self.active_sessions.retain(|_, session| session.last_activity >= stale_cutoff);
492
493        Ok(())
494    }
495
496    /// Helper methods
497    fn calculate_activity_trends(&self) -> ActivityTrends {
498        let now = Utc::now();
499        let mut daily_activity = Vec::new();
500
501        // Calculate last 7 days
502        for i in 0..7 {
503            let date = now - Duration::days(i);
504            let day_start = date.date_naive().and_hms_opt(0, 0, 0).unwrap().and_utc();
505            let day_end = day_start + Duration::days(1);
506
507            let day_activities: Vec<_> = self
508                .activity_feed
509                .iter()
510                .filter(|a| a.timestamp >= day_start && a.timestamp < day_end)
511                .collect();
512
513            let daily = DailyActivity {
514                date: day_start,
515                reports: day_activities
516                    .iter()
517                    .filter(|a| matches!(a.event_type, ActivityType::ReportShared))
518                    .count(),
519                annotations: day_activities
520                    .iter()
521                    .filter(|a| matches!(a.event_type, ActivityType::AnnotationAdded))
522                    .count(),
523                comments: day_activities
524                    .iter()
525                    .filter(|a| matches!(a.event_type, ActivityType::CommentPosted))
526                    .count(),
527                active_users: day_activities
528                    .iter()
529                    .map(|a| a.actor)
530                    .collect::<std::collections::HashSet<_>>()
531                    .len(),
532            };
533
534            daily_activity.push(daily);
535        }
536
537        // Calculate weekly summary
538        let total_reports = daily_activity.iter().map(|d| d.reports).sum();
539        let total_annotations = daily_activity.iter().map(|d| d.annotations).sum();
540        let total_comments = daily_activity.iter().map(|d| d.comments).sum();
541        let avg_daily_active_users =
542            daily_activity.iter().map(|d| d.active_users as f64).sum::<f64>() / 7.0;
543
544        let weekly_summary = WeeklyActivity {
545            total_reports,
546            total_annotations,
547            total_comments,
548            peak_day: "Monday".to_string(), // This would be calculated properly
549            avg_daily_active_users,
550        };
551
552        ActivityTrends {
553            daily_activity,
554            weekly_summary,
555            growth_rate: 0.0, // Calculate actual growth rate
556        }
557    }
558
559    fn calculate_collaboration_score(
560        &self,
561        _stats: &crate::collaboration::CollaborationStats,
562    ) -> f64 {
563        // Calculate a collaboration score based on various metrics
564        // This is a simplified calculation
565        50.0 + (self.activity_feed.len() as f64 * 0.1)
566    }
567
568    fn calculate_top_contributors(&self) -> Vec<ContributorMetric> {
569        let mut contributor_map: HashMap<Uuid, (usize, usize, usize)> = HashMap::new();
570
571        for activity in &self.activity_feed {
572            let entry = contributor_map.entry(activity.actor).or_insert((0, 0, 0));
573            match activity.event_type {
574                ActivityType::ReportShared => entry.0 += 1,
575                ActivityType::AnnotationAdded => entry.1 += 1,
576                ActivityType::CommentPosted => entry.2 += 1,
577                _ => {},
578            }
579        }
580
581        contributor_map
582            .into_iter()
583            .map(|(member_id, (reports, annotations, comments))| {
584                let activity_score =
585                    reports as f64 * 3.0 + annotations as f64 * 2.0 + comments as f64;
586                ContributorMetric {
587                    member_id,
588                    name: format!("User {}", member_id.to_string()[0..8].to_uppercase()),
589                    reports_count: reports,
590                    annotations_count: annotations,
591                    comments_count: comments,
592                    activity_score,
593                }
594            })
595            .collect()
596    }
597
598    fn count_today_activities(&self, activity_type: ActivityType) -> usize {
599        let today = Utc::now().date_naive();
600        self.activity_feed
601            .iter()
602            .filter(|a| {
603                a.timestamp.date_naive() == today
604                    && std::mem::discriminant(&a.event_type)
605                        == std::mem::discriminant(&activity_type)
606            })
607            .count()
608    }
609
610    fn calculate_avg_response_time(&self) -> f64 {
611        // Simplified calculation - would need more sophisticated tracking
612        15.0 // 15 minutes average response time
613    }
614
615    fn count_activities_by_type(&self, activities: &[&ActivityEvent]) -> HashMap<String, usize> {
616        let mut counts = HashMap::new();
617        for activity in activities {
618            let type_name = match &activity.event_type {
619                ActivityType::ReportShared => "ReportShared",
620                ActivityType::AnnotationAdded => "AnnotationAdded",
621                ActivityType::CommentPosted => "CommentPosted",
622                ActivityType::SessionStarted => "SessionStarted",
623                ActivityType::Custom(name) => name,
624                _ => "Other",
625            };
626            *counts.entry(type_name.to_string()).or_insert(0) += 1;
627        }
628        counts
629    }
630
631    fn find_most_active_day(&self, activities: &[&ActivityEvent]) -> String {
632        let mut day_counts = HashMap::new();
633        for activity in activities {
634            let day = activity.timestamp.format("%A").to_string();
635            *day_counts.entry(day).or_insert(0) += 1;
636        }
637
638        day_counts
639            .into_iter()
640            .max_by_key(|(_, count)| *count)
641            .map(|(day, _)| day)
642            .unwrap_or_else(|| "Monday".to_string())
643    }
644}
645
646/// Complete dashboard data for rendering
647#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct DashboardData {
649    pub config: DashboardConfig,
650    pub activity_feed: Vec<ActivityEvent>,
651    pub metrics: TeamMetrics,
652    pub active_sessions: Vec<SessionActivity>,
653    pub notifications: Vec<DashboardNotification>,
654    pub last_updated: DateTime<Utc>,
655}
656
657/// Activity summary for reporting
658#[derive(Debug, Clone, Serialize, Deserialize)]
659pub struct ActivitySummary {
660    pub total_activities: usize,
661    pub unique_contributors: usize,
662    pub activity_by_type: HashMap<String, usize>,
663    pub most_active_day: String,
664    pub period_days: u32,
665}
666
667impl Default for NotificationSystem {
668    fn default() -> Self {
669        Self::new()
670    }
671}
672
673impl NotificationSystem {
674    pub fn new() -> Self {
675        Self {
676            notifications: Vec::new(),
677            settings: NotificationSettings {
678                browser_notifications: true,
679                sound_alerts: false,
680                auto_dismiss_time: 5,
681                max_notifications: 50,
682            },
683        }
684    }
685
686    pub fn send_notification(
687        &mut self,
688        notification_type: NotificationType,
689        title: String,
690        message: String,
691        priority: NotificationPriority,
692        target_users: Vec<Uuid>,
693    ) -> Uuid {
694        let notification_id = Uuid::new_v4();
695        let notification = DashboardNotification {
696            id: notification_id,
697            notification_type,
698            title,
699            message,
700            priority,
701            timestamp: Utc::now(),
702            target_users,
703            read_by: Vec::new(),
704            actions: Vec::new(),
705        };
706
707        self.notifications.insert(0, notification);
708
709        // Limit notifications
710        if self.notifications.len() > self.settings.max_notifications {
711            self.notifications.truncate(self.settings.max_notifications);
712        }
713
714        notification_id
715    }
716
717    pub fn mark_read(&mut self, notification_id: Uuid, user_id: Uuid) -> Result<()> {
718        if let Some(notification) = self.notifications.iter_mut().find(|n| n.id == notification_id)
719        {
720            if !notification.read_by.contains(&user_id) {
721                notification.read_by.push(user_id);
722            }
723            Ok(())
724        } else {
725            Err(anyhow::anyhow!("Notification not found"))
726        }
727    }
728
729    pub fn get_unread_notifications(&self) -> Vec<DashboardNotification> {
730        self.notifications.clone()
731    }
732}
733
734impl Default for TeamMetrics {
735    fn default() -> Self {
736        Self {
737            active_members: 0,
738            reports_today: 0,
739            annotations_today: 0,
740            comments_today: 0,
741            avg_response_time: 0.0,
742            collaboration_score: 0.0,
743            top_contributors: Vec::new(),
744            activity_trends: ActivityTrends {
745                daily_activity: Vec::new(),
746                weekly_summary: WeeklyActivity {
747                    total_reports: 0,
748                    total_annotations: 0,
749                    total_comments: 0,
750                    peak_day: "Monday".to_string(),
751                    avg_daily_active_users: 0.0,
752                },
753                growth_rate: 0.0,
754            },
755        }
756    }
757}
758
759impl Default for DashboardConfig {
760    fn default() -> Self {
761        Self {
762            refresh_interval: 30,
763            max_activities: 100,
764            real_time_updates: true,
765            show_detailed_metrics: true,
766            widgets: vec![
767                WidgetConfig {
768                    id: "activity-feed".to_string(),
769                    widget_type: WidgetType::ActivityFeed,
770                    position: WidgetPosition {
771                        x: 0,
772                        y: 0,
773                        col: 0,
774                        row: 0,
775                    },
776                    size: WidgetSize {
777                        width: 6,
778                        height: 8,
779                        min_width: 4,
780                        min_height: 6,
781                    },
782                    settings: HashMap::new(),
783                    visible: true,
784                },
785                WidgetConfig {
786                    id: "team-metrics".to_string(),
787                    widget_type: WidgetType::TeamMetrics,
788                    position: WidgetPosition {
789                        x: 6,
790                        y: 0,
791                        col: 6,
792                        row: 0,
793                    },
794                    size: WidgetSize {
795                        width: 6,
796                        height: 4,
797                        min_width: 4,
798                        min_height: 3,
799                    },
800                    settings: HashMap::new(),
801                    visible: true,
802                },
803            ],
804            theme: DashboardTheme {
805                primary_color: "#007acc".to_string(),
806                secondary_color: "#6c757d".to_string(),
807                background_color: "#ffffff".to_string(),
808                text_color: "#333333".to_string(),
809                dark_mode: false,
810            },
811        }
812    }
813}
814
815#[cfg(test)]
816mod tests {
817    use super::*;
818
819    #[test]
820    fn test_dashboard_creation() {
821        let config = DashboardConfig::default();
822        let dashboard = TeamDashboard::new(config);
823
824        assert_eq!(dashboard.activity_feed.len(), 0);
825        assert_eq!(dashboard.metrics.active_members, 0);
826    }
827
828    #[test]
829    fn test_activity_event_addition() {
830        let config = DashboardConfig::default();
831        let mut dashboard = TeamDashboard::new(config);
832
833        let actor = Uuid::new_v4();
834        let target = ActivityTarget::Report(Uuid::new_v4());
835
836        let event_id = dashboard.add_activity_event(
837            ActivityType::ReportShared,
838            actor,
839            target,
840            "Shared debugging report".to_string(),
841        );
842
843        assert_eq!(dashboard.activity_feed.len(), 1);
844        assert_eq!(dashboard.activity_feed[0].id, event_id);
845    }
846
847    #[test]
848    fn test_notification_system() {
849        let mut notification_system = NotificationSystem::new();
850
851        let notification_id = notification_system.send_notification(
852            NotificationType::NewReport,
853            "New Report".to_string(),
854            "A new debugging report has been shared".to_string(),
855            NotificationPriority::Normal,
856            vec![Uuid::new_v4()],
857        );
858
859        assert_eq!(notification_system.notifications.len(), 1);
860        assert_eq!(notification_system.notifications[0].id, notification_id);
861    }
862
863    #[test]
864    fn test_activity_summary() {
865        let config = DashboardConfig::default();
866        let mut dashboard = TeamDashboard::new(config);
867
868        // Add some test activities
869        for i in 0..5 {
870            dashboard.add_activity_event(
871                ActivityType::ReportShared,
872                Uuid::new_v4(),
873                ActivityTarget::Report(Uuid::new_v4()),
874                format!("Test activity {}", i),
875            );
876        }
877
878        let summary = dashboard.get_activity_summary(7);
879        assert_eq!(summary.total_activities, 5);
880        assert_eq!(summary.period_days, 7);
881    }
882}