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
505                .date_naive()
506                .and_hms_opt(0, 0, 0)
507                .expect("0:0:0 is always a valid time")
508                .and_utc();
509            let day_end = day_start + Duration::days(1);
510
511            let day_activities: Vec<_> = self
512                .activity_feed
513                .iter()
514                .filter(|a| a.timestamp >= day_start && a.timestamp < day_end)
515                .collect();
516
517            let daily = DailyActivity {
518                date: day_start,
519                reports: day_activities
520                    .iter()
521                    .filter(|a| matches!(a.event_type, ActivityType::ReportShared))
522                    .count(),
523                annotations: day_activities
524                    .iter()
525                    .filter(|a| matches!(a.event_type, ActivityType::AnnotationAdded))
526                    .count(),
527                comments: day_activities
528                    .iter()
529                    .filter(|a| matches!(a.event_type, ActivityType::CommentPosted))
530                    .count(),
531                active_users: day_activities
532                    .iter()
533                    .map(|a| a.actor)
534                    .collect::<std::collections::HashSet<_>>()
535                    .len(),
536            };
537
538            daily_activity.push(daily);
539        }
540
541        // Calculate weekly summary
542        let total_reports = daily_activity.iter().map(|d| d.reports).sum();
543        let total_annotations = daily_activity.iter().map(|d| d.annotations).sum();
544        let total_comments = daily_activity.iter().map(|d| d.comments).sum();
545        let avg_daily_active_users =
546            daily_activity.iter().map(|d| d.active_users as f64).sum::<f64>() / 7.0;
547
548        let weekly_summary = WeeklyActivity {
549            total_reports,
550            total_annotations,
551            total_comments,
552            peak_day: "Monday".to_string(), // This would be calculated properly
553            avg_daily_active_users,
554        };
555
556        ActivityTrends {
557            daily_activity,
558            weekly_summary,
559            growth_rate: 0.0, // Calculate actual growth rate
560        }
561    }
562
563    fn calculate_collaboration_score(
564        &self,
565        _stats: &crate::collaboration::CollaborationStats,
566    ) -> f64 {
567        // Calculate a collaboration score based on various metrics
568        // This is a simplified calculation
569        50.0 + (self.activity_feed.len() as f64 * 0.1)
570    }
571
572    fn calculate_top_contributors(&self) -> Vec<ContributorMetric> {
573        let mut contributor_map: HashMap<Uuid, (usize, usize, usize)> = HashMap::new();
574
575        for activity in &self.activity_feed {
576            let entry = contributor_map.entry(activity.actor).or_insert((0, 0, 0));
577            match activity.event_type {
578                ActivityType::ReportShared => entry.0 += 1,
579                ActivityType::AnnotationAdded => entry.1 += 1,
580                ActivityType::CommentPosted => entry.2 += 1,
581                _ => {},
582            }
583        }
584
585        contributor_map
586            .into_iter()
587            .map(|(member_id, (reports, annotations, comments))| {
588                let activity_score =
589                    reports as f64 * 3.0 + annotations as f64 * 2.0 + comments as f64;
590                ContributorMetric {
591                    member_id,
592                    name: format!("User {}", member_id.to_string()[0..8].to_uppercase()),
593                    reports_count: reports,
594                    annotations_count: annotations,
595                    comments_count: comments,
596                    activity_score,
597                }
598            })
599            .collect()
600    }
601
602    fn count_today_activities(&self, activity_type: ActivityType) -> usize {
603        let today = Utc::now().date_naive();
604        self.activity_feed
605            .iter()
606            .filter(|a| {
607                a.timestamp.date_naive() == today
608                    && std::mem::discriminant(&a.event_type)
609                        == std::mem::discriminant(&activity_type)
610            })
611            .count()
612    }
613
614    fn calculate_avg_response_time(&self) -> f64 {
615        // Simplified calculation - would need more sophisticated tracking
616        15.0 // 15 minutes average response time
617    }
618
619    fn count_activities_by_type(&self, activities: &[&ActivityEvent]) -> HashMap<String, usize> {
620        let mut counts = HashMap::new();
621        for activity in activities {
622            let type_name = match &activity.event_type {
623                ActivityType::ReportShared => "ReportShared",
624                ActivityType::AnnotationAdded => "AnnotationAdded",
625                ActivityType::CommentPosted => "CommentPosted",
626                ActivityType::SessionStarted => "SessionStarted",
627                ActivityType::Custom(name) => name,
628                _ => "Other",
629            };
630            *counts.entry(type_name.to_string()).or_insert(0) += 1;
631        }
632        counts
633    }
634
635    fn find_most_active_day(&self, activities: &[&ActivityEvent]) -> String {
636        let mut day_counts = HashMap::new();
637        for activity in activities {
638            let day = activity.timestamp.format("%A").to_string();
639            *day_counts.entry(day).or_insert(0) += 1;
640        }
641
642        day_counts
643            .into_iter()
644            .max_by_key(|(_, count)| *count)
645            .map(|(day, _)| day)
646            .unwrap_or_else(|| "Monday".to_string())
647    }
648}
649
650/// Complete dashboard data for rendering
651#[derive(Debug, Clone, Serialize, Deserialize)]
652pub struct DashboardData {
653    pub config: DashboardConfig,
654    pub activity_feed: Vec<ActivityEvent>,
655    pub metrics: TeamMetrics,
656    pub active_sessions: Vec<SessionActivity>,
657    pub notifications: Vec<DashboardNotification>,
658    pub last_updated: DateTime<Utc>,
659}
660
661/// Activity summary for reporting
662#[derive(Debug, Clone, Serialize, Deserialize)]
663pub struct ActivitySummary {
664    pub total_activities: usize,
665    pub unique_contributors: usize,
666    pub activity_by_type: HashMap<String, usize>,
667    pub most_active_day: String,
668    pub period_days: u32,
669}
670
671impl Default for NotificationSystem {
672    fn default() -> Self {
673        Self::new()
674    }
675}
676
677impl NotificationSystem {
678    pub fn new() -> Self {
679        Self {
680            notifications: Vec::new(),
681            settings: NotificationSettings {
682                browser_notifications: true,
683                sound_alerts: false,
684                auto_dismiss_time: 5,
685                max_notifications: 50,
686            },
687        }
688    }
689
690    pub fn send_notification(
691        &mut self,
692        notification_type: NotificationType,
693        title: String,
694        message: String,
695        priority: NotificationPriority,
696        target_users: Vec<Uuid>,
697    ) -> Uuid {
698        let notification_id = Uuid::new_v4();
699        let notification = DashboardNotification {
700            id: notification_id,
701            notification_type,
702            title,
703            message,
704            priority,
705            timestamp: Utc::now(),
706            target_users,
707            read_by: Vec::new(),
708            actions: Vec::new(),
709        };
710
711        self.notifications.insert(0, notification);
712
713        // Limit notifications
714        if self.notifications.len() > self.settings.max_notifications {
715            self.notifications.truncate(self.settings.max_notifications);
716        }
717
718        notification_id
719    }
720
721    pub fn mark_read(&mut self, notification_id: Uuid, user_id: Uuid) -> Result<()> {
722        if let Some(notification) = self.notifications.iter_mut().find(|n| n.id == notification_id)
723        {
724            if !notification.read_by.contains(&user_id) {
725                notification.read_by.push(user_id);
726            }
727            Ok(())
728        } else {
729            Err(anyhow::anyhow!("Notification not found"))
730        }
731    }
732
733    pub fn get_unread_notifications(&self) -> Vec<DashboardNotification> {
734        self.notifications.clone()
735    }
736}
737
738impl Default for TeamMetrics {
739    fn default() -> Self {
740        Self {
741            active_members: 0,
742            reports_today: 0,
743            annotations_today: 0,
744            comments_today: 0,
745            avg_response_time: 0.0,
746            collaboration_score: 0.0,
747            top_contributors: Vec::new(),
748            activity_trends: ActivityTrends {
749                daily_activity: Vec::new(),
750                weekly_summary: WeeklyActivity {
751                    total_reports: 0,
752                    total_annotations: 0,
753                    total_comments: 0,
754                    peak_day: "Monday".to_string(),
755                    avg_daily_active_users: 0.0,
756                },
757                growth_rate: 0.0,
758            },
759        }
760    }
761}
762
763impl Default for DashboardConfig {
764    fn default() -> Self {
765        Self {
766            refresh_interval: 30,
767            max_activities: 100,
768            real_time_updates: true,
769            show_detailed_metrics: true,
770            widgets: vec![
771                WidgetConfig {
772                    id: "activity-feed".to_string(),
773                    widget_type: WidgetType::ActivityFeed,
774                    position: WidgetPosition {
775                        x: 0,
776                        y: 0,
777                        col: 0,
778                        row: 0,
779                    },
780                    size: WidgetSize {
781                        width: 6,
782                        height: 8,
783                        min_width: 4,
784                        min_height: 6,
785                    },
786                    settings: HashMap::new(),
787                    visible: true,
788                },
789                WidgetConfig {
790                    id: "team-metrics".to_string(),
791                    widget_type: WidgetType::TeamMetrics,
792                    position: WidgetPosition {
793                        x: 6,
794                        y: 0,
795                        col: 6,
796                        row: 0,
797                    },
798                    size: WidgetSize {
799                        width: 6,
800                        height: 4,
801                        min_width: 4,
802                        min_height: 3,
803                    },
804                    settings: HashMap::new(),
805                    visible: true,
806                },
807            ],
808            theme: DashboardTheme {
809                primary_color: "#007acc".to_string(),
810                secondary_color: "#6c757d".to_string(),
811                background_color: "#ffffff".to_string(),
812                text_color: "#333333".to_string(),
813                dark_mode: false,
814            },
815        }
816    }
817}
818
819#[cfg(test)]
820mod tests {
821    use super::*;
822
823    #[test]
824    fn test_dashboard_creation() {
825        let config = DashboardConfig::default();
826        let dashboard = TeamDashboard::new(config);
827
828        assert_eq!(dashboard.activity_feed.len(), 0);
829        assert_eq!(dashboard.metrics.active_members, 0);
830    }
831
832    #[test]
833    fn test_activity_event_addition() {
834        let config = DashboardConfig::default();
835        let mut dashboard = TeamDashboard::new(config);
836
837        let actor = Uuid::new_v4();
838        let target = ActivityTarget::Report(Uuid::new_v4());
839
840        let event_id = dashboard.add_activity_event(
841            ActivityType::ReportShared,
842            actor,
843            target,
844            "Shared debugging report".to_string(),
845        );
846
847        assert_eq!(dashboard.activity_feed.len(), 1);
848        assert_eq!(dashboard.activity_feed[0].id, event_id);
849    }
850
851    #[test]
852    fn test_notification_system() {
853        let mut notification_system = NotificationSystem::new();
854
855        let notification_id = notification_system.send_notification(
856            NotificationType::NewReport,
857            "New Report".to_string(),
858            "A new debugging report has been shared".to_string(),
859            NotificationPriority::Normal,
860            vec![Uuid::new_v4()],
861        );
862
863        assert_eq!(notification_system.notifications.len(), 1);
864        assert_eq!(notification_system.notifications[0].id, notification_id);
865    }
866
867    #[test]
868    fn test_activity_summary() {
869        let config = DashboardConfig::default();
870        let mut dashboard = TeamDashboard::new(config);
871
872        // Add some test activities
873        for i in 0..5 {
874            dashboard.add_activity_event(
875                ActivityType::ReportShared,
876                Uuid::new_v4(),
877                ActivityTarget::Report(Uuid::new_v4()),
878                format!("Test activity {}", i),
879            );
880        }
881
882        let summary = dashboard.get_activity_summary(7);
883        assert_eq!(summary.total_activities, 5);
884        assert_eq!(summary.period_days, 7);
885    }
886}