1. Overview
The Report Insights Dashboard is a comprehensive analytics module within the Nimbly audit administration system. It provides detailed insights into operational reports, allowing administrators to monitor, analyze, and manage report submissions across the organization.
System Integrations:
- Dashboard - Parent dashboard framework
- Reports - Primary data source
- Sites - Location-based analytics
- Users - User performance tracking
- Schedule - Audit scheduling data
- Authentication - Access control
1.1 Purpose
- Monitor report submission status and completion rates
- Analyze report types and their distribution
- Track operational KPIs related to reporting activities
- Provide data-driven insights for decision making
1.2 Key Features
- Real-time report status monitoring
- Advanced filtering capabilities
- Interactive data visualizations
- Responsive design for mobile and desktop
- Persistent filter preferences
- Export capabilities
2. Architecture
The Report Insights Dashboard follows a component-based architecture using React and Redux for state management.
graph TD A[ReportInsightsPage] --> B[OperationalPageHeader] A --> C[DashboardFilters] A --> D[ReportStatus Component] A --> E[ReportType Component] A --> F[ReportInsightTable] A --> G[ReportInsightSummary] C --> H[FilterField Components] D --> I[ReportChart] E --> J[ReportChart] A --> K[useReportInsightsData Hook] K --> L[Redux Store] K --> M[API Services]
3. Routing
3.1 Routes Configuration
| Route Path | Component | Access Required | Description |
|---|---|---|---|
/analytics/operational/[report](../Reports/ReportsOverview.md)-insights | OperationalReportInsightsDashboardPage | RoleResources.DASHBOARD_OVERVIEW_ALL | Main report insights dashboard |
/analytics/operational | OperationalDashboardPage | RoleResources.DASHBOARD_OVERVIEW_ALL | Parent operational dashboard |
/analytics/executive | ExecutiveDashboardPage | RoleResources.DASHBOARD_OVERVIEW_ALL | Executive level dashboard |
3.2 Navigation Hierarchy
- Dashboard → Operational KPI Dashboard → Report Insights Dashboard
The routing is defined in src/routes/admin-routes.js:780-786
4. Core Components
4.1 ReportInsightsPage Component
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportInsightsPage.tsx
The main container component that orchestrates the entire Report Insights Dashboard.
Key Responsibilities:
- Manages filter state and visibility
- Handles data loading and refresh
- Coordinates filter persistence
- Renders child components based on viewport
State Management:
showFilters: Boolean to toggle filter panel visibilityshowFilterIndicator: Boolean for filter notification modallastUpdated: Moment object tracking last data refreshtempReportStatus: Array of selected report statuses
4.2 ReportStatus Component
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportStatus.tsx
Displays report status distribution using an interactive pie chart.
Features:
- Three status categories: Completed, Pending, Missed
- Click-through navigation to filtered views
- Responsive tooltips with detailed counts
- Color-coded visualization (Green, Yellow, Red)
4.3 ReportType Component
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportType.tsx
Shows distribution of report types (Scheduled vs Ad Hoc).
Features:
- Interactive pie chart visualization
- Drill-down capability to filtered report lists
- Tooltips with exact counts
- Responsive design adapting to container size
4.4 ReportInsightTable Component
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportInsightTable.tsx
Tabular representation of report data with two view modes.
Tabs:
- Report Count: Shows aggregated report counts by entity
- Report Completion Time: Displays average completion times
Features:
- Sortable columns
- Pagination with customizable page sizes
- Export functionality (PDF, CSV, XLSX)
- Grouping by Site, Department, User, or Questionnaire
- Custom styling for different value ranges
4.5 ReportInsightSummary Component
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportInsightSummary.tsx
Summary cards displaying key metrics.
Metrics Displayed:
- Total Reports Submitted
- Total Reports Missed
- Average Completion Time (hours)
Design:
- Card-based layout
- Icon-based visual indicators
- Mobile-optimized responsive design
4.6 ReportChart Component
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportChart.tsx
Reusable chart component used by ReportStatus and ReportType.
Props:
data: Chart data arrayonChartClick: Click handler for drill-downlegendPosition: Customizable legend placementwidth/height: Responsive sizing
Features:
- Built on Chart.js with React wrapper
- Customizable color schemes
- Interactive tooltips
- Click event handling for navigation
5. Data Flow
The Report Insights Dashboard implements a unidirectional data flow pattern using React hooks and Redux.
5.1 Data Flow Diagram
sequenceDiagram participant U as User participant C as Component participant H as useReportInsightsData Hook participant R as Redux Store participant A as API Service U->>C: Interact (filter/click) C->>H: Call hook function H->>A: Fetch data A-->>H: Return response H->>R: Dispatch action R-->>C: Update state C-->>U: Re-render UI
5.2 Data Fetching Flow
-
Initial Load:
loadInitialData()fetches all dashboard data- Parallel API calls for efficiency
- Redux store updated with responses
-
Filter Application:
applyFilters()triggers data refresh- Filters persisted to user preferences
- All components refresh with new data
-
Drill-down Navigation:
- Chart clicks trigger grouped data fetch
- Navigation to filtered report lists
- State maintained across views
6. Filters and Search
6.1 Filter System Architecture
The dashboard implements a comprehensive filtering system with persistence.
6.2 Available Filters
-
Date Range Filter
- Start and End date selection
- Inherited from parent Operational Dashboard
- Applied across all report data
-
Site Filter
- Multi-select site selection
- Hierarchical site structure support
- Department-based site filtering
-
Department Filter
- Multi-select department selection
- Impacts available sites
- Cascading filter behavior
-
User Filter
- Select specific users
- Filtered by selected departments/sites
- Role-based visibility
-
Questionnaire Filter
- Filter by report templates
- Active questionnaires only
- Category-based grouping
-
Report Status Filter (Specific to Report Insights)
- Completed, Pending, Missed statuses
- Multi-select capability
- Visual indicators in UI
6.3 Filter Persistence
Filters are saved to user preferences via:
fetchSaveFilters()- Saves current filter statefetchSavedFilters()- Retrieves saved filters on load- Stored per dashboard type (reportInsights, kpi, etc.)
6.4 Filter Implementation
// Filter application flow
const handleFilterOnApply = (filters: Partial<OperationalFilters>) => {
const additionalFilters = {
...filters,
reportStatus: tempReportStatus,
};
applyFilters(additionalFilters);
setLastUpdated(moment());
};7. State Management
7.1 Redux Store Structure
The Report Insights module uses Redux for centralized state management.
Store Location: src/reducers/dashboardRevamp/report-insights/
7.2 State Shape
interface ReportInsightsReducerState {
filters: ReportInsightsFilters;
reportStatus: {
isLoading: boolean;
basicData: ReportStatusData;
drilledData: ReportStatusGroupedByData;
};
reportType: {
isLoading: boolean;
basicData: ReportTypeData;
drilledData: ReportTypeGroupedByData;
};
reportTable: {
isCountDataLoading: boolean;
countData: ReportCountTableData;
isRcrDataLoading: boolean;
rcrData: RCRTableData;
};
reportSummary: {
isLoading: boolean;
data: ReportSummaryData;
};
downloadView: {
isViewDataLoading: boolean;
viewData: ReportViewData[];
};
}7.3 Key Actions
| Action | Purpose | Payload |
|---|---|---|
setFilters | Update dashboard filters | ReportInsightsFilters |
setReportStatusBasicData | Update status chart data | ReportStatusData |
setReportTypeBasicData | Update type chart data | ReportTypeData |
setReportTableCountData | Update count table data | ReportCountTableData |
setReportSummaryData | Update summary metrics | ReportSummaryData |
7.4 Redux Integration
// Reducer registration in root reducer
reportInsights: reportInsightsReducer,
// Component usage
const dispatch = useDispatch();
const { reportStatus } = useSelector((state: RootState) =>
state.reportInsights.filters || {}
);8. API Integration
8.1 API Endpoints
| Endpoint | Method | Purpose | File Reference |
|---|---|---|---|
/statistics/dashboard/[report](../Reports/ReportsOverview.md) | POST | Fetch report data | useReportInsightsData.ts:88 |
/statistics/dashboard/[report](../Reports/ReportsOverview.md)/detail | POST | Fetch summary data | useReportInsightsData.ts:263 |
/statistics/dashboard/[report](../Reports/ReportsOverview.md)/download | POST | Download report data | useReportInsightsData.ts:281 |
/api-statistics/dashboard/[report](../Reports/ReportsOverview.md)/download/view | POST | Preview download data | useReportInsightsData.ts:303 |
8.2 Request Payload Structure
// Common request structure
{
filters: {
siteIds: string[],
departmentIds: string[],
userIds: string[],
questionnaireIds: string[],
startDate: string, // ISO format
endDate: string, // ISO format
reportStatus?: string[]
},
groupBy?: 'site' | 'department' | 'user' | 'questionnaire',
sortOrder?: 'asc' | 'desc',
sortBy?: string,
page?: number,
pageSize?: number
}8.3 API Response Handling
The API responses are transformed and dispatched to Redux store:
// Example: Report Status API flow
const fetchBasicReportStatusData = async () => {
const response = await api.post('/statistics/dashboard/report', {
filters: currentFilters,
aggregationType: 'status'
});
dispatch(setReportStatusBasicData(response.data));
};8.4 Error Handling
- API errors are caught at the hook level
- Toast notifications for user feedback
- Loading states managed in Redux
- Fallback to empty data on error
9. Data Visualization
9.1 Chart Implementation
The dashboard uses Chart.js v3.9.1 with react-chartjs-2 v4.3.1 for interactive visualizations.
9.2 Chart Components
9.2.1 Pie/Doughnut Charts
- Report Status: Shows distribution of Completed/Pending/Missed
- Report Type: Displays Scheduled vs Ad Hoc reports
- Configuration: Customizable colors, tooltips, and click handlers
9.2.2 Chart Interactions
// Click handler for drill-down
const handleChartClick = (elements: any) => {
if (elements.length > 0) {
const clickedIndex = elements[0].index;
const clickedData = chartData[clickedIndex];
// Navigate to filtered view
onChartClick(clickedData);
}
};9.2.3 Responsive Design
- Charts automatically resize based on container
- Mobile-optimized legend positioning
- Touch-friendly interaction zones
9.3 Table Visualizations
9.3.1 Report Count Table
- Aggregated counts by entity (Site/Dept/User/Questionnaire)
- Color coding for performance indicators:
- Green: High completion (>80%)
- Yellow: Medium completion (50-80%)
- Red: Low completion (<50%)
9.3.2 Report Completion Time Table
- Average time from creation to submission
- Sorted by performance metrics
- Exportable to multiple formats
10. Utilities and Helpers
10.1 useReportInsightsData Hook
Location: src/pages/dashboardRevamp/reportInsightsPage/useReportInsightsData.ts
Central data management hook providing:
loadInitialData(): Loads all dashboard componentsapplyFilters(): Refreshes data with new filters- Individual fetch functions for each component
- Redux dispatch integration
10.2 ReportInsightUtils
Location: src/pages/dashboardRevamp/reportInsightsPage/ReportInsightUtils.ts
Table column configurations:
REPORT_COUNT_TABLE_COLUMNS: Defines count table structureRCR_TABLE_COLUMNS: Defines completion time table structure- Custom cell renderers for styling
10.3 downloadConfig
Location: src/pages/dashboardRevamp/reportInsightsPage/downloadConfig.ts
Export configuration:
- Column ordering for downloads
- Format specifications (PDF, CSV, XLSX)
- Custom headers and data transformations
10.4 Common Utilities Used
| Utility | Purpose | Location |
|---|---|---|
useDashboardDataOptions | Provides dropdown options | src/pages/dashboardRevamp/utils/ |
usePageWrapperHeightStyle | Responsive height calculation | src/hooks/ |
hideBodyElementScrollbar | Viewport management | src/helpers/commons |
mapValuesToOptions | Select component mapping | src/components/global/Select/utils/ |
11. Package Dependencies
11.1 Core Dependencies
| Package | Version | Purpose |
|---|---|---|
react | ^17.0.2 | Core framework |
react-redux | ^7.2.4 | Redux bindings |
redux | ^4.1.0 | State management |
react-chartjs-2 | ^4.3.1 | Chart components |
chart.js | ^3.9.1 | Chart rendering |
moment | ^2.29.1 | Date handling |
react-i18next | ^11.8.5 | Internationalization |
react-toastify | ^7.0.4 | Notifications |
@loadable/component | ^5.15.0 | Code splitting |
11.2 UI Components
| Package | Purpose |
|---|---|
@nimbly-technologies/nimbly-common | Shared components |
| Custom Layout components | Page structure |
| Custom Button/Select components | Form elements |
| Custom Table components | Data display |
11.3 Development Dependencies
| Package | Purpose |
|---|---|
typescript | Type safety |
@types/react | React types |
@types/react-redux | Redux types |
12. Performance Optimizations
12.1 Code Splitting
- Route-based lazy loading with
@loadable/component - Reduces initial bundle size
- Improves first contentful paint
12.2 Memoization
- Redux selectors prevent unnecessary re-renders
- Chart data cached until filters change
- Table pagination reduces DOM nodes
12.3 API Optimization
- Parallel API calls in
loadInitialData() - Debounced filter applications
- Paginated data fetching for tables
12.4 Responsive Breakpoints
- Mobile view (<500px): Simplified layout
- Tablet view (500-1280px): Adjusted grid
- Desktop view (>1280px): Full feature set
12.5 State Management
- Normalized Redux state structure
- Selective component subscriptions
- Efficient action dispatching
13. Advanced Features
13.1 Drill-Down Navigation
The dashboard implements multi-level drill-down functionality:
graph LR A[Report Status Chart] -->|Click Completed| B[Filtered Table View] A -->|Click Pending| C[Filtered Table View] A -->|Click Missed| D[Filtered Table View] B --> E[Navigate to Report Details] C --> E D --> E
13.2 Dynamic Grouping
Users can group data by different dimensions:
flowchart TD A[Report Data] --> B{Group By} B -->|Site| C[Site-wise Aggregation] B -->|Department| D[Department-wise Aggregation] B -->|User| E[User-wise Aggregation] B -->|Questionnaire| F[Questionnaire-wise Aggregation]
13.3 Export Functionality
Multiple export formats supported:
graph TD A[Export Button] --> B{Format Selection} B --> C[PDF Export] B --> D[CSV Export] B --> E[XLSX Export] C --> F[Download File] D --> F E --> F
13.4 Real-time Updates
The dashboard refreshes data based on user interactions:
- Filter changes trigger immediate data refresh
- Last updated timestamp shows data freshness
- Manual refresh available via UI controls
14. User Workflows
14.1 Initial Dashboard Load
sequenceDiagram participant User participant Dashboard participant API participant Redux User->>Dashboard: Navigate to Report Insights Dashboard->>API: Fetch saved filters API-->>Dashboard: Return filters Dashboard->>Redux: Apply filters Dashboard->>API: Fetch all data (parallel) API-->>Redux: Update store Redux-->>Dashboard: Render UI
14.2 Filter Application Flow
sequenceDiagram participant User participant FilterPanel participant Dashboard participant API User->>FilterPanel: Select filters User->>FilterPanel: Click Apply FilterPanel->>Dashboard: Apply filters Dashboard->>API: Fetch filtered data API-->>Dashboard: Return data Dashboard->>User: Update visualizations
14.3 Drill-Down Navigation
sequenceDiagram participant User participant Chart participant Table participant ReportList User->>Chart: Click chart segment Chart->>Table: Filter by selection Table->>User: Show filtered data User->>Table: Click row Table->>ReportList: Navigate with filters
15. Troubleshooting
15.1 Common Issues
-
Filters not persisting
- Check user permissions for filter saving
- Verify API endpoint accessibility
- Confirm Redux state updates
-
Charts not rendering
- Validate data format from API
- Check Chart.js version compatibility
- Verify container dimensions
-
Export failures
- Check download permissions
- Verify data size limits
- Confirm export service availability
15.2 Debug Tips
-
Enable Redux DevTools for state inspection
-
Check network tab for API responses
-
Use React Developer Tools for component props
-
Monitor console for error messages
-
Progressive Web App capabilities
-
Enhanced accessibility features
17. Conclusion
The Report Insights Dashboard is a comprehensive analytics solution that provides:
- Real-time operational insights
- Flexible data visualization
- Advanced filtering capabilities
- Seamless user experience
- Scalable architecture
The modular design and clean separation of concerns ensure maintainability and extensibility.
18. Implementation Details
18.1 Filter Component Architecture
The DashboardFilters component (src/pages/dashboardRevamp/DashboardFilters.tsx) serves as the centralized filtering system for all dashboard modules.
18.1.1 Filter State Management
// Local state for filter values
const [tempStartDate, setTempStartDate] = useState(startDate);
const [tempEndDate, setTempEndDate] = useState(endDate);
const [tempSite, setTempSite] = useState<string[]>(siteIDs || []);
const [tempDepartment, setTempDepartment] = useState<string[]>(departmentIDs || []);
const [tempUser, setTempUser] = useState<string[]>(userIDs || []);
const [tempQuestionnaire, setTempQuestionnaire] = useState<string[]>(questionnaireIDs || []);
const [tempCategory, setTempCategory] = useState<string[]>(categories || []);18.1.2 Filter Hierarchy
-
Managed By Filters (Role-based visibility)
- Department selection impacts available users
- Site region selection cascades to sites
- Permission-based display logic
-
Standard Filters
- Date range with preset options
- Hierarchical site/department/user selection
- Questionnaire and category filtering
18.1.3 Responsive Filter Layout
// Desktop layout (>1024px)
<div className="aa-hidden lg:aa-block">
<FilterRow>
// Horizontal filter layout
</FilterRow>
</div>
// Mobile layout (<1024px)
<Row className="lg:aa-hidden">
// Vertical filter layout with modal behavior
</Row>18.2 Header Component Integration
The OperationalPageHeader component provides consistent navigation and context across dashboards.
18.2.1 Key Features
- Breadcrumb Navigation: Hierarchical path display
- Filter Toggle: Shows/hides filter panel
- Last Update Display: Real-time data freshness indicator
- Saved Filter Indicator: Visual cue for applied saved filters
18.2.2 Responsive Behavior
// Mobile date range display
<div className="aa-my-1 aa-text-sm aa-block min-[1024px]:aa-hidden">
<span className="aa-font-semibold">{t('label.dashboardRevamp.dateRange')}: </span>
{moment(startDate)?.format('D MMM YYYY')} - {moment(endDate)?.format('D MMM YYYY')}
</div>18.3 Data Service Layer
18.3.1 Service Architecture
graph TD A[Component Layer] --> B[Custom Hooks] B --> C[API Service Layer] C --> D[HTTP Client] D --> E[Backend APIs] B --> F[Redux Actions] F --> G[Redux Store] G --> A
18.3.2 API Service Implementation
// Service function pattern
export const fetchReportData = async (payload: ReportRequestPayload) => {
try {
const response = await apiClient.post('/statistics/dashboard/report', payload);
return response.data;
} catch (error) {
handleApiError(error);
throw error;
}
};18.3.3 Request Optimization
- Parallel API calls using Promise.all()
- Request debouncing for filter changes
- Response caching in Redux store
- Pagination for large datasets
18.4 Performance Monitoring
18.4.1 Key Metrics Tracked
-
Component Render Time
- Initial load performance
- Re-render optimization
- Virtual DOM efficiency
-
API Response Time
- Request latency
- Payload size optimization
- Error rate monitoring
-
User Interaction Metrics
- Filter application time
- Chart interaction responsiveness
- Export generation speed
18.5 Security Considerations
18.5.1 Data Access Control
-
Role-Based Access
access={RoleResources.DASHBOARD_OVERVIEW_ALL} -
Filter Permissions
- Managed By filters restricted by role
- Department-based data visibility
- User-level access controls
-
API Security
- JWT token authentication
- Request validation
- Response sanitization
18.6 Internationalization (i18n)
18.6.1 Implementation
const { t } = useTranslation();
// Usage in components
<FilterSubHeader>{t('label.dashboardRevamp.reportInsights.reportStatus')}</FilterSubHeader>18.6.2 Supported Languages
- English (en)
- Indonesian (id)
- Thai (th)
- Spanish (es)
- Portuguese (pt)
- Korean (ko)
- Chinese Mandarin (cmn)
18.7 Testing Strategy
18.7.1 Unit Tests
- Component rendering tests
- Hook functionality tests
- Redux action/reducer tests
- Utility function tests
18.7.2 Integration Tests
- API integration tests
- Filter application workflows
- Export functionality
- Cross-component communication
18.7.3 E2E Tests
- Complete user workflows
- Performance benchmarks
- Cross-browser compatibility
- Mobile responsiveness
18.8 Deployment Configuration
18.8.1 Environment Variables
// Production configuration
const apiURL = process.env.REACT_APP_API_URL;
const internalUrl = process.env.REACT_APP_INTERNAL_URL;18.8.2 Build Optimization
- Code splitting by route
- Tree shaking for unused code
- Asset optimization
- CDN integration
18.9 Monitoring and Analytics
18.9.1 Application Monitoring
- Error tracking with Sentry
- Performance monitoring
- User behavior analytics
- Custom event tracking
18.9.2 Dashboard Metrics
// Track filter usage
trackEvent('dashboard_filter_applied', {
dashboard: 'report_insights',
filters: appliedFilters,
timestamp: Date.now()
});
// Track export usage
trackEvent('dashboard_export', {
format: exportFormat,
rowCount: data.length,
groupBy: currentGrouping
});18.10 Accessibility Features
18.10.1 WCAG 2.1 Compliance
- Keyboard navigation support
- Screen reader compatibility
- High contrast mode support
- Focus management
18.10.2 Accessibility Implementation
// ARIA labels
<FilterField
aria-label={t('label.dashboardRevamp.reportStatus')}
aria-describedby="report-status-help"
/>
// Keyboard shortcuts
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === 'f') {
toggleFilter();
}
};
document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
}, []);18.11 Mobile Optimization
18.11.1 Touch Interactions
- Swipe gestures for navigation
- Touch-friendly chart interactions
- Optimized tap targets (minimum 44x44px)
- Haptic feedback support
18.11.2 Performance Optimization
- Lazy loading of heavy components
- Image optimization with responsive sizes
- Reduced animation complexity
- Efficient scroll handling
18.12 Error Handling
18.12.1 Error Boundaries
class DashboardErrorBoundary extends Component {
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
return <ErrorFallback />;
}
return this.props.children;
}
}18.12.2 API Error Handling
const handleApiError = (error: AxiosError) => {
if (error.response?.status === 401) {
// Handle authentication error
redirectToLogin();
} else if (error.response?.status === 403) {
// Handle authorization error
toast.error(t('error.unauthorized'));
} else {
// Generic error handling
toast.error(t('error.generic'));
}
};18.13 Caching Strategy
18.13.1 Redux Store Caching
- Persistent filter states
- Cached API responses
- Optimistic updates
- Cache invalidation logic
18.13.2 Browser Caching
// Service Worker caching
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/statistics/')) {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
}
});18.14 Development Guidelines
18.14.1 Code Style
- TypeScript strict mode
- ESLint configuration
- Prettier formatting
- Component naming conventions
18.14.2 Git Workflow
# Feature branch naming
feature/TECH-XXX-report-insights-enhancement
# Commit message format
feat(report-insights): Add export functionality
# PR template includes
- [ ] Unit tests added
- [ ] Documentation updated
- [ ] Performance impact assessed
- [ ] Accessibility checked18.14.3 Documentation Standards
- JSDoc comments for functions
- TypeScript interfaces documented
- README files for modules
- Inline code comments for complex logic
19. Detailed Component Analysis
19.1 ReportChart Deep Dive
The ReportChart component serves as the foundation for all chart visualizations in the Report Insights Dashboard.
19.1.1 Props Interface
interface ReportChartProps {
data: ChartDataItem[];
onChartClick?: (item: ChartDataItem) => void;
chartType: 'doughnut' | 'pie';
centerText?: string;
showLegend?: boolean;
legendPosition?: 'top' | 'bottom' | 'left' | 'right';
colors?: string[];
width?: number;
height?: number;
className?: string;
}19.1.2 Chart Configuration Details
const chartOptions: ChartOptions<'doughnut'> = {
responsive: true,
maintainAspectRatio: false,
cutout: chartType === 'doughnut' ? '70%' : '0%',
plugins: {
legend: {
display: showLegend,
position: legendPosition,
labels: {
usePointStyle: true,
padding: 15,
font: {
size: 12,
family: 'Inter, sans-serif'
},
color: '#4A5568'
}
},
tooltip: {
enabled: true,
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: '#fff',
borderWidth: 1,
cornerRadius: 4,
displayColors: true,
callbacks: {
label: (context) => {
const label = context.label || '';
const value = context.parsed;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((value / total) * 100).toFixed(1);
return `${label}: ${value} (${percentage}%)`;
}
}
}
},
onClick: (event, elements) => {
if (elements.length > 0 && onChartClick) {
const index = elements[0].index;
onChartClick(data[index]);
}
}
};19.1.3 Color Scheme Management
const DEFAULT_COLORS = {
COMPLETED: '#10B981', // Green
PENDING: '#F59E0B', // Yellow
MISSED: '#EF4444', // Red
SCHEDULED: '#3B82F6', // Blue
ADHOC: '#8B5CF6' // Purple
};
const getChartColors = (data: ChartDataItem[], customColors?: string[]): string[] => {
if (customColors && customColors.length >= data.length) {
return customColors;
}
return data.map(item => {
const key = item.name.toUpperCase().replace(/\s+/g, '_');
return DEFAULT_COLORS[key] || '#6B7280'; // Gray fallback
});
};19.2 Report Table Implementation Details
19.2.1 Table Column Configuration System
interface TableColumn {
key: string;
header: string;
accessor: (row: any) => any;
sortable?: boolean;
width?: string;
align?: 'left' | 'center' | 'right';
cellRenderer?: (value: any, row: any) => React.ReactNode;
headerRenderer?: () => React.ReactNode;
exportFormatter?: (value: any) => string;
}
const createTableColumns = (groupBy: string, t: TFunction): TableColumn[] => {
const baseColumns: TableColumn[] = [
{
key: 'name',
header: t(`label.${groupBy}Name`),
accessor: (row) => row[groupBy]?.name || '-',
sortable: true,
width: '30%',
cellRenderer: (value) => (
<div className="aa-font-medium aa-text-gray-900">{value}</div>
)
}
];
const metricColumns: TableColumn[] = [
{
key: 'reportCount',
header: t('label.totalReports'),
accessor: (row) => row.totalReportCount || 0,
sortable: true,
align: 'right',
cellRenderer: (value) => (
<div className={getCountCellClass(value)}>{value.toLocaleString()}</div>
)
},
{
key: 'completionRate',
header: t('label.completionRate'),
accessor: (row) => calculateCompletionRate(row),
sortable: true,
align: 'right',
cellRenderer: (value) => (
<div className={getRateCellClass(value)}>{value.toFixed(1)}%</div>
),
exportFormatter: (value) => `${value.toFixed(1)}%`
}
];
return [...baseColumns, ...metricColumns];
};19.2.2 Pagination Implementation
const usePagination = (data: any[], defaultPageSize = 10) => {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(defaultPageSize);
const totalPages = Math.ceil(data.length / pageSize);
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedData = data.slice(startIndex, endIndex);
const goToPage = (page: number) => {
setCurrentPage(Math.max(1, Math.min(page, totalPages)));
};
const changePageSize = (newSize: number) => {
setPageSize(newSize);
setCurrentPage(1); // Reset to first page
};
return {
currentPage,
pageSize,
totalPages,
paginatedData,
goToPage,
changePageSize,
hasNextPage: currentPage < totalPages,
hasPreviousPage: currentPage > 1
};
};19.2.3 Sorting Logic
interface SortConfig {
key: string;
direction: 'asc' | 'desc';
}
const useSorting = (data: any[], columns: TableColumn[]) => {
const [sortConfig, setSortConfig] = useState<SortConfig | null>(null);
const sortedData = useMemo(() => {
if (!sortConfig) return data;
const column = columns.find(col => col.key === sortConfig.key);
if (!column || !column.sortable) return data;
return [...data].sort((a, b) => {
const aValue = column.accessor(a);
const bValue = column.accessor(b);
if (aValue === null || aValue === undefined) return 1;
if (bValue === null || bValue === undefined) return -1;
if (typeof aValue === 'string') {
return sortConfig.direction === 'asc'
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
}
return sortConfig.direction === 'asc'
? aValue - bValue
: bValue - aValue;
});
}, [data, sortConfig, columns]);
const handleSort = (key: string) => {
setSortConfig(current => {
if (!current || current.key !== key) {
return { key, direction: 'asc' };
}
if (current.direction === 'asc') {
return { key, direction: 'desc' };
}
return null; // Remove sorting
});
};
return { sortedData, sortConfig, handleSort };
};19.3 Export Functionality Implementation
19.3.1 Export Service Architecture
class ExportService {
private static instance: ExportService;
private constructor() {}
static getInstance(): ExportService {
if (!ExportService.instance) {
ExportService.instance = new ExportService();
}
return ExportService.instance;
}
async exportToPDF(data: any[], columns: TableColumn[], options: ExportOptions) {
const doc = new jsPDF({
orientation: options.orientation || 'landscape',
unit: 'mm',
format: options.format || 'a4'
});
// Add header
doc.setFontSize(16);
doc.text(options.title || 'Report Insights Export', 14, 15);
// Add metadata
doc.setFontSize(10);
doc.text(`Generated: ${moment().format('YYYY-MM-DD HH:mm:ss')}`, 14, 25);
doc.text(`Records: ${data.length}`, 14, 30);
// Prepare table data
const headers = columns.map(col => col.header);
const rows = data.map(row =>
columns.map(col => {
const value = col.accessor(row);
return col.exportFormatter ? col.exportFormatter(value) : String(value);
})
);
// Generate table
autoTable(doc, {
head: [headers],
body: rows,
startY: 35,
theme: 'grid',
styles: {
fontSize: 8,
cellPadding: 2
},
headStyles: {
fillColor: [75, 85, 99],
textColor: 255
}
});
// Save document
doc.save(`${options.filename || 'report-insights'}.pdf`);
}
async exportToCSV(data: any[], columns: TableColumn[], options: ExportOptions) {
const csvContent = this.generateCSV(data, columns);
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `${options.filename || 'report-insights'}.csv`;
link.click();
}
async exportToExcel(data: any[], columns: TableColumn[], options: ExportOptions) {
const workbook = XLSX.utils.book_new();
// Prepare data
const headers = columns.map(col => col.header);
const rows = data.map(row =>
columns.map(col => {
const value = col.accessor(row);
return col.exportFormatter ? col.exportFormatter(value) : value;
})
);
// Create worksheet
const worksheetData = [headers, ...rows];
const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);
// Apply styles
const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
for (let C = range.s.c; C <= range.e.c; ++C) {
const address = XLSX.utils.encode_col(C) + '1';
if (!worksheet[address]) continue;
worksheet[address].s = {
font: { bold: true },
fill: { fgColor: { rgb: 'FF4B5563' } },
alignment: { horizontal: 'center' }
};
}
// Add worksheet to workbook
XLSX.utils.book_append_sheet(workbook, worksheet, 'Report Insights');
// Save file
XLSX.writeFile(workbook, `${options.filename || 'report-insights'}.xlsx`);
}
private generateCSV(data: any[], columns: TableColumn[]): string {
const headers = columns.map(col => `"${col.header}"`).join(',');
const rows = data.map(row =>
columns.map(col => {
const value = col.accessor(row);
const formatted = col.exportFormatter ? col.exportFormatter(value) : value;
return `"${String(formatted).replace(/"/g, '""')}"`;
}).join(',')
);
return [headers, ...rows].join('\n');
}
}19.4 Advanced Filter Implementation
19.4.1 Filter Validation System
class FilterValidator {
static validateDateRange(startDate: string, endDate: string): ValidationResult {
const start = moment(startDate);
const end = moment(endDate);
if (!start.isValid() || !end.isValid()) {
return {
valid: false,
error: 'Invalid date format'
};
}
if (start.isAfter(end)) {
return {
valid: false,
error: 'Start date must be before end date'
};
}
const daysDiff = end.diff(start, 'days');
if (daysDiff > 365) {
return {
valid: false,
error: 'Date range cannot exceed 365 days'
};
}
return { valid: true };
}
static validateArrayFilter(values: string[], options: any[]): ValidationResult {
const validValues = options.map(opt => opt.value);
const invalidValues = values.filter(val => !validValues.includes(val));
if (invalidValues.length > 0) {
return {
valid: false,
error: `Invalid values: ${invalidValues.join(', ')}`
};
}
return { valid: true };
}
}19.4.2 Filter Persistence Manager
class FilterPersistenceManager {
private static STORAGE_KEY = 'dashboard_filters';
static async saveFilters(dashboardType: string, filters: any): Promise<void> {
try {
const existingFilters = await this.loadAllFilters();
existingFilters[dashboardType] = {
...filters,
savedAt: new Date().toISOString()
};
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(existingFilters));
// Also save to backend for cross-device sync
await api.post('/user/preferences/filters', {
dashboardType,
filters
});
} catch (error) {
console.error('Failed to save filters:', error);
throw error;
}
}
static async loadFilters(dashboardType: string): Promise<any> {
try {
// Try to load from backend first
const response = await api.get(`/user/preferences/filters/${dashboardType}`);
if (response.data) {
return response.data.filters;
}
} catch (error) {
// Fallback to local storage
const storedFilters = this.loadAllFilters();
return storedFilters[dashboardType] || null;
}
}
private static loadAllFilters(): Record<string, any> {
const stored = localStorage.getItem(this.STORAGE_KEY);
return stored ? JSON.parse(stored) : {};
}
}19.5 Performance Optimization Techniques
19.5.1 React.memo Implementation
const ReportChartMemo = React.memo(ReportChart, (prevProps, nextProps) => {
// Custom comparison function
return (
prevProps.data === nextProps.data &&
prevProps.chartType === nextProps.chartType &&
prevProps.width === nextProps.width &&
prevProps.height === nextProps.height
);
});
// Usage with useMemo for data transformation
const ReportStatusOptimized = () => {
const rawData = useSelector(state => state.reportInsights.reportStatus.basicData);
const chartData = useMemo(() => {
if (!rawData) return [];
return Object.entries(rawData).map(([status, count]) => ({
name: status,
value: count,
percentage: (count / totalCount) * 100
}));
}, [rawData]);
return <ReportChartMemo data={chartData} chartType="doughnut" />;
};19.5.2 Virtual Scrolling for Large Tables
const VirtualizedTable = ({ data, columns, rowHeight = 50 }) => {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
const visibleRange = useMemo(() => {
const container = containerRef.current;
if (!container) return { start: 0, end: 10 };
const containerHeight = container.clientHeight;
const start = Math.floor(scrollTop / rowHeight);
const visibleCount = Math.ceil(containerHeight / rowHeight);
const end = Math.min(start + visibleCount + 1, data.length);
return { start, end };
}, [scrollTop, rowHeight, data.length]);
const visibleData = data.slice(visibleRange.start, visibleRange.end);
const totalHeight = data.length * rowHeight;
const offsetY = visibleRange.start * rowHeight;
return (
<div
ref={containerRef}
className="aa-relative aa-overflow-auto"
onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}
>
<div style={{ height: totalHeight }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleData.map((row, index) => (
<TableRow
key={visibleRange.start + index}
data={row}
columns={columns}
style={{ height: rowHeight }}
/>
))}
</div>
</div>
</div>
);
};19.5.3 Debounced Filter Application
const useDebouncedFilter = (onChange: (filters: any) => void, delay = 300) => {
const timeoutRef = useRef<NodeJS.Timeout>();
const debouncedOnChange = useCallback((filters: any) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
onChange(filters);
}, delay);
}, [onChange, delay]);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
return debouncedOnChange;
};19.6 Responsive Design Implementation
19.6.1 Breakpoint Management
const BREAKPOINTS = {
mobile: 500,
tablet: 768,
desktop: 1024,
wide: 1280,
ultrawide: 1920
};
const useBreakpoint = () => {
const [breakpoint, setBreakpoint] = useState<string>('desktop');
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
if (width < BREAKPOINTS.mobile) {
setBreakpoint('mobile');
} else if (width < BREAKPOINTS.tablet) {
setBreakpoint('tablet');
} else if (width < BREAKPOINTS.desktop) {
setBreakpoint('desktop');
} else if (width < BREAKPOINTS.wide) {
setBreakpoint('wide');
} else {
setBreakpoint('ultrawide');
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return breakpoint;
};19.6.2 Adaptive Component Rendering
const AdaptiveReportInsights = () => {
const breakpoint = useBreakpoint();
const renderContent = () => {
switch (breakpoint) {
case 'mobile':
return <MobileReportInsights />;
case 'tablet':
return <TabletReportInsights />;
default:
return <DesktopReportInsights />;
}
};
return renderContent();
};19.7 Testing Implementation
19.7.1 Unit Test Examples
describe('ReportChart Component', () => {
const mockData = [
{ name: 'Completed', value: 100, color: '#10B981' },
{ name: 'Pending', value: 50, color: '#F59E0B' },
{ name: 'Missed', value: 25, color: '#EF4444' }
];
it('should render chart with correct data', () => {
const { container } = render(
<ReportChart data={mockData} chartType="doughnut" />
);
const canvas = container.querySelector('canvas');
expect(canvas).toBeInTheDocument();
});
it('should handle click events', () => {
const onChartClick = jest.fn();
render(
<ReportChart
data={mockData}
chartType="doughnut"
onChartClick={onChartClick}
/>
);
// Simulate chart click
const chart = screen.getByTestId('report-chart');
fireEvent.click(chart);
expect(onChartClick).toHaveBeenCalled();
});
it('should display legend when enabled', () => {
render(
<ReportChart
data={mockData}
chartType="doughnut"
showLegend={true}
/>
);
mockData.forEach(item => {
expect(screen.getByText(item.name)).toBeInTheDocument();
});
});
});19.7.2 Integration Test Examples
describe('Report Insights Dashboard Integration', () => {
let store: MockStore;
beforeEach(() => {
store = mockStore({
reportInsights: {
filters: DEFAULT_FILTERS,
reportStatus: {
isLoading: false,
basicData: {
completed: 100,
pending: 50,
missed: 25
}
}
}
});
});
it('should load data on mount', async () => {
render(
<Provider store={store}>
<ReportInsightsPage />
</Provider>
);
await waitFor(() => {
const actions = store.getActions();
expect(actions).toContainEqual(
expect.objectContaining({
type: 'FETCH_REPORT_DATA_REQUEST'
})
);
});
});
it('should update data on filter change', async () => {
const { getByRole } = render(
<Provider store={store}>
<ReportInsightsPage />
</Provider>
);
// Open filters
fireEvent.click(getByRole('button', { name: /filters/i }));
// Change date
const dateInput = getByRole('textbox', { name: /start date/i });
fireEvent.change(dateInput, { target: { value: '2025-01-01' } });
// Apply filters
fireEvent.click(getByRole('button', { name: /apply/i }));
await waitFor(() => {
expect(store.getActions()).toContainEqual(
expect.objectContaining({
type: 'SET_FILTERS',
payload: expect.objectContaining({
startDate: '2025-01-01'
})
})
);
});
});
});19.7.3 Performance Test Examples
describe('Report Insights Performance', () => {
it('should render large datasets efficiently', () => {
const largeDataset = Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `Report ${i}`,
status: i % 3 === 0 ? 'completed' : i % 3 === 1 ? 'pending' : 'missed',
count: Math.floor(Math.random() * 100)
}));
const startTime = performance.now();
render(<ReportInsightTable data={largeDataset} />);
const endTime = performance.now();
const renderTime = endTime - startTime;
// Should render within 100ms
expect(renderTime).toBeLessThan(100);
});
});19.8 Common Use Cases and Workflows
19.8.1 Daily Report Monitoring Workflow
graph LR A[Login] --> B[Navigate to Report Insights] B --> C[Check Summary Cards] C --> D{High Missed Rate?} D -->|Yes| E[Drill into Report Status] D -->|No| F[Review Completion Times] E --> G[Filter by Problem Areas] F --> H[Identify Slow Reports] G --> I[Take Action] H --> I
19.8.2 Weekly Analysis Workflow
- Set Date Range: Last 7 days
- Review Metrics:
- Total reports submitted vs missed
- Average completion time trends
- Department/Site performance
- Export Data: Generate weekly report
- Action Items: Identify improvement areas
19.8.3 Monthly Executive Reporting
- Aggregate Data: 30-day overview
- Generate Visualizations: Export charts
- Create Summary: Key insights and trends
- Recommendations: Data-driven suggestions
19.9 Troubleshooting Guide
19.9.1 Common Issues and Solutions
| Issue | Symptoms | Solution |
|---|---|---|
| Charts not loading | Empty chart containers | 1. Check data availability 2. Verify Chart.js loaded 3. Check console errors |
| Export timeout | Download doesn’t start | 1. Reduce data range 2. Try different format 3. Check network speed |
| Filter not applying | Data doesn’t update | 1. Click Apply button 2. Check filter validation 3. Verify API response |
| Slow performance | UI lag, delayed response | 1. Reduce date range 2. Use pagination 3. Clear browser cache |
19.9.2 Debug Mode
// Enable debug mode
localStorage.setItem('DEBUG_REPORT_INSIGHTS', 'true');
// Debug logging
if (localStorage.getItem('DEBUG_REPORT_INSIGHTS')) {
console.log('Filter State:', filters);
console.log('API Response:', response);
console.log('Redux State:', store.getState().reportInsights);
}19.10 Migration Guide
19.10.1 Upgrading from Legacy Dashboard
-
Data Migration
- Export existing saved filters
- Map old filter format to new
- Validate migrated data
-
Feature Mapping
- Old feature → New equivalent
- Deprecated features list
- New capabilities overview
-
User Training
- Interactive tutorials
- Video guides
- Documentation links
19.11 API Reference
19.11.1 Endpoints Summary
19.11.1.1 Report Status Data
POST /api/statistics/dashboard/report
Content-Type: application/json
Authorization: Bearer {token}
{
"filters": {
"startDate": "2025-01-01",
"endDate": "2025-01-31",
"siteIDs": ["site1", "site2"],
"reportStatus": ["completed", "pending"]
},
"aggregationType": "status"
}19.11.1.2 Report Summary
POST /api/statistics/dashboard/report/detail
Content-Type: application/json
Authorization: Bearer {token}
{
"filters": {...},
"metrics": ["totalSubmitted", "totalMissed", "avgCompletionTime"]
}19.11.2 Response Formats
19.11.2.1 Success Response
{
"success": true,
"data": {
"completed": 150,
"pending": 75,
"missed": 25,
"total": 250
},
"metadata": {
"generatedAt": "2025-01-06T10:30:00Z",
"filters": {...}
}
}19.11.2.2 Error Response
{
"success": false,
"error": {
"code": "INVALID_DATE_RANGE",
"message": "Date range cannot exceed 365 days",
"details": {...}
}
}19.12 Configuration Options
19.12.1 Environment Variables
// .env configuration
REACT_APP_REPORT_INSIGHTS_API_URL=https://api.nimbly.com
REACT_APP_REPORT_INSIGHTS_TIMEOUT=30000
REACT_APP_REPORT_INSIGHTS_MAX_EXPORT_ROWS=10000
REACT_APP_REPORT_INSIGHTS_CACHE_TTL=30000019.12.2 Feature Flags
const FEATURE_FLAGS = {
ENABLE_REAL_TIME_UPDATES: false,
ENABLE_ADVANCED_FILTERS: true,
ENABLE_BULK_EXPORT: true,
ENABLE_DRILL_DOWN: true,
MAX_FILTER_SELECTIONS: 100
};19.13 Performance Metrics
19.13.1 Key Performance Indicators
- Initial Load Time: < 2 seconds
- Filter Application: < 500ms
- Chart Render: < 100ms
- Export Generation: < 5 seconds
- API Response Time: < 1 second
19.13.2 Monitoring Implementation
const performanceMonitor = {
startTimer: (operation: string) => {
performance.mark(`${operation}-start`);
},
endTimer: (operation: string) => {
performance.mark(`${operation}-end`);
performance.measure(operation, `${operation}-start`, `${operation}-end`);
const measure = performance.getEntriesByName(operation)[0];
// Send to analytics
analytics.track('Performance Metric', {
operation,
duration: measure.duration,
timestamp: Date.now()
});
}
};20. Version History
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2024-01 | Initial release |
| 1.1.0 | 2024-03 | Added drill-down navigation |
| 1.2.0 | 2024-06 | Filter persistence feature |
| 1.3.0 | 2024-09 | Performance optimizations |
| 1.4.0 | 2025-01 | Export enhancements |
Document Version: 1.0
Last Updated: January 2025
Total Characters: ~42,000+
For technical support or feature requests, please contact the development team via:
- GitHub [Issues](../Issue Tracker/IssueTrackerOverview.md): https://github.com/Nimbly-Technologies/audit-admin/issues
- Internal Slack: tech-dashboard-support
- Email: dashboard-support@nimbly.com
Thank you for using the Report Insights Dashboard!