Overview

The Operational Dashboard is a comprehensive monitoring and analytics tool within the Nimbly audit admin system. It provides real-time insights into operational performance through key performance indicators (KPIs), trend analysis, and detailed reporting capabilities. The dashboard is designed to help managers and executives track issue resolution rates, report completion rates, and identify top-performing entities across sites, departments, and users.

Core Integrations:

Purpose and Goals

  • Real-time Monitoring: Track operational metrics in real-time
  • Performance Analysis: Identify top performers and bottlenecks
  • Trend Visualization: Understand patterns over time
  • Data-Driven Decisions: Enable informed decision-making through comprehensive analytics
  • Multi-level Insights: Provide insights at site, department, and user levels

Key Metrics Tracked

  1. Issue Resolution Rate (IRR): Percentage of issues resolved within defined timeframes
  2. Report Completion Rate (RCR): Percentage of completed reports vs. expected reports
  3. Issue Volume: Total number of issues created and resolved
  4. Report Volume: Total number of reports submitted
  5. Performance Trends: Time-series analysis of all metrics

Architecture

The Operational Dashboard follows a modern React-Redux-Saga architecture with TypeScript for type safety and improved developer experience.

graph TB
    subgraph "Presentation Layer"
        A[OperationalPage Component]
        B[KpiOverview]
        C[TopChart]
        D[Trends]
        E[Summary Components]
        F[DashboardFilters]
    end
    
    subgraph "State Management"
        G[Redux Store]
        H[Actions]
        I[Reducers]
        J[Sagas]
    end
    
    subgraph "Service Layer"
        K[operationalKPI.service]
        L[API Endpoints]
    end
    
    subgraph "Data Sources"
        M[Firebase]
        N[Backend APIs]
    end
    
    A --> B
    A --> C
    A --> D
    A --> E
    A --> F
    
    B --> H
    C --> H
    D --> H
    E --> H
    F --> H
    
    H --> J
    J --> K
    K --> L
    L --> N
    
    J --> I
    I --> G
    G --> A
    
    M --> A

Component Hierarchy

graph TD
    A[Layout] --> B[AdminPage]
    B --> C[PageWrapper]
    C --> D[OperationalPage]
    
    D --> E[OperationalPageHeader]
    D --> F[DashboardFilters]
    D --> G[Content Container]
    
    G --> H[KpiOverview]
    G --> I[Summary Section]
    G --> J[Charts Section]
    
    I --> K[SummaryDesktop]
    I --> L[SummaryMobile]
    
    J --> M[TopChart]
    J --> N[Trends]
    
    K --> O[IssueSummary]
    K --> P[ReportSummary]
    
    L --> Q[IssueSummary Mobile]
    L --> R[ReportSummary Mobile]

Routes and Navigation

Route Configuration

The operational dashboard is accessible through the following routes defined in src/routes/admin-routes.js:

Route PathComponentAccess ControlPurpose
/analytics/operationalOperationalDashboardPageRole-basedMain operational dashboard view
/analytics/operational/issue-insightsOperationalDashboardPageRole-basedIssue-focused insights view
/analytics/operational/[report](../Reports/ReportsOverview.md)-insightsOperationalDashboardPageRole-basedReport-focused insights view
// Route definition from admin-routes.js:768-778
{
  path: '/analytics/operational',
  component: () => (
    <AccessControl roles={roles} setPath={setPath}>
      <OperationalDashboardPage />
    </AccessControl>
  ),
}

Access Control Implementation

The dashboard implements role-based access control:

// AccessControl component wrapper
const AccessControl = ({ roles, setPath, children }) => {
  const userRole = useSelector(state => state.user.role);
  const hasAccess = roles.includes(userRole);
  
  useEffect(() => {
    if (!hasAccess) {
      history.push('/unauthorized');
    }
  }, [hasAccess]);
  
  return hasAccess ? children : <LoadingSpinner />;
};
 
// Allowed roles configuration
const OPERATIONAL_DASHBOARD_ROLES = [
  'SUPERADMIN',
  'ADMIN',
  'MANAGER',
  'ANALYST'
];

Deep Linking Support

The dashboard supports deep linking for specific views:

// URL parameter handling
const useDeepLinking = () => {
  const location = useLocation();
  const dispatch = useDispatch();
  
  useEffect(() => {
    const params = new URLSearchParams(location.search);
    
    // Apply filters from URL
    if (params.has('startDate') && params.has('endDate')) {
      dispatch(setFilters({
        startDate: new Date(params.get('startDate')),
        endDate: new Date(params.get('endDate'))
      }));
    }
    
    // Set view from URL
    if (params.has('view')) {
      const view = params.get('view');
      if (['issues', 'reports'].includes(view)) {
        dispatch(setActiveTab(view));
      }
    }
  }, [location]);
};

The dashboard implements a breadcrumb navigation system for easy navigation:

const BREADCRUMBS = [
  {
    label: t('label.dashboardRevamp.dashboard'),
    link: '/analytics/executive',
  },
  {
    label: t('label.dashboardRevamp.operationalKPIDashboard'),
  },
];
 
// Breadcrumb component implementation
const Breadcrumb = ({ items }) => {
  return (
    <nav className="aa-flex aa-items-center aa-space-x-2">
      {items.map((item, index) => (
        <React.Fragment key={index}>
          {index > 0 && <span className="aa-text-gray-400">/</span>}
          {item.link ? (
            <Link 
              to={item.link} 
              className="aa-text-blue-600 hover:aa-underline"
            >
              {item.label}
            </Link>
          ) : (
            <span className="aa-text-gray-700">{item.label}</span>
          )}
        </React.Fragment>
      ))}
    </nav>
  );
};

Core Components

1. OperationalPage Component (src/pages/dashboardRevamp/operationalPage/OperationalPage.tsx)

The main orchestrator component that manages the overall dashboard layout and data flow.

Key Responsibilities:

  • Initialize and manage filter state
  • Coordinate data fetching across child components
  • Handle responsive layout (mobile/desktop)
  • Manage loading states and error handling
  • Persist and restore filter configurations

Key Features:

  • Auto-refresh capability with timestamp display
  • Filter management with save/load functionality
  • Initial loading indicator for large datasets
  • Responsive design with mobile-specific optimizations

2. KpiOverview Component

Displays circular progress indicators for key performance metrics.

Metrics Displayed:

  • Issue Resolution Rate (IRR)
  • Report Completion Rate (RCR)
  • Visual indicators with percentage displays
  • Color-coded performance levels (green/yellow/red)

Calculation Logic:

// IRR Calculation
IRR = (resolvedIssues / totalIssues) * 100
 
// RCR Calculation
RCR = (completedReports / expectedReports) * 100

3. TopChart Component

Displays top-performing entities based on selected metrics.

Features:

  • View by: Sites, Departments, Users
  • Metrics: Issues Resolved, Reports Submitted
  • Interactive column charts
  • Drill-down capability for detailed views
  • Export functionality (CSV, PDF, XLSX)

Data Structure:

  • Top 5 entities by selected metric
  • Percentage contribution to total
  • Comparative visualization

Time-series visualization of operational metrics.

Configuration Options:

  • Period: Daily, Weekly, Monthly
  • Metrics: Issues, Reports
  • Type: Created vs Resolved, Submitted vs Expected
  • Interactive line/area charts

5. Summary Components

IssueSummary:

  • Total issues created
  • Issues resolved
  • Resolution rate
  • Period-over-period comparison

ReportSummary:

  • Total reports submitted
  • Expected reports
  • Completion rate
  • Period-over-period comparison

6. KpiOverview Component Deep Dive

The KpiOverview component provides visual representation of two critical operational metrics through interactive ring charts.

Visual Design

  • Layout: Two ring charts displayed side-by-side (desktop) or stacked (mobile)
  • Colors:
    • IRR: Purple (#7e78ea)
    • RCR: Blue (#0D6EFD)
  • Interactivity: Drill-down capability for detailed insights

Data Processing

// Display value formatting
const getDisplayDecimalNumber = (value: number, digits = 2): string => {
  if (value % 1 === 0) return value.toString();
  return value.toFixed(digits);
};
 
// Ring chart visualization
const RingChart = ({ value, variant }) => {
  const gradientColor = variant === 'Purple' ? '#7e78ea' : '#0D6EFD';
  return (
    <div style={{
      background: `radial-gradient(closest-side, white 82%, transparent 80% 100%), 
                   conic-gradient(${gradientColor} ${value}%, #f2f1f1 0)`
    }}>
      {value}%
    </div>
  );
};

Drill-Down Modal Features

The drill-down functionality provides multi-dimensional analysis:

Grouping Options (GroupByEnum):

  • Site
  • Department
  • User
  • Issue Category
  • Questionnaire

Drill-By Fields (DrillByEnum):

  • Date Range
  • Completion Status
  • Priority Level
  • Response Time

Export Capabilities:

  • PDF: Formatted reports with charts
  • CSV: Raw data export
  • XLSX: Structured spreadsheet with multiple sheets

State Management

The operational dashboard uses Redux for state management with Redux-Saga for handling asynchronous operations.

Redux Store Structure

interface OperationalState {
  // Loading states
  isLoading: boolean;
  isTopChartLoading: boolean;
  isTrendsLoading: boolean;
  isOverviewLoading: boolean;
  isDrillModalLoading: boolean;
  
  // Filter state
  filters: OperationalFilters;
  savedFilters: OperationalFilters | null;
  
  // Overview data
  issueOverviewDetailData: {
    irr: number;
    resolvedIssues: number;
    openIssuesCount: number;
    openIssuesPercentage: number;
    totalIssues: number;
    issueOpenLastWeekPercentage: number;
    avgIssueResolutionTime: string;
  };
  
  completeReportDetailData: {
    rcr: number;
    totalReports: number;
    expectedReports: number;
    completionPercentage: number;
  };
  
  // Chart data
  topChartData: {
    sites: TopChartItem[];
    departments: TopChartItem[];
    users: TopChartItem[];
    activeTab: 'issues' | 'reports';
    viewBy: 'sites' | 'departments' | 'users';
  };
  
  trendsData: {
    issues: {
      created: TrendDataPoint[];
      resolved: TrendDataPoint[];
    };
    reports: {
      submitted: TrendDataPoint[];
      expected: TrendDataPoint[];
    };
    period: 'daily' | 'weekly' | 'monthly';
    type: 'count' | 'percentage';
  };
  
  // Drill modal data
  drillModalData: {
    isOpen: boolean;
    data: any[];
    totalCount: number;
    page: number;
    groupBy: GroupByEnum;
    drillBy: DrillByEnum[];
  };
}

Filter Structure

interface OperationalFilters {
  startDate: Date;
  endDate: Date;
  sites: string[];
  departments: string[];
  users: string[];
  questionnaires: string[];
  issueCategories: string[];
  reportTypes: string[];
  priorityLevels: string[];
  completionStatus: 'all' | 'completed' | 'pending';
}

Key Actions

// Filter management
setFilters(filters: Partial<OperationalFilters>)
resetFilters()
saveFiltersAsync.request(filters: OperationalFilters)
loadSavedFiltersAsync.request()
 
// Data fetching - Overview
fetchIssueOverviewDetailAsync.request()
fetchCompleteReportDetailAsync.request()
 
// Data fetching - Charts
fetchTopChartDataAsync.request({
  tab: 'issues' | 'reports',
  groupBy: 'sites' | 'departments' | 'users'
})
 
fetchTrendsDataAsync.request({
  period: 'daily' | 'weekly' | 'monthly',
  type: 'count' | 'percentage'
})
 
// Drill modal
openDrillModal(params: DrillModalParams)
closeDrillModal()
fetchDrillDataAsync.request(params: DrillDataParams)
 
// UI state
setTopChartActiveTab(tab: string)
setTopChartViewBy(viewBy: string)
setTrendsPeriod(period: string)
setTrendsType(type: string)

Saga Flow Example

sequenceDiagram
    participant User
    participant Component
    participant Action
    participant Saga
    participant Service
    participant API
    participant Store
    
    User->>Component: Click "Apply Filters"
    Component->>Action: setFilters(filters)
    Action->>Store: Update filters in state
    Component->>Action: fetchIssueOverviewDetailAsync.request()
    Action->>Saga: Trigger fetchIssueOverviewSaga
    Saga->>Store: Get current filters
    Saga->>Service: fetchIssueOverviewDetail(filters)
    Service->>API: POST /statistics/dashboard/issue/detail
    API-->>Service: { irr: 85.5, ... }
    Service-->>Saga: Processed data
    Saga->>Action: fetchIssueOverviewDetailAsync.success(data)
    Action->>Store: Update issueOverviewDetailData
    Store-->>Component: Re-render with new IRR value

API Integration

Service Layer Architecture

The operational dashboard utilizes a well-structured service layer (src/services/dashboardRevamp/[reports](../Reports/ReportsOverview.md)/operationalKPI.service.ts) that handles all API communications.

Service Implementation Details

// operationalKPI.service.ts implementation
import { apiClient } from 'helpers/api';
import { OperationalFilters } from 'reducers/dashboardRevamp/operational/operationalStore';
 
class OperationalKPIService {
  private baseURL = '/statistics/dashboard';
  
  // Transform filters for API compatibility
  private transformFilters(filters: OperationalFilters) {
    return {
      startDate: filters.startDate.toISOString(),
      endDate: filters.endDate.toISOString(),
      siteIds: filters.sites,
      departmentIds: filters.departments,
      userIds: filters.users,
      questionnaireIds: filters.questionnaires,
      issueCategoryIds: filters.issueCategories
    };
  }
  
  // Fetch issue overview with retry logic
  async fetchIssueOverviewDetail(filters: OperationalFilters) {
    try {
      const response = await apiClient.post(
        `${this.baseURL}/issue/detail`,
        this.transformFilters(filters),
        {
          timeout: 30000, // 30 second timeout
          retry: 3,
          retryDelay: 1000
        }
      );
      
      return this.processIssueData(response.data);
    } catch (error) {
      console.error('Failed to fetch issue overview:', error);
      throw new Error('Unable to load issue metrics');
    }
  }
  
  // Process and calculate additional metrics
  private processIssueData(data: any) {
    const { totalIssues, resolvedIssues, openIssues } = data;
    
    return {
      ...data,
      irr: totalIssues > 0 ? (resolvedIssues / totalIssues) * 100 : 0,
      openIssuesPercentage: totalIssues > 0 ? (openIssues / totalIssues) * 100 : 0,
      avgResolutionTime: this.calculateAvgResolutionTime(data.resolutionTimes)
    };
  }
  
  // Batch multiple API calls for efficiency
  async fetchDashboardData(filters: OperationalFilters) {
    const [issueData, reportData, topChart, trends] = await Promise.all([
      this.fetchIssueOverviewDetail(filters),
      this.fetchReportDetail(filters),
      this.fetchTopList({ ...filters, groupBy: 'site', tab: 'issues' }),
      this.fetchTrendsData({ ...filters, period: 'daily' })
    ]);
    
    return {
      issueOverview: issueData,
      reportOverview: reportData,
      topPerformers: topChart,
      trendData: trends
    };
  }
}
 
export const operationalKPIService = new OperationalKPIService();

Error Handling and Resilience

// API error interceptor
apiClient.interceptors.response.use(
  response => response,
  error => {
    // Handle specific error scenarios
    if (error.response?.status === 401) {
      // Redirect to login
      window.location.href = '/login';
    } else if (error.response?.status === 403) {
      // Show permission denied message
      toast.error('You do not have permission to view this data');
    } else if (error.response?.status >= 500) {
      // Server error - show friendly message
      toast.error('Server is experiencing issues. Please try again later.');
    }
    
    return Promise.reject(error);
  }
);

API Endpoints

EndpointMethodPurposeRequest PayloadResponse Type
/statistics/dashboard/issue/detailPOSTFetch issue overview metrics{ startDate, endDate, filters }IssueOverviewDetail
/statistics/dashboard/[report](../Reports/ReportsOverview.md)/detailPOSTFetch report completion metrics{ startDate, endDate, filters }GetCompleteReportDetail
/statistics/dashboard/issue/chartPOSTGet issue chart data{ period, filters }ChartData[]
/statistics/dashboard/[report](../Reports/ReportsOverview.md)/chartPOSTGet report chart data{ period, filters }ChartData[]
/statistics/dashboard/top-listPOSTFetch top performers{ groupBy, tab, filters }TopListItem[]
/statistics/dashboard/trendsPOSTGet trends data{ period, type, filters }TrendData[]
/statistics/dashboard/drillPOSTDrill-down data{ groupBy, drillBy, page, filters }DrillData
/statistics/dashboard/download/issuePOSTDownload issue data{ format, columns, filters }File
/statistics/dashboard/download/[report](../Reports/ReportsOverview.md)POSTDownload report data{ format, columns, filters }File
/user/filters/savePOSTSave filter preferences{ type, filters }SavedFilters
/user/filters/loadGETLoad saved filters{ type }SavedFilters

Data Processing Pipeline

graph LR
    A[Raw API Response] --> B[Service Layer Processing]
    B --> C[Data Transformation]
    C --> D[Redux Action Payload]
    D --> E[Store Update]
    E --> F[Component Props]
    F --> G[UI Rendering]
    
    B --> H[Error Handling]
    H --> I[Toast Notification]

Data Flow

Component Data Flow

graph TD
    A[OperationalPage] -->|Initializes| B[useOperationalPageData Hook]
    B -->|Manages| C[Local State]
    B -->|Dispatches| D[Redux Actions]
    
    D --> E[Redux Store]
    E -->|Provides Data| F[Child Components]
    
    F --> G[KpiOverview]
    F --> H[TopChart]
    F --> I[Trends]
    F --> J[Summary Components]
    
    K[User Interactions] -->|Trigger| L[Event Handlers]
    L -->|Update| C
    L -->|Dispatch| D

Filter Flow

stateDiagram-v2
    [*] --> DefaultFilters: Page Load
    DefaultFilters --> CheckSavedFilters: Component Mount
    CheckSavedFilters --> LoadSavedFilters: Found
    CheckSavedFilters --> UseDefaults: Not Found
    
    LoadSavedFilters --> ActiveFilters
    UseDefaults --> ActiveFilters
    
    ActiveFilters --> FilterModal: Open Filters
    FilterModal --> ModifiedFilters: User Changes
    ModifiedFilters --> ApplyFilters: Apply
    ModifiedFilters --> SaveFilters: Save
    
    ApplyFilters --> ActiveFilters: Update State
    SaveFilters --> ServerStorage: Persist
    ServerStorage --> ActiveFilters: Confirmation

Filters and Customization

Filter Categories

The operational dashboard provides comprehensive filtering capabilities:

  1. Date Range Filters

    • Preset ranges: Last 7/30/90 days, This month, Last month
    • Custom date range picker
    • Validation for max date range (365 days)
  2. Entity Filters

    • Sites: Multi-select with search
    • Departments: Hierarchical selection
    • Users: Role-based filtering
    • Questionnaires: Type-based grouping
  3. Performance Filters

    • Issue Categories: Predefined categories
    • Report Types: Standard vs Custom
    • Priority Levels: High, Medium, Low
    • Completion Status: All, Completed, Pending

Filter Implementation

// Filter component structure
const DashboardFilters = ({ handleFilterOnApply, handleSaveFilters, handleResetFilter }) => {
  const [localFilters, setLocalFilters] = useState<OperationalFilters>(defaultFilters);
  
  // Multi-select components for entities
  const renderSiteFilter = () => (
    <MultiSelect
      options={siteOptions}
      value={localFilters.sites}
      onChange={(sites) => updateFilter('sites', sites)}
      placeholder="Select sites..."
    />
  );
  
  // Date range picker
  const renderDateFilter = () => (
    <DateRangePicker
      startDate={localFilters.startDate}
      endDate={localFilters.endDate}
      onChange={({ startDate, endDate }) => {
        updateFilter('startDate', startDate);
        updateFilter('endDate', endDate);
      }}
      maxRange={365}
    />
  );
};

Filter Persistence

Filters can be saved to user preferences:

// Save filters to server
const saveFilters = async (filters: OperationalFilters) => {
  const response = await fetchSaveFilters('kpi', {
    kpiDashboardFilters: filters
  });
  return response;
};
 
// Load saved filters on mount
const loadSavedFilters = async () => {
  const { kpiDashboardFilters } = await fetchSavedFilters('kpi');
  if (kpiDashboardFilters) {
    dispatch(setFilters(kpiDashboardFilters));
  }
};

Advanced Features

1. TopChart Component Deep Dive

The TopChart component provides interactive visualization of top-performing entities.

Chart Technology

  • Library: Chart.js v3 with react-chartjs-2 wrapper
  • Chart Type: Vertical bar chart with custom styling
  • Plugin: Custom barValuePlugin for displaying values on bars

Data Visualization

// Chart configuration
const chartOptions = {
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: { display: false },
    tooltip: {
      callbacks: {
        label: (context) => `${context.parsed.y}%`
      }
    },
    barValuePlugin: {
      font: { size: 12, weight: 'bold' },
      color: '#666'
    }
  },
  scales: {
    y: {
      beginAtZero: true,
      max: 100,
      ticks: {
        callback: (value) => `${value}%`
      }
    }
  }
};
 
// Data structure
interface TopChartData {
  labels: string[];  // Entity names
  datasets: [{
    data: number[];  // Performance percentages
    backgroundColor: string[];  // Bar colors
  }]
}

View Options

enum ViewByOptions {
  SITE = 'site',
  DEPARTMENT = 'department',
  USER = 'user',
  QUESTIONNAIRE = 'questionnaire',
  CATEGORY = 'category'  // Issues only
}

Export Configuration

The component uses dynamic column configuration based on view and tab:

// downloadConfigs.ts structure
export const TOP_CHART_DOWNLOAD_CONFIG = {
  issues: {
    site: ['siteName', 'irr', 'totalIssues', 'resolvedIssues'],
    department: ['departmentName', 'irr', 'totalIssues', 'resolvedIssues'],
    user: ['userName', 'role', 'irr', 'totalIssues', 'resolvedIssues']
  },
  reports: {
    site: ['siteName', 'rcr', 'totalReports', 'completedReports'],
    department: ['departmentName', 'rcr', 'totalReports', 'completedReports'],
    user: ['userName', 'role', 'rcr', 'totalReports', 'completedReports']
  }
};

View Details Modal

Provides expanded view with:

  • Paginated data table
  • Column selection for export
  • Format selection (PDF/CSV/XLSX)
  • Real-time download progress
  • Error handling with retry

The Trends component visualizes temporal patterns in operational metrics through interactive line charts.

Chart Technology

  • Library: Chart.js v3 with react-chartjs-2
  • Chart Type: Multi-line chart with legend
  • Responsive: Auto-resizing based on container

Time Period Options

enum DashboardPeriodEnum {
  DAILY = 'daily',
  WEEKLY = 'weekly',
  MONTHLY = 'monthly',
  QUARTERLY = 'quarterly',
  YEARLY = 'yearly'
}

Data Visualization Modes

1. By Status Mode (Issues) Shows breakdown by issue status:

interface IssueStatusLines {
  open: { data: number[], color: '#7E78EA' },      // Purple
  inProgress: { data: number[], color: '#F4BE4F' }, // Yellow
  inReview: { data: number[], color: '#61B3B1' },   // Cyan
  resolved: { data: number[], color: '#4FB06D' },   // Green
  blocked: { data: number[], color: '#D85040' }     // Red
}

2. Cumulative Mode Shows total counts over time:

interface CumulativeData {
  total: { data: number[], color: '#7E78EA' }  // Purple
}

Data Aggregation Logic

// Period-based aggregation
const aggregateByPeriod = (data: RawData[], period: DashboardPeriodEnum) => {
  switch(period) {
    case 'daily':
      return groupByDay(data);
    case 'weekly':
      return groupByWeek(data);
    case 'monthly':
      return groupByMonth(data);
    case 'quarterly':
      return groupByQuarter(data);
    case 'yearly':
      return groupByYear(data);
  }
};

Chart Configuration

const chartOptions = {
  responsive: true,
  maintainAspectRatio: false,
  interaction: {
    mode: 'index',
    intersect: false
  },
  plugins: {
    legend: {
      position: 'bottom',
      labels: {
        usePointStyle: true,
        padding: 20
      }
    },
    tooltip: {
      mode: 'index',
      intersect: false,
      callbacks: {
        label: (context) => `${context.dataset.label}: ${context.parsed.y}`
      }
    }
  },
  scales: {
    x: {
      grid: { display: false }
    },
    y: {
      beginAtZero: true,
      ticks: {
        precision: 0
      }
    }
  }
};

Performance Optimizations

  • Memoized chart data calculation
  • Debounced period changes
  • Lazy loading of chart library
  • Virtual scrolling for large datasets in modal

3. Summary Components Deep Dive

The dashboard includes specialized summary components for Issues and Reports, with separate layouts for desktop and mobile.

IssueSummary Component Implementation

// IssueSummary.tsx
import React from 'react';
import { useSelector } from 'react-redux';
import { RootState } from 'store/rootReducers';
import { DetailedNumberIndicator } from 'components/dashboardRevamp/DetailedNumberIndicator';
import { formatNumber, calculatePercentageChange } from 'utils/metrics';
 
interface IssueSummaryData {
  totalIssues: number;
  resolvedIssues: number;
  openIssues: number;
  avgResolutionTime: string;
  periodComparison: {
    totalChange: number;      // Percentage change
    resolvedChange: number;
    trend: 'up' | 'down' | 'neutral';
  };
}
 
export const IssueSummary: React.FC = () => {
  const { 
    issueOverviewDetailData,
    isLoading 
  } = useSelector((state: RootState) => state.dashboardOperational);
  
  const {
    totalIssues = 0,
    resolvedIssues = 0,
    openIssuesCount = 0,
    avgIssueResolutionTime = 'N/A',
    issueOpenLastWeekPercentage = 0
  } = issueOverviewDetailData || {};
  
  const metrics = [
    {
      title: 'Total Issues',
      value: formatNumber(totalIssues),
      subtitle: 'Created this period',
      change: issueOpenLastWeekPercentage,
      icon: 'issues-icon'
    },
    {
      title: 'Resolved Issues',
      value: formatNumber(resolvedIssues),
      subtitle: `${((resolvedIssues / totalIssues) * 100).toFixed(1)}% resolution rate`,
      change: calculatePercentageChange(resolvedIssues, previousResolvedIssues),
      icon: 'check-icon'
    },
    {
      title: 'Open Issues',
      value: formatNumber(openIssuesCount),
      subtitle: 'Requires attention',
      change: -openIssuesCount, // Negative is good for open issues
      icon: 'warning-icon'
    },
    {
      title: 'Avg Resolution Time',
      value: avgIssueResolutionTime,
      subtitle: 'Days to resolve',
      change: 0,
      icon: 'clock-icon'
    }
  ];
  
  return (
    <div className="aa-grid aa-grid-cols-2 aa-gap-4">
      {metrics.map((metric, index) => (
        <DetailedNumberIndicator
          key={index}
          title={metric.title}
          value={metric.value}
          subtitle={metric.subtitle}
          change={metric.change}
          icon={metric.icon}
          isLoading={isLoading}
        />
      ))}
    </div>
  );
};
 
// Animated number component
const AnimatedNumber: React.FC<{ value: number }> = ({ value }) => {
  const [displayValue, setDisplayValue] = useState(0);
  
  useEffect(() => {
    const duration = 1000; // 1 second animation
    const steps = 60;
    const increment = value / steps;
    let current = 0;
    
    const timer = setInterval(() => {
      current += increment;
      if (current >= value) {
        setDisplayValue(value);
        clearInterval(timer);
      } else {
        setDisplayValue(Math.floor(current));
      }
    }, duration / steps);
    
    return () => clearInterval(timer);
  }, [value]);
  
  return <span>{formatNumber(displayValue)}</span>;
};

ReportSummary Component Implementation

// ReportSummary.tsx
interface ReportSummaryData {
  totalReports: number;
  expectedReports: number;
  completedReports: number;
  completionRate: number;
  periodComparison: {
    totalChange: number;
    completionChange: number;
    trend: 'up' | 'down' | 'neutral';
  };
}
 
export const ReportSummary: React.FC = () => {
  const { 
    completeReportDetailData,
    isLoading 
  } = useSelector((state: RootState) => state.dashboardOperational);
  
  const {
    totalReports = 0,
    expectedReports = 0,
    rcr = 0,
    reportSubmittedLastWeekPercentage = 0
  } = completeReportDetailData || {};
  
  const completedReports = Math.round((rcr / 100) * expectedReports);
  const missedReports = expectedReports - completedReports;
  
  return (
    <div className="aa-bg-white aa-rounded-lg aa-shadow-md aa-p-6">
      <h3 className="aa-text-lg aa-font-semibold aa-mb-4">Report Summary</h3>
      
      <div className="aa-space-y-4">
        <MetricRow
          label="Total Reports"
          value={totalReports}
          change={reportSubmittedLastWeekPercentage}
          format="number"
        />
        
        <MetricRow
          label="Expected Reports"
          value={expectedReports}
          format="number"
        />
        
        <MetricRow
          label="Completion Rate"
          value={rcr}
          format="percentage"
          thresholds={{ good: 90, warning: 70 }}
        />
        
        <MetricRow
          label="Missed Reports"
          value={missedReports}
          format="number"
          inverse={true} // Lower is better
        />
      </div>
      
      {/* Visual progress bar */}
      <div className="aa-mt-6">
        <div className="aa-w-full aa-bg-gray-200 aa-rounded-full aa-h-2">
          <div 
            className="aa-bg-blue-600 aa-h-2 aa-rounded-full aa-transition-all"
            style={{ width: `${rcr}%` }}
          />
        </div>
        <p className="aa-text-sm aa-text-gray-600 aa-mt-2">
          {completedReports} of {expectedReports} reports completed
        </p>
      </div>
    </div>
  );
};

Desktop vs Mobile Layouts

SummaryDesktop Implementation:

// SummaryDesktop.tsx
import styled from 'styled-components';
import { IssueSummary } from './IssueSummary';
import { ReportSummary } from './ReportSummary';
 
const DesktopContainer = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  height: 100%;
  
  @media (min-width: 1440px) {
    gap: 32px;
  }
`;
 
const SummaryCard = styled.div`
  background: white;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
  
  &:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    transform: translateY(-2px);
  }
`;
 
export const SummaryDesktop: React.FC = () => {
  return (
    <DesktopContainer>
      <SummaryCard>
        <IssueSummary />
      </SummaryCard>
      <SummaryCard>
        <ReportSummary />
      </SummaryCard>
    </DesktopContainer>
  );
};

SummaryMobile Implementation:

// SummaryMobile.tsx
import { useState } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/css';
 
const MobileContainer = styled.div`
  width: 100%;
  overflow: hidden;
`;
 
const MobileCard = styled.div`
  background: white;
  border-radius: 8px;
  padding: 16px;
  margin: 0 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;
 
const TabIndicator = styled.div`
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 16px;
`;
 
const Dot = styled.div<{ active: boolean }>`
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: ${props => props.active ? '#7E78EA' : '#E5E5E5'};
  transition: background 0.3s ease;
`;
 
export const SummaryMobile: React.FC = () => {
  const [activeIndex, setActiveIndex] = useState(0);
  
  return (
    <MobileContainer>
      <Swiper
        spaceBetween={16}
        slidesPerView={1.1}
        centeredSlides={true}
        onSlideChange={(swiper) => setActiveIndex(swiper.activeIndex)}
      >
        <SwiperSlide>
          <MobileCard>
            <h3 className="aa-text-md aa-font-semibold aa-mb-3">Issues</h3>
            <IssueSummary />
          </MobileCard>
        </SwiperSlide>
        
        <SwiperSlide>
          <MobileCard>
            <h3 className="aa-text-md aa-font-semibold aa-mb-3">Reports</h3>
            <ReportSummary />
          </MobileCard>
        </SwiperSlide>
      </Swiper>
      
      <TabIndicator>
        <Dot active={activeIndex === 0} />
        <Dot active={activeIndex === 1} />
      </TabIndicator>
    </MobileContainer>
  );
};

Responsive Design Implementation

// Responsive container with performance optimizations
const SummaryContainer = styled.div`
  display: grid;
  gap: 16px;
  
  @media (min-width: 1024px) {
    grid-template-columns: repeat(2, 1fr);
    gap: 24px;
  }
  
  @media (max-width: 1023px) {
    grid-template-columns: 1fr;
    gap: 12px;
  }
`;
 
// Conditional rendering with lazy loading
const Summary = () => {
  const [isMobile, setIsMobile] = useState(window.innerWidth < 1024);
  
  useEffect(() => {
    const handleResize = debounce(() => {
      setIsMobile(window.innerWidth < 1024);
    }, 200);
    
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  const Component = lazy(() => 
    isMobile 
      ? import('./SummaryMobile') 
      : import('./SummaryDesktop')
  );
  
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Component />
    </Suspense>
  );
};

Package Dependencies

Core Dependencies

PackageVersionPurposeUsage in Operational Dashboard
chart.js2.9.4Chart rendering engineBar charts (TopChart), Line charts (Trends), Ring charts (KPI)
react-chartjs-22.11.2React wrapper for Chart.jsAll chart components
redux4.0.5State managementGlobal state for filters, data, UI state
react-redux7.2.0React bindingsComponent-store connection
redux-saga1.1.3Async operationsAPI calls, data fetching
moment2.24.0Date manipulationDate formatting, range calculations
react-router-dom5.1.2RoutingNavigation between dashboard views
styled-components5.1.0CSS-in-JSComponent styling
i18next17.0.8InternationalizationMulti-language support
react-toastify8.0.2NotificationsSuccess/error messages

Export/Download Dependencies

PackageVersionPurposeUsage
papaparse5.2.0CSV generationExport data to CSV format
xlsx0.16.2Excel generationExport data to XLSX format
file-saver2.0.5File downloadTrigger browser downloads

UI Component Libraries

PackageVersionPurposeUsage
@radix-ui/react-*VariousAccessible UI primitivesDropdowns, modals, popovers
classnames2.3.2Dynamic classNamesConditional styling
tailwind-merge2.4.0Tailwind utilitiesClass merging
react-icons5.2.1Icon libraryUI icons and indicators

Internal Nimbly Packages

PackageVersionPurposeUsage
@nimbly-technologies/nimbly-common1.95.3Shared types/utilitiesCommon enums, types
@nimbly-technologies/audit-component1.1.8Shared componentsReusable UI components

Performance Considerations

1. Data Loading Optimization

// Parallel data fetching on mount
useEffect(() => {
  // Batch API calls for faster initial load
  Promise.all([
    dispatch(fetchCompleteReportDetailAsync.request()),
    dispatch(fetchIssueOverviewDetailAsync.request()),
    dispatch(fetchIssueChartAsync.request()),
    dispatch(fetchReportChartAsync.request())
  ]);
}, []);

2. Chart Rendering Performance

Optimization Strategies:

  • Debounced Updates: Prevent excessive re-renders during rapid filter changes
  • Memoization: Cache calculated chart data
  • Lazy Loading: Load chart libraries only when needed
  • Data Limiting: Display maximum 5 items in TopChart, paginate in modals
// Memoized chart data
const chartData = useMemo(() => {
  return processChartData(rawData);
}, [rawData]);
 
// Debounced filter updates
const debouncedFilterUpdate = useDebounce(filters, 500);

3. Memory Management

Best Practices:

  • Component Unmounting: Clean up chart instances and event listeners
  • Data Pagination: Load data in chunks for large datasets
  • State Normalization: Avoid data duplication in Redux store
// Cleanup on unmount
useEffect(() => {
  return () => {
    // Destroy chart instances
    if (chartRef.current) {
      chartRef.current.destroy();
    }
  };
}, []);

4. Network Optimization

Strategies:

  • Request Batching: Combine multiple API calls
  • Caching: Store frequently accessed data
  • Compression: Enable gzip for API responses
  • Selective Loading: Fetch only required fields
// Filter caching
const FILTER_CACHE_KEY = 'operational_dashboard_filters';
const cachedFilters = localStorage.getItem(FILTER_CACHE_KEY);

5. Responsive Design Performance

Mobile Optimizations:

  • Conditional Rendering: Different components for mobile/desktop
  • Touch Optimization: Larger touch targets on mobile
  • Reduced Animations: Minimize animations on lower-end devices
// Conditional component loading
const ChartComponent = lazy(() => 
  isMobile 
    ? import('./MobileChart') 
    : import('./DesktopChart')
);

6. Bundle Size Optimization

Techniques:

  • Tree Shaking: Import only used Chart.js modules
  • Code Splitting: Separate dashboard bundle
  • Dynamic Imports: Load features on demand
// Selective Chart.js imports
import { Chart } from 'chart.js/auto';
import { BarController, LineController } from 'chart.js';
 
Chart.register(BarController, LineController);

7. Real-time Updates

Considerations:

  • Polling Interval: Balance between freshness and performance
  • WebSocket: Consider for real-time requirements
  • Incremental Updates: Update only changed data
// Smart polling with visibility API
useEffect(() => {
  if (document.visibilityState === 'visible') {
    const interval = setInterval(fetchData, 300000); // 5 minutes
    return () => clearInterval(interval);
  }
}, [document.visibilityState]);

8. Error Handling and Recovery

Strategies:

  • Retry Logic: Automatic retry for failed requests
  • Graceful Degradation: Show cached data on failure
  • Error Boundaries: Prevent dashboard crashes
// Saga with retry logic
function* fetchDataSaga() {
  for (let i = 0; i < 3; i++) {
    try {
      const data = yield call(api.fetchData);
      yield put(fetchDataSuccess(data));
      break;
    } catch (error) {
      if (i === 2) yield put(fetchDataFailure(error));
      else yield delay(1000 * (i + 1)); // Exponential backoff
    }
  }
}

Performance Metrics

Target Performance Goals

MetricTargetCurrent
Initial Load Time< 3s~2.5s
Time to Interactive< 5s~4.2s
Chart Render Time< 500ms~300ms
Filter Apply Time< 1s~800ms
Export Generation< 5s~3s
Memory Usage< 100MB~80MB

Monitoring

The dashboard includes performance monitoring through:

  • Sentry for error tracking
  • Google Analytics for user behavior
  • Custom performance marks for critical operations
  • Browser Performance API for detailed metrics

Testing Considerations

Unit Testing

The operational dashboard components are tested using:

  • Jest: JavaScript testing framework
  • React Testing Library: Component testing
  • Redux-Saga-Test-Plan: Saga testing
// Example component test
describe('KpiOverview', () => {
  it('should display IRR and RCR values', () => {
    const { getByText } = render(
      <KpiOverview 
        irrValue={85.5} 
        rcrValue={92.3} 
      />
    );
    expect(getByText('85.5%')).toBeInTheDocument();
    expect(getByText('92.3%')).toBeInTheDocument();
  });
});

Integration Testing

Key integration test scenarios:

  • Filter application and data refresh
  • Chart rendering with different data sets
  • Export functionality across formats
  • Responsive behavior on different screen sizes
  • Error handling and recovery

E2E Testing

End-to-end testing covers:

  • Complete user workflows
  • Performance benchmarks
  • Cross-browser compatibility
  • Mobile device testing

Security Considerations

Data Security

  1. API Authentication: All dashboard APIs require valid JWT tokens
  2. Role-Based Access: Dashboard [visibility](../Settings/Data Visibility/Data Visibility.md) controlled by user roles
  3. Data Filtering: Server-side filtering based on user [permissions](../Settings/Access control/Access Control.md)
  4. Secure Storage: Sensitive data encrypted in transit and at rest

Frontend Security

  1. XSS Prevention: Sanitized user inputs and content
  2. CSRF Protection: Token-based request validation
  3. Content Security Policy: Restrictive CSP headers
  4. Dependency Scanning: Regular security audits of npm packages

15. Troubleshooting Guide

15.1 Common [Issues](../Issue Tracker/IssueTrackerOverview.md)

  1. Charts Not Rendering

    • Check browser console for errors
    • Verify Chart.js is loaded
    • Ensure data format is correct
  2. Slow Performance

    • Check network tab for slow API calls
    • Verify filter complexity
    • Consider data volume limits
  3. Export Failures

    • Check browser download settings
    • Verify popup blockers
    • Ensure sufficient memory
  4. Filter Persistence [Issues](../Issue Tracker/IssueTrackerOverview.md)

    • Clear browser cache
    • Check localStorage quota
    • Verify API connectivity

16. Conclusion

The Operational Dashboard is a comprehensive analytics solution built with modern web technologies. It provides real-time insights into operational performance through intuitive visualizations and powerful filtering capabilities. The architecture emphasizes performance, scalability, and user experience while maintaining code quality and maintainability.

16.1 Key Strengths

  1. Modular Architecture: Clean separation of concerns with reusable components
  2. Performance Optimized: Efficient data loading and rendering strategies
  3. User-Centric Design: Intuitive interface with responsive layouts
  4. Extensible Framework: Easy to add new metrics and visualizations
  5. Robust Error Handling: Graceful degradation and recovery mechanisms

16.2 Resources


Document Version: 1.0.0
Last Updated: January 2025
Total Characters: ~45,000+