Overview Feature Documentation
Table of Contents
- Feature Overview
- Architecture
- Progress Calculation Methodology
- Component-Specific Calculations
- Data Flow
- API Integration
- UI Components
- State Management
Feature Overview
The Overview feature provides a comprehensive dashboard displaying organizational and individual performance metrics within the Nimbly audit application. It presents completion rates, progress trends, and comparative analytics to help users understand their audit performance over the last 30 days.
Key Metrics Displayed
- Completion Rate: Overall task completion percentage for both individual users and company average
- Completion by Type: Breakdown of completion rates across:
- Schedules (Report-based tasks)
- Issues (Issue resolution tasks)
- LMS (Learning Management System tasks)
- Completion Over Time: Weekly progress trends comparing individual and organizational performance
Default Filters Applied
All metrics in the Overview feature are automatically filtered by:
- Time Period: Last 30 days (rolling window)
- Organization: Current user’s organization
- Active Status: Only active entities (sites, users, departments)
- User Context: When viewing “My Tasks”, filtered by current user ID
Architecture
Frontend Structure
packages/app/features/overview/
├── screen.tsx # Main screen component
├── types.ts # TypeScript definitions
├── state.ts # Jotai atoms for state management
├── utils.ts # Utility functions
├── stylesheet.ts # Styled components
├── controllers/
│ ├── use-overview-controller.ts # Main controller hook
│ └── overview-query.ts # React Query hooks
└── components/
├── RefreshButton.tsx # Manual refresh control
├── CompletionProgressChart.tsx # Doughnut chart component
├── CompletionProgressBar.tsx # Progress bar component
└── CompletionProgressMultipleBarChart.tsx # Weekly comparison chart
Backend Structure (api-statistics)
src/domains/dashboard/issue/
├── usecase/
│ └── dashboard.issue.usecase.ts # Core business logic
└── models/
└── dashboardIssuesResponse.ts # Response types
Progress Calculation Methodology
The health score calculation is the core metric that combines multiple performance indicators. The system calculates scores at different levels with varying formulas based on the grouping context.
Core Metrics
1. RCR (Report Completion Rate)
- Definition: Percentage of scheduled reports completed on time
- Calculation:
(Completed Reports / Total Scheduled Reports) × 100 - Data Source: Schedule statistics from completed audits/inspections
2. IRR (Issue Resolution Rate)
- Definition: Percentage of issues that have been resolved
- Calculation:
(Resolved Issues / Total Issues) × 100 - Data Source: Issue tracking system
3. Issue Raised Percentage
- Definition: Percentage of reports that resulted in issues being raised
- Calculation:
(Reports with Issues / Total Reports) × 100 - Note: Lower percentages indicate better performance
4. LMS Score
- Current Status: Always returns 0 (feature not yet implemented)
- Future: Will track learning/training completion rates
Health Score Formulas
The health score calculation varies based on the grouping level:
Site/Department Level
healthScore = (0.5 × RCR) + (0.25 × (100 - issueRaisedPercentage)) + (0.25 × IRR)
Where:
- 50% weight: Report Completion Rate
- 25% weight: Low issue generation (inverse of issue raised percentage)
- 25% weight: Issue Resolution Rate
Breakdown:
- reportBasedScore = 0.5 × RCR
- issueBasedScore = 0.25 × (100 - issueRaisedPercentage) + 0.25 × IRRCategory Level
healthScore = (0.6 × IRR) + (0.4 × (100 - issueRaisedPercentage))
Where:
- 60% weight: Issue Resolution Rate
- 40% weight: Low issue generation
- Report completion is not considered at category level
Breakdown:
- reportBasedScore = 0
- issueBasedScore = 0.6 × IRR + 0.4 × (100 - issueRaisedPercentage)User Level
healthScore = (0.6 × RCR) + (0.2 × issueAllocated) + (0.2 × resolvedOnTime)
Where:
- 60% weight: Report Completion Rate
- 20% weight: Issue allocation efficiency
- 20% weight: On-time resolution percentage
Additional Calculations:
- issueAllocated = Normalized value based on min/max issues assigned
- resolvedOnTime = Percentage of issues resolved within SLA
Breakdown:
- reportBasedScore = 0.6 × RCR
- issueBasedScore = 0.2 × issueAllocated + 0.2 × resolvedOnTimePeer Comparison
The system calculates peer scores using the same formulas but with data from comparable organizations. This enables benchmarking:
value = |healthScore - healthScorePeers|
moreThanPeers = healthScore > healthScorePeersColor Coding Logic
Progress indicators use color coding based on performance thresholds:
getColor(progress) {
if (progress <= 20) return red; // Critical
if (progress <= 50) return yellow; // Warning
if (progress > 50) return green; // Good
}Data Flow
1. Initial Load
graph LR A[Screen Mount] --> B[Controller Hook] B --> C{Persisted Data?} C -->|No| D[Fetch All Metrics] C -->|Yes| E[Use Cached Data] D --> F[Update Atoms] F --> G[Render UI] E --> G
2. Data Fetching Process
-
Payload Creation: Uses
createPayload()to build request with:- Date range (last 30 days)
- Metric type (‘health’)
- Group by parameter (‘user’ or organization level)
- User/Site/Department filters
-
Parallel API Calls:
- Organization level score
- User level score
- User weekly progress data
- Organization weekly progress data
-
Data Persistence: Results stored in Jotai atoms for state management
3. Manual Refresh
- User clicks refresh button
- All four API endpoints called simultaneously
- Last updated timestamp recorded
- UI updates with new data
API Integration
API Endpoints Overview
| API Name | Endpoint | Method | Purpose | Used For | Response Data |
|---|---|---|---|---|---|
| Progress Scores | /v1.0/statistics/dashboard/issue/detail | POST | Fetch health scores and breakdown metrics | Both user-level and organization-level scores | ProgressData object with health scores |
| Weekly Progress | /v1.0/statistics/dashboard/issue/weekly-completion-score | POST | Fetch weekly progress data for trend analysis | Weekly bar chart data | Array of ProgressDataByWeek objects |
API Usage Breakdown
| Component | API Called | Query Parameters | Data Usage |
|---|---|---|---|
| My Tasks (Doughnut) | Progress Scores | userIDs: [currentUser.userID] | healthScore for percentage display |
| Company Average (Doughnut) | Progress Scores | userIDs: [] (all users) | healthScore for organization average |
| Schedules Progress Bar | Progress Scores | userIDs: [currentUser.userID] | reportBasedScore for bar progress |
| Issues Progress Bar | Progress Scores | userIDs: [currentUser.userID] | issueBasedScore for bar progress |
| LMS Progress Bar | Progress Scores | userIDs: [currentUser.userID] | lmsBasedScore (currently always 0) |
| Weekly Bar Chart | Weekly Progress | Both user and org queries | healthScore from each week’s data |
Detailed Endpoints
1. Progress Scores API
- Endpoint:
/v1.0/statistics/dashboard/issue/detail - Method: POST
- Purpose: Fetch health scores and breakdown metrics
- Called By:
useUserLevelScore()hookuseOrgLevelScore()hook
2. Weekly Progress API
- Endpoint:
/v1.0/statistics/dashboard/issue/weekly-completion-score - Method: POST
- Purpose: Fetch weekly progress data for trend analysis
- Called By:
useUserWeeklyProgressData()hookuseOrgWeeklyProgressData()hook
Request Payload Structure
interface OverviewPayload {
metric: string; // 'health'
groupBy: string; // 'user', 'site', 'department', 'category'
startDate: string; // YYYY-MM-DD format
endDate: string; // YYYY-MM-DD format
siteIDs: string[]; // Filter by sites
departmentIDs: string[]; // Filter by departments
userIDs: string[]; // Filter by users (for user-level queries)
questionnaireIDs: string[];
categories: string[];
roles: string[];
}Response Structure
interface ProgressData {
healthScore: number; // Overall health score (0-100)
issueBasedScore: number; // Issue component score
reportBasedScore: number; // Report component score
lmsBasedScore: number; // LMS component (currently 0)
healthScorePeers: number; // Peer average for comparison
moreThanPeers: boolean; // Performance vs peers
value: number; // Absolute difference from peers
}
interface ProgressDataByWeek {
dateRange: {
start: string;
end: string;
};
week: string; // 'Week 1', 'Week 2', etc.
score: ProgressData; // Same structure as above
}UI Components
1. CompletionProgressChart
- Type: Doughnut/Ring chart
- Library: react-native-chart-kit
- Props:
progress: Percentage value (0-100)title: Chart label
- Features:
- Color-coded based on performance
- Shows percentage in center
- Loading state with spinner
2. CompletionProgressBar
- Type: Horizontal progress bar
- Props:
type: Schedule/Issues/LMSprogress: Percentage value
- Features:
- Icon representation for each type
- Color-coded progress indicator
- Percentage display
3. CompletionProgressMultipleBarChart
- Type: Grouped bar chart
- Purpose: Compare weekly trends
- Features:
- Dual bars (User vs Organization)
- 4-week view
- Legend support
- Custom colors for differentiation
4. RefreshButton
- Purpose: Manual data refresh
- Features:
- Shows last updated time
- Loading state during refresh
- Disabled in offline mode
State Management
Jotai Atoms
// Persisted score data
export const userLevelScoreAtom = atom<ProgressData>({});
export const orgLevelScoreAtom = atom<ProgressData>({});
// Weekly progress data
export const userWeeklyProgressDataAtom = atom<ProgressDataByWeek[]>([]);
export const orgWeeklyProgressDataAtom = atom<ProgressDataByWeek[]>([]);Controller Hook Pattern
The useOverviewController hook manages:
- Data Fetching: Coordinates API calls
- State Persistence: Updates Jotai atoms
- Cache Management: Prevents unnecessary fetches
- Offline Support: Handles connectivity states
- Timestamp Management: Tracks last update time
Component-Specific Calculations
1. Completion Rate (Doughnut Charts)
My Tasks (User-Level Health Score)
// Query Filter
const userQuery = {
metric: 'health',
groupBy: 'user',
userIDs: [currentUser.userID],
startDate: '30 days ago',
endDate: 'today',
// Additional filters
siteIDs: [], // Empty = all sites
departmentIDs: [], // Empty = all departments
questionnaireIDs: [], // Empty = all questionnaires
categories: [], // Empty = all categories
roles: [] // Empty = all roles
}
// Calculation Formula (User Level)
healthScore = (0.6 × RCR) + (0.2 × issueAllocationEfficiency) + (0.2 × onTimeResolution)
Where:
- RCR = Report Completion Rate for user's assigned tasks
- issueAllocationEfficiency = Normalized issue allocation score
- onTimeResolution = Percentage of issues resolved within SLACompany Average (Organization-Level Health Score)
// Query Filter
const orgQuery = {
metric: 'health',
groupBy: 'user',
userIDs: [], // Empty = all users in organization
startDate: '30 days ago',
endDate: 'today',
// No user-specific filtering
siteIDs: [],
departmentIDs: [],
questionnaireIDs: [],
categories: [],
roles: [],
};
// Calculation aggregates all users in the organization
// Uses same formula but with organization-wide data2. Completion by Type (Progress Bars)
Schedules (Report-Based Score)
// Data Source: Schedule Statistics
const scheduleQuery = {
// Filters completed vs scheduled reports
doneBy: { $ne: [] }, // Reports marked as complete
organizationID: currentUser.organizationID,
userID: currentUser.userID, // For "My Tasks"
dateRange: last30Days
}
// Calculation
reportBasedScore = {
user: 0.6 × RCR, // 60% weight for user level
site: 0.5 × RCR, // 50% weight for site level
department: 0.5 × RCR, // 50% weight for department level
category: 0 // Not applicable for category level
}
// Display Value
scheduleProgress = reportBasedScore (directly from health score calculation)Issues (Issue-Based Score)
// Data Source: Issue Statistics
const issueQuery = {
organizationID: currentUser.organizationID,
assignedTo: currentUser.userID, // For "My Tasks"
dateRange: last30Days,
status: ['open', 'in_progress', 'in_review', 'blocked', 'resolved']
}
// Calculation Components
const resolvedIssues = issues.filter(issue => issue.status === 'resolved')
const totalIssues = issues.length
const IRR = (resolvedIssues.length / totalIssues) × 100
const issueRaisedPercentage = (reportsWithIssues / totalReports) × 100
// Final Calculation
issueBasedScore = {
user: 0.2 × issueAllocation + 0.2 × onTimeResolution,
site: 0.25 × (100 - issueRaisedPercentage) + 0.25 × IRR,
department: 0.25 × (100 - issueRaisedPercentage) + 0.25 × IRR,
category: 0.6 × IRR + 0.4 × (100 - issueRaisedPercentage)
}
// Display Value
issueProgress = issueBasedScore (from health score breakdown)LMS (Learning Management System)
// Current Implementation
lmsBasedScore = 0; // Feature not yet implemented
// Future Implementation (Planned)
const lmsQuery = {
organizationID: currentUser.organizationID,
userID: currentUser.userID,
dateRange: last30Days,
completionStatus: ['completed', 'in_progress', 'not_started'],
};
// Planned Calculation
// lmsProgress = (completedCourses / assignedCourses) × 1003. Completion Over Time (Weekly Bar Chart)
My Tasks vs Overall Comparison
// Data Processing
const weeklyRanges = getWeeklyRanges(startDate, endDate); // 4 weeks
const userWeeklyData = [];
const orgWeeklyData = [];
// For each week
weeklyRanges.forEach((week, index) => {
// User Data Query
const userWeekQuery = {
...baseQuery,
userIDs: [currentUser.userID],
startDate: week.start,
endDate: week.end,
};
// Organization Data Query
const orgWeekQuery = {
...baseQuery,
userIDs: [], // All users
startDate: week.start,
endDate: week.end,
};
// Store weekly health scores
userWeeklyData[index] = getUserHealthScore(userWeekQuery);
orgWeeklyData[index] = getOrgHealthScore(orgWeekQuery);
});
// Chart Data Mapping
const chartData = userWeeklyData.map((userWeek, index) => [
{
value: userWeek.healthScore,
frontColor: '#63cfca', // Teal for user
gradientColor: '#009FFF',
spacing: 6,
label: `Week ${index + 1}`,
},
{
value: orgWeeklyData[index].healthScore,
frontColor: '#FFB74D', // Orange for organization
gradientColor: '#fff64d',
},
]);4. Peer Comparison Logic
Peer Selection Filters
const peerQuery = {
// Same industry/sector
industry: currentOrg.industry,
sector: currentOrg.sector,
// Similar organization size (±20% employee count)
employeeCount: {
$gte: currentOrg.employeeCount * 0.8,
$lte: currentOrg.employeeCount * 1.2,
},
// Active organizations only
status: 'active',
// Exclude current organization
organizationID: { $ne: currentUser.organizationID },
};
// Peer Score Calculation
const peerHealthScore = calculateAverageHealthScore(peerOrganizations);
const comparison = {
value: Math.abs(currentHealthScore - peerHealthScore),
moreThanPeers: currentHealthScore > peerHealthScore,
};5. Color Coding and Thresholds
// Performance Thresholds
const getProgressColor = (score) => {
if (score <= 20) return '#DC3545'; // Red - Critical
if (score <= 50) return '#FFC107'; // Yellow - Warning
if (score > 50) return '#28A745'; // Green - Good
};
// Applied to all progress indicators:
// - Doughnut chart colors
// - Progress bar colors
// - Weekly trend indicators6. Data Refresh and Caching
Automatic Filters Applied on Refresh
const refreshFilters = {
// Time window always updates to current rolling 30 days
startDate: dayjs().subtract(30, 'days').format('YYYY-MM-DD'),
endDate: dayjs().format('YYYY-MM-DD'),
// User context maintained
organizationID: currentUser.organizationID,
userIDs: [currentUser.userID], // For user-specific queries
// No manual filters - Overview uses default organizational scope
siteIDs: [], // All sites in organization
departmentIDs: [], // All departments in organization
questionnaireIDs: [], // All questionnaires
categories: [], // All categories
roles: [], // All roles
};Optimization Strategies
- Conditional Fetching: Only fetches when on overview screen
- Data Persistence: Caches results to prevent redundant API calls
- Parallel Requests: All metrics fetched simultaneously
- Offline Mode: Uses cached data when offline
Performance Considerations
Data Caching
- Results persisted in Jotai atoms
- Survives navigation between tabs
- Manual refresh available for updates
Network Optimization
- Batch API calls in parallel
- Network mode set to ‘online’ only
- Error handling with user feedback
UI Responsiveness
- Loading states for each component
- Progressive data display
- Smooth animations in charts
Usage Example
// In a React Native screen
import OverviewScreen from 'app/features/overview/screen';
// The screen automatically:
// 1. Fetches data on mount (if needed)
// 2. Displays cached data immediately
// 3. Updates UI when new data arrives
// 4. Handles refresh requests
// 5. Manages offline states
<OverviewScreen />