Overview
The Non-Operational Day Management System is a comprehensive feature within the audit administration platform that enables organizations to manage when auditors, sites, and the entire organization are not operational. The system operates at three distinct levels:
- Organization Level: Global non-operational days that affect all sites and auditors
- Site Level: Site-specific non-operational days that override organization settings
- User Level: Individual auditor non-operational day assignments
Key Features
- Multi-Level Hierarchy: Organization → Site → User level off-days management
- Dashboard Analytics: Comprehensive metrics and visualization for non-operational patterns
- Bulk Operations: Mass assignment and management of off-days
- Calendar Integration: Visual calendar interface for date selection and management
- Real-time Updates: Live synchronization across all interfaces
- Permission-based Access: Role-based access control for different management levels
Architecture
The Non-Operational Day Management System follows a sophisticated multi-layered architecture designed to handle complex organizational hierarchies and real-time data synchronization:
System Architecture Overview
┌─────────────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
├─────────────────┬─────────────────┬─────────────────────────────────┤
│ Organization │ Site Level │ Auditor Level │
│ Level │ │ │
├─────────────────┼─────────────────┼─────────────────────────────────┤
│ • Firebase │ • Redux State │ • REST API │
│ Connection │ Management │ Integration │
│ • Direct │ • Custom Hooks │ • Complex Data │
│ Firestore │ • Saga │ Transformation │
│ Updates │ Middleware │ • User Assignment │
└─────────────────┴─────────────────┴─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ BUSINESS LOGIC LAYER │
├─────────────────────────────────────────────────────────────────────┤
│ • Permission-based Access Control │
│ • Data Hierarchy Management (Organization → Site → User) │
│ • Calendar Integration & Date Processing │
│ • Real-time Synchronization Logic │
│ • Complex Query Parameter Management │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
├─────────────────┬─────────────────┬─────────────────────────────────┤
│ Firestore │ REST APIs │ Local State │
│ Collections │ │ │
├─────────────────┼─────────────────┼─────────────────────────────────┤
│ • Organization │ • User Off-Days │ • Component State │
│ Off-Days │ Management │ • Form State │
│ • Real-time │ • Dashboard │ • UI State │
│ Updates │ Analytics │ • Cache Management │
│ │ • Site Off-Days │ │
└─────────────────┴─────────────────┴─────────────────────────────────┘
Data Flow Architecture
The system implements three distinct data flow patterns based on the operational level:
1. Organization Level Data Flow
[Firestore] ←→ [React-Redux-Firebase] ←→ [OffDaysManager] ←→ [OffDaysDatePicker]
↑ ↑
└─────────────── Real-time Updates ──────────────────────────┘
2. Site Level Data Flow
[REST API] → [Redux Action] → [Saga Middleware] → [Reducer] → [Hook] → [Component]
↑ ↓
└─────────────────── API Calls ←──────────────────────────────────────┘
3. Auditor Level Data Flow
[REST API] ←→ [Service Layer] ←→ [Component State] ←→ [Date Picker Component]
↑ ↓
└── Complex Data Transformation & User Assignment ←──────┘
Key Architectural Components
Frontend Architecture
- React Components: Modular, reusable components with clear separation of concerns
- Styled Components: CSS-in-JS for consistent theming and responsive design
- Redux State Management: Centralized state for site-level operations
- Custom Hooks: Encapsulation of business logic and API interactions
- React-Redux-Firebase: Real-time Firestore integration for organization data
State Management Strategy
- Organization Level: Firebase real-time updates via react-redux-firebase
- Site Level: Redux with saga middleware for complex async operations
- User Level: Local component state with API integration
- Dashboard: Composite state management with multiple data sources
Data Storage Strategy
- Firestore Collections:
organizationOffDay: Organization-wide off-days- Real-time synchronization across all clients
- REST API Endpoints:
/users/off-days: User-specific off-day management/sites/off-days/{siteID}: Site-specific configurations/users/off-days/count: Dashboard metrics/users/off-days/upcoming: Schedule analytics
Permission & Access Control Architecture
graph TB A[User Authentication] --> B{Permission Check} B --> C[Admin Non-Operational Days Access] C --> D{Level Access} D --> E[Organization Level] D --> F[Site Level] D --> G[User Level] E --> H[Full Calendar Management] F --> I[Site Override Capability] G --> J[Individual Assignment]
Component Hierarchy
Layout
├── AdminPage
├── OffDaysManager (Organization)
│ ├── OffDaysHeader
│ └── OffDaysDatePicker (Shared)
├── OffDaysManager (Auditor)
│ ├── OffDaysHeader
│ └── OffDaysDatePicker (Auditor-specific)
├── SiteOffDays
│ ├── Toggle Switch
│ ├── useSiteOffDays Hook
│ └── OffDaysDatePicker (Shared)
└── OffDaysDashboard
├── UserMetrics (75% width)
├── OffDaysTable (75% width)
├── TotalNonOffDays (25% width)
└── UpcomingSchedule (25% width)
Technology Stack Integration
Core Technologies
- React 17+: Component framework with hooks
- TypeScript: Type safety and development tooling
- Styled Components: CSS-in-JS styling solution
- Redux Toolkit: State management with standardized patterns
- Redux-Saga: Side effect management for complex async flows
UI/UX Libraries
- React-Dates: Advanced calendar picker with customization
- Chart.js: Data visualization for analytics dashboard
- React-Select: Advanced select components with search/filter
- @nimbly-technologies/audit-component: Internal component library
Data & API Integration
- Firebase SDK: Firestore integration and authentication
- React-Redux-Firebase: Firebase-Redux binding
- Fetch API: REST endpoint communication
- @nimbly-technologies/nimbly-common: Shared type definitions
Scalability & Performance Considerations
Optimization Strategies
- Lazy Loading: Code splitting for route-based components
- Memoization: React.memo and useMemo for expensive computations
- Debounced Search: Reduced API calls for search functionality
- Pagination: Table data loaded in chunks of 10 items
- Responsive Calendars: Dynamic month count based on screen size
Data Management Efficiency
- Real-time Updates: Only for organization-level changes requiring immediate sync
- Cached Responses: Component-level caching for frequently accessed data
- Optimistic Updates: UI updates before API confirmation
- Selective Rendering: Custom cell renderers for table performance
Core Components
The Non-Operational Day Management System is built using a sophisticated component architecture that enables multi-level management across organization, site, and user levels.
Organization Level Components
OffDaysManager (Organization)
File: src/components/offdays/OffDaysManager.tsx
The organization-level off-days manager provides comprehensive calendar management for organization-wide non-operational days.
Key Features:
- Real-time Firestore Integration: Direct connection to Firebase for immediate synchronization
- Organization-wide Scope: Changes affect all sites and users unless overridden
- Redux Integration: Connected to Firebase state via react-redux-firebase
- Permission Validation: Checks
userAccess.admin['non-operational-days'].all.permissions
Technical Implementation:
interface OffDays {
organization: string;
siteKey?: string;
dates: Dates; // { [date: string]: boolean }
}State Management:
- Uses
firestoreConnectfor real-time data binding - Firestore collection:
organizationOffDay - Local state for temporary changes before save
- Optimistic UI updates with error handling
Business Logic:
- Date management with moment.js integration
- Validation of date selections
- Bulk save operations with transaction support
- Error monitoring with Monitoring.logEvent
Site Level Components
SiteOffDays Component
File: src/components/sites/SiteOffDays/SiteOffDays.tsx
Site-specific off-days management that can override organization settings.
Key Features:
- Toggle-based Enable/Disable: Visual slider to activate site-specific off-days
- Organization Override: When enabled, site off-days take precedence
- Visual Inheritance: Shows organization off-days as dots on the calendar
- Redux-Saga Integration: Complex async state management
Component Structure:
const SiteOffDays = ({ organizationOffDay, siteKey }) => {
const [
{ isFetchLoading, isUpsertLoading, siteOffDays },
{ handleOffDayToggle, handleChangeOffDay, handleSubmitOffDay }
] = useSiteOffDays({
siteID: siteKey,
onFetchError, onUpsertError, onUpsertSuccess
});
}useSiteOffDays Custom Hook
File: src/components/sites/SiteOffDays/hook/useSiteOffDays.ts
Encapsulates all site off-days business logic and state management.
Hook Features:
- Loading State Management: Separate states for fetch and upsert operations
- Error Handling: Comprehensive error callbacks with i18n support
- Data Transformation: Converts array format to boolean object mapping
- Redux Integration: Dispatches actions and manages saga lifecycle
Return Interface:
type UseSiteOffDaysReturnType = [
{
isFetchLoading: boolean;
isUpsertLoading: boolean;
siteOffDays: ProcessedSiteOffDays;
},
{
handleOffDayToggle: () => void;
handleChangeOffDay: (newOffDays: OffDays) => void;
handleSubmitOffDay: () => void;
}
];User Level Components
OffDaysManager (Auditor)
File: src/components/auditors/offDaysManager/OffDaysManager.tsx
Manages individual auditor off-day assignments with complex user-date mapping.
Key Features:
- Multi-User Assignment: Multiple users can be assigned to the same date
- Complex Data Transformation: Converts between date-user and user-date mappings
- REST API Integration: Uses
/users/off-daysendpoint - Differential Updates: Tracks added and removed users for efficient API calls
Data Transformation Logic:
// Frontend Format: { [date]: [userID1, userID2] }
const frontendFormat = {
'2024-01-15': ['user1', 'user2'],
'2024-01-16': ['user3']
};
// API Format: [{ user1: ['2024-01-15'] }, { user2: ['2024-01-15'] }]
const apiFormat = [
{ user1: ['2024-01-15'] },
{ user2: ['2024-01-15'] },
{ user3: ['2024-01-16'] }
];API Integration:
- Fetch:
GET /users/off-days- Retrieves current assignments - Save:
POST /users/off-days- Updates assignments with differential logic - Error Handling: Comprehensive error states with monitoring
OffDaysDatePicker (Auditor-specific)
File: src/components/auditors/offDaysManager/offDaysDatePicker.tsx
Specialized calendar picker for auditor assignments with user management interface.
Features:
- User Selection Interface: Shows assigned user counts per date
- Multi-User Support: Assign multiple auditors to single dates
- Visual Indicators: Different styling for assigned vs available dates
- User Management: Add/remove users from specific dates
Shared Components
OffDaysDatePicker (Shared)
File: src/components/global/OffDaysDatePicker/OffDaysDatePicker.tsx
Highly sophisticated calendar component used across organization and site levels.
Advanced Features:
- Responsive Design: Dynamic month count (1-2) based on screen width
- Year Navigation: Select dropdown with range 2019-2025
- Dual View: Calendar grid + selected dates list
- Organization Inheritance: Shows org off-days as dots on site calendars
- Custom Styling: Extensive CSS customization for calendar appearance
Technical Specifications:
interface OffDayProps {
type: 'site' | 'organization';
organizationOffDay: OffDays;
siteOffDay?: OffDays;
handleUpdateOffDay: (offDays: OffDays) => void;
}Responsive Behavior:
- Desktop (>1275px): 2-month view
- Tablet (992-1275px): 1-month view
- Mobile (<992px): 2-month view (if >682px), 1-month view (if <682px)
Calendar Customization:
- react-dates Integration: DayPickerSingleDateController
- Custom Navigation: Back/forward arrows with custom styling
- Highlighting Logic: Different states for selected, highlighted, and blocked dates
- Tooltip Support: Hover information for organization off-days
Dashboard Components
OffDaysDashboard
File: src/components/auditors/offDaysDashboard/OffDaysDashboard.tsx
Main dashboard layout with analytics and management capabilities.
Layout Structure:
- 75% Main Area: UserMetrics + OffDaysTable
- 25% Sidebar: TotalNonOffDays + UpcomingSchedule
- Responsive Design: Tailwind CSS classes for consistent layout
UserMetrics Widget
File: src/components/auditors/offDaysDashboard/UserMetrics.tsx
Displays real-time user count metrics across different time periods.
Metrics Displayed:
- Today: Current day off-duty count
- This Week: Weekly off-duty count
- This Month: Monthly off-duty count
- This Year: Annual off-duty count
API Integration:
- Endpoint:
GET /users/off-days/count - Response Format:
UserCountI { today, week, month, year } - Loading State: Skeleton loading with spinner
- Error Handling: Graceful fallback to zero values
OffDaysTable Component
File: src/components/auditors/offDaysDashboard/OffDaysTable.tsx
Advanced data table with dual view modes and comprehensive filtering.
View Modes:
- User-wise View: Groups by user, shows their off-days
- Date-wise View: Groups by date, shows affected users
Advanced Features:
- Segmented View Toggle: Switch between user-wise and date-wise views
- Advanced Filtering: Multi-select filters for users, sites, departments, date ranges
- Calendar Integration: Date picker for date-wise view with visual indicators
- Search Functionality: Debounced search with 500ms delay
- Pagination: 10 items per page with navigation controls
- Sorting: Multi-column sorting with visual indicators
Filter Configuration:
const filterConfigs: FilterConfig[] = [
{ name: 'user', type: FilterFieldType.MULTI_SELECT },
{ name: 'date', type: FilterFieldType.DATE_RANGE_PRESET },
{ name: 'sites', type: FilterFieldType.MULTI_SELECT },
{ name: 'departments', type: FilterFieldType.MULTI_SELECT }
];Integration Points:
- Issue Tracking: Links to issues page with date/user filters
- Schedule Details: Modal views for schedule information
- User Management: Navigation to user off-days management
TotalNonOffDays Chart
File: src/components/auditors/offDaysDashboard/TotalNonOffDays.tsx
Interactive chart displaying monthly non-operational day trends.
Chart Features:
- Chart.js Integration: Line chart with gradient fill
- Month Selection: Dropdown for 2020-2026 range
- Gradient Styling: Purple gradient matching brand colors
- Responsive Design: Maintains aspect ratio across devices
API Integration:
- Endpoint:
GET /users/off-days/userOffDaysPerMonth - Query Parameter:
yearMonth(YYYY-MM format) - Data Processing: Sorts dates and formats for chart display
UpcomingSchedule Widget
File: src/components/auditors/offDaysDashboard/UpcomingSchedule.tsx
Displays upcoming scheduled off-days categorized by time periods.
Categories:
- Today: Users scheduled off today
- This Week: Users scheduled off this week
- Next Week: Users scheduled off next week (labeled as “This Month”)
UI Elements:
- Profile Icons: Visual user representation
- Scrollable List: Accommodates long user lists
- Empty State: “No upcoming schedules” message
API Integration:
- Endpoint:
GET /users/off-days/upcoming - Response:
UpcomingScheduleI { today[], thisWeek[], nextWeek[] }
Modal Components
ScheduleListModal & ScheduleDetailModal
Files:
src/components/auditors/offDaysDashboard/ScheduleListModal.tsxsrc/components/auditors/offDaysDashboard/ScheduleDetailModal.tsx
Purpose: Provide detailed schedule information with drill-down capability.
Navigation Flow:
- User clicks schedule count in table
- ScheduleListModal opens with schedule list
- User selects specific schedule
- ScheduleDetailModal opens with detailed information
- User can navigate back to list or close entirely
Component Integration Patterns
Shared State Management
// Organization Level - Firebase Real-time
const organizationOffDay = getVal(state.firestore.data, '@organizationManagerOrganizationOffDay');
// Site Level - Redux State
const { siteOffDays } = useSelector((state: RootState) => state.siteOffDays);
// User Level - Local State
const [tempOrganizationOffDay, setTempOrganizationOffDay] = useState<Dates>({});Error Handling Patterns
- Firestore: Uses
isLoaded()checks and loading states - REST API: Try-catch blocks with Monitoring.logEvent
- Redux: Error states in reducers with saga error handling
- User Feedback: Toast notifications for all operations
Performance Optimization
- Code Splitting: Lazy loading for all page components
- Memoization: useMemo for expensive table data calculations
- Debouncing: Search inputs to reduce API calls
- Selective Rendering: Custom table cell renderers for efficiency
API Endpoints
The Non-Operational Day Management System integrates with multiple API layers and data storage systems. This section provides comprehensive documentation of all endpoints, their implementations, and integration patterns.
User Off-Days API
GET /users/off-days
Purpose: Retrieve current user off-day assignments
Service File: src/services/auditorOffDays/auditorOffDays.service.ts
Component Usage: src/components/auditors/offDaysManager/OffDaysManager.tsx:41
Authentication: Firebase Token Required
Method: GET
Headers:
{
Authorization: string // Firebase JWT token
}Response Format:
{
data: {
userIDs: Array<{ [userID: string]: string[] }> // User ID mapped to array of dates
}
}Response Example:
{
"data": {
"userIDs": [
{ "user123": ["2024-01-15", "2024-01-16"] },
{ "user456": ["2024-01-15", "2024-01-20"] }
]
}
}Business Logic:
- Fetches all user assignments across all dates
- Used for initial load and refresh operations
- Data transformation occurs client-side to convert to date-user mapping
POST /users/off-days
Purpose: Update user off-day assignments with differential logic
Service File: src/services/auditorOffDays/auditorOffDays.service.ts
Component Usage: src/components/auditors/offDaysManager/OffDaysManager.tsx:79
Authentication: Firebase Token Required
Method: POST
Headers:
{
Authorization: string,
'Content-Type': 'application/json'
}Request Body:
{
userIDs: Array<{ [userID: string]: string[] }> // Complete user assignments
}Request Example:
{
"userIDs": [
{ "user123": ["2024-01-15", "2024-01-16"] },
{ "user456": ["2024-01-15"] },
{ "user789": [] } // Empty array removes all assignments
]
}Complex Business Logic:
- Differential Updates: Compares current state vs new state
- Removal Logic: Empty arrays signal complete removal of user assignments
- Bulk Operations: Handles multiple user updates in single request
- Data Transformation: Converts from date-user mapping to user-date mapping
Error Handling:
- HTTP 200: Success with data refresh
- Other status codes: Error toast with monitoring log
GET /users/off-days/count
Purpose: Retrieve user count metrics for dashboard
Service File: src/services/auditorOffDays/auditorOffDays.service.ts:11
Component Usage: src/components/auditors/offDaysDashboard/UserMetrics.tsx:22
Authentication: Firebase Token Required
Method: GET
Headers:
{
Authorization: string
}Response Format:
interface UserCountI {
today: number;
week: number;
month: number;
year: number;
}Response Example:
{
"data": {
"today": 5,
"week": 12,
"month": 45,
"year": 150
}
}Metrics Calculation:
- Today: Users with off-days on current date
- Week: Users with off-days in current week
- Month: Users with off-days in current month
- Year: Users with off-days in current year
GET /users/off-days/upcoming
Purpose: Retrieve upcoming scheduled off-days for dashboard widget
Service File: src/services/auditorOffDays/auditorOffDays.service.ts:32
Component Usage: src/components/auditors/offDaysDashboard/UpcomingSchedule.tsx:24
Authentication: Firebase Token Required
Method: GET
Response Format:
interface UpcomingScheduleI {
today: string[]; // User names
thisWeek: string[]; // User names
nextWeek: string[]; // User names (labeled as "This Month" in UI)
}Response Example:
{
"data": {
"today": ["John Doe", "Jane Smith"],
"thisWeek": ["Mike Johnson", "Sarah Wilson"],
"nextWeek": ["David Brown", "Lisa Davis"]
}
}Business Logic:
- Returns user names (not IDs) for display
- Categorizes by time periods for dashboard organization
- Used for upcoming schedule widget in dashboard sidebar
GET /users/off-days/userOffDaysPerMonth
Purpose: Retrieve monthly non-operational day statistics for chart visualization
Service File: src/services/auditorOffDays/auditorOffDays.service.ts:53
Component Usage: src/components/auditors/offDaysDashboard/TotalNonOffDays.tsx:84
Authentication: Firebase Token Required
Method: GET
Query Parameters:
{
yearMonth: string // Format: "YYYY-MM"
}Request Example:
GET /users/off-days/userOffDaysPerMonth?yearMonth=2024-01
Response Format:
interface MonthlyNonOpsI {
[date: string]: number; // Date (YYYY-MM-DD) mapped to user count
}Response Example:
{
"data": {
"2024-01-01": 3,
"2024-01-02": 5,
"2024-01-15": 8,
"2024-01-31": 2
}
}Chart Integration:
- Data processed for Chart.js line chart
- Dates sorted chronologically for timeline visualization
- Gradient fill styling applied for visual appeal
GET /users/off-days/userOffDaysData
Purpose: Retrieve paginated off-days data for dashboard table
Service File: src/services/auditorOffDays/auditorOffDays.service.ts:77
Component Usage: src/components/auditors/offDaysDashboard/OffDaysTable.tsx:332
Authentication: Firebase Token Required
Method: GET
Query Parameters:
// User-wise View Parameters
{
type: 'user';
department?: string[];
site?: string[];
users?: string[];
startDate?: string; // YYYY-MM-DD
endDate?: string; // YYYY-MM-DD
search?: string;
sortBy?: string;
order?: 'asc' | 'desc';
limit?: number;
page?: number;
}
// Date-wise View Parameters
{
type: 'date';
dates?: string[]; // Array of YYYY-MM-DD dates
sortBy?: string;
order?: 'asc' | 'desc';
limit?: number;
page?: number;
}Request Examples:
// User-wise view
GET /users/off-days/userOffDaysData?type=user&search=john&sortBy=name&order=asc&limit=10&page=1
// Date-wise view
GET /users/off-days/userOffDaysData?type=date&dates=2024-01-15,2024-01-16&limit=10&page=1
Response Format:
interface NonOpsDataI {
docs: NonOpsDocsI[];
limit?: number | string;
page?: number | string;
totalDocs?: number;
totalPages?: number;
}
interface NonOpsDocsI {
userID?: string;
name?: string;
data: NonOpsDetailDataI[];
date?: string; // For date-wise view
}
interface NonOpsDetailDataI {
date?: string;
issueCount: number;
scheduleCount: number;
scheduleData: ScheduleDataI[];
userId?: string;
name?: string;
}Advanced Features:
- Dual View Support: User-wise and date-wise grouping
- Complex Filtering: Multi-dimensional filtering capabilities
- Pagination: Server-side pagination with metadata
- Search Integration: Full-text search across user names
- Schedule Integration: Links to schedule and issue data
Site Off-Days API
GET /sites/off-days/{siteID}
Purpose: Retrieve site-specific off-days configuration
Service File: src/services/siteOffDays/siteOffDays.service.ts:19
Component Usage: Via Redux Action src/reducers/site/siteOffDays/siteOffDays.action.ts
Authentication: Firebase Token Required
Method: GET
Path Parameters:
{
siteID: string
}Response Format:
interface SiteOffdayMongo {
organizationID: string;
siteID: string;
dates: string[]; // Array of date strings
disabled: boolean;
createdAt?: Date;
updatedAt?: Date;
}Response Example:
{
"data": {
"organizationID": "org123",
"siteID": "site456",
"dates": ["2024-01-15", "2024-01-16", "2024-01-20"],
"disabled": false,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-10T00:00:00.000Z"
}
}Integration Pattern:
- Called via Redux saga middleware
- Response processed through custom hook
- Data transformation from array to boolean mapping occurs client-side
PUT /sites/off-days/{siteID}
Purpose: Create or update site-specific off-days configuration
Service File: src/services/siteOffDays/siteOffDays.service.ts:6
Component Usage: Via Redux Action src/reducers/site/siteOffDays/siteOffDays.action.ts
Authentication: Firebase Token Required
Method: PUT
Path Parameters:
{
siteID: string
}Request Body:
Partial<SiteOffdayMongo> {
organizationID?: string;
siteID?: string;
dates?: string[];
disabled?: boolean;
}Request Example:
{
"organizationID": "org123",
"siteID": "site456",
"dates": ["2024-01-15", "2024-01-16"],
"disabled": false
}Response Format:
{
data: string; // Success message
}Error Handling:
- Custom Error Class:
SiteOffDaysApiError - Error File:
src/services/siteOffDays/siteOffDays.errors.ts - Error Processing: Automatic error detection and throwing
Business Logic:
- Upsert Operation: Creates new or updates existing configuration
- Toggle Support: Enables/disables site-specific off-days
- Organization Override: When enabled, overrides organization-level settings
Firestore Integration
Organization Off-Days Collection
Collection: organizationOffDay
Document ID: Organization ID
Access Pattern: Real-time subscription via react-redux-firebase
Component Usage: src/components/offdays/OffDaysManager.tsx:142
Document Structure:
{
organization: string;
dates: { [dateString: string]: boolean };
updatedBy: string; // User UID
updatedAt: Timestamp;
}Document Example:
{
"organization": "org123",
"dates": {
"2024-01-15": true,
"2024-01-16": true,
"2024-01-20": true
},
"updatedBy": "user456",
"updatedAt": { "_seconds": 1704153600, "_nanoseconds": 0 }
}Real-time Features:
- Automatic Synchronization: Changes propagate immediately to all connected clients
- Optimistic Updates: UI updates immediately, rolls back on error
- Conflict Resolution: Last-write-wins with timestamp tracking
Security Rules:
- Authentication required
- Permission validation via
userAccess.admin['non-operational-days'].all.permissions
API Integration Patterns
Authentication Flow
// Consistent authentication across all APIs
const token = await API.getFirebaseToken();
const headers = { Authorization: token };Error Handling Strategy
// REST APIs
try {
const response = await fetch(url, options);
if (response.status === 200) {
const data = await response.json();
return data.data;
} else {
throw new Error('API Error');
}
} catch (error) {
Monitoring.logEvent('APIError', error);
return Promise.reject(error);
}
// Firestore
try {
await ref.set(data, { merge: false });
toast.success(t('success.message'));
} catch (error) {
Monitoring.logEvent('FirestoreError', error);
toast.error(t('error.message'));
}Data Transformation Patterns
// API to Frontend (User Off-Days)
const frontendFormat = userIDs.reduce((acc, userObj) => {
Object.entries(userObj).forEach(([userID, dates]) => {
dates.forEach(date => {
if (!acc[date]) acc[date] = [];
acc[date].push(userID);
});
});
return acc;
}, {});
// Frontend to API (User Off-Days)
const apiFormat = Object.entries(frontendData).reduce((acc, [date, userIDs]) => {
userIDs.forEach(userID => {
const existingUser = acc.find(item => item[userID]);
if (existingUser) {
existingUser[userID].push(date);
} else {
acc.push({ [userID]: [date] });
}
});
return acc;
}, []);
// Site Off-Days (Array to Boolean Mapping)
const processedDates = apiDates.reduce((map, date) => {
map[date] = true;
return map;
}, {});API Performance Considerations
Caching Strategy
- Component Level: Local state caching for UI responsiveness
- Request Debouncing: 500ms delay for search operations
- Pagination: 10 items per page to reduce payload size
Request Optimization
- Batch Operations: Single request for multiple user updates
- Differential Updates: Only send changed data
- Query Parameter Validation: Client-side validation before API calls
Error Recovery
- Automatic Retry: Network error retry with exponential backoff
- Graceful Degradation: Fallback to cached data when APIs fail
- User Feedback: Toast notifications for all operation states
Database Schema
The Non-Operational Day Management System utilizes a hybrid data storage approach combining Firestore for real-time organization data and MongoDB for complex relational operations.
Firestore Collections
organizationOffDay Collection
Collection Path: /organizationOffDay/{organizationID}
Purpose: Real-time organization-wide off-days management
Access Pattern: Document per organization with real-time subscriptions
Document Schema:
interface OrganizationOffDay {
organization: string; // Organization identifier
dates: { [date: string]: boolean }; // Date mapping { "2024-01-15": true }
updatedBy: string; // User UID who made the change
updatedAt: Timestamp; // Firestore timestamp
createdAt?: Timestamp; // Optional creation timestamp
}Document Example:
{
"organization": "org_nimbly_123",
"dates": {
"2024-01-15": true,
"2024-01-16": true,
"2024-02-14": true,
"2024-12-25": true
},
"updatedBy": "user_abc123",
"updatedAt": {
"_seconds": 1704153600,
"_nanoseconds": 123456789
},
"createdAt": {
"_seconds": 1704067200,
"_nanoseconds": 0
}
}Indexing Strategy:
- Primary Index: Document ID (organization)
- Compound Index: organization + updatedAt (for audit trails)
- TTL Index: None (permanent data)
Security Rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /organizationOffDay/{organizationId} {
allow read, write: if request.auth != null
&& hasPermission(resource, 'non-operational-days');
}
}
}Real-time Subscriptions:
- Connection Pattern:
react-redux-firebasewith automatic reconnection - Update Strategy: Optimistic updates with rollback on failure
- Conflict Resolution: Last-write-wins with timestamp validation
MongoDB Collections (API Backend)
User Off-Days Collection
Collection: userOffDays
Purpose: Individual user off-day assignments
Database: Audit Admin MongoDB
Document Schema:
interface UserOffDay {
_id: ObjectId; // MongoDB document ID
organizationID: string; // Organization reference
userID: string; // User identifier
dates: string[]; // Array of ISO date strings
createdAt: Date; // Creation timestamp
updatedAt: Date; // Last update timestamp
createdBy: string; // User who created the assignment
updatedBy: string; // User who last updated
}Document Example:
{
"_id": { "$oid": "507f1f77bcf86cd799439011" },
"organizationID": "org_nimbly_123",
"userID": "user_auditor_456",
"dates": [
"2024-01-15",
"2024-01-16",
"2024-02-20"
],
"createdAt": { "$date": "2024-01-01T00:00:00.000Z" },
"updatedAt": { "$date": "2024-01-10T00:00:00.000Z" },
"createdBy": "admin_user_789",
"updatedBy": "admin_user_789"
}Indexing Strategy:
// Primary indexes
db.userOffDays.createIndex({ "organizationID": 1, "userID": 1 }, { unique: true });
db.userOffDays.createIndex({ "organizationID": 1, "dates": 1 });
db.userOffDays.createIndex({ "updatedAt": -1 });
// Compound indexes for dashboard queries
db.userOffDays.createIndex({
"organizationID": 1,
"dates": 1,
"userID": 1
});
// Text search index
db.userOffDays.createIndex({
"userID": "text",
"createdBy": "text",
"updatedBy": "text"
});Aggregation Pipelines:
// User count metrics pipeline
const userCountPipeline = [
{ $match: { organizationID: "org_nimbly_123" } },
{ $unwind: "$dates" },
{ $addFields: {
dateObj: { $dateFromString: { dateString: "$dates" } }
}},
{ $group: {
_id: null,
today: { $sum: { $cond: [
{ $eq: [{ $dayOfYear: "$dateObj" }, { $dayOfYear: new Date() }] },
1, 0
]}},
week: { $sum: { $cond: [
{ $gte: ["$dateObj", startOfWeek] },
1, 0
]}},
month: { $sum: { $cond: [
{ $gte: ["$dateObj", startOfMonth] },
1, 0
]}},
year: { $sum: { $cond: [
{ $gte: ["$dateObj", startOfYear] },
1, 0
]}}
}}
];
// Monthly statistics pipeline
const monthlyStatsPipeline = [
{ $match: {
organizationID: "org_nimbly_123",
dates: { $regex: "^2024-01" }
}},
{ $unwind: "$dates" },
{ $group: {
_id: "$dates",
userCount: { $sum: 1 }
}},
{ $sort: { "_id": 1 } }
];Site Off-Days Collection
Collection: siteOffDays
Purpose: Site-specific off-day configurations
Database: Audit Admin MongoDB
Document Schema:
interface SiteOffDay {
_id: ObjectId; // MongoDB document ID
organizationID: string; // Organization reference
siteID: string; // Site identifier
dates: string[]; // Array of ISO date strings
disabled: boolean; // Enable/disable toggle
createdAt: Date; // Creation timestamp
updatedAt: Date; // Last update timestamp
createdBy: string; // User who created
updatedBy: string; // User who last updated
}Document Example:
{
"_id": { "$oid": "507f1f77bcf86cd799439012" },
"organizationID": "org_nimbly_123",
"siteID": "site_downtown_789",
"dates": [
"2024-01-15",
"2024-03-17",
"2024-07-04"
],
"disabled": false,
"createdAt": { "$date": "2024-01-01T00:00:00.000Z" },
"updatedAt": { "$date": "2024-01-15T00:00:00.000Z" },
"createdBy": "site_admin_456",
"updatedBy": "site_admin_456"
}Indexing Strategy:
// Unique site configuration per organization
db.siteOffDays.createIndex({
"organizationID": 1,
"siteID": 1
}, { unique: true });
// Query optimization indexes
db.siteOffDays.createIndex({ "organizationID": 1, "disabled": 1 });
db.siteOffDays.createIndex({ "updatedAt": -1 });Relational Data Patterns
User Schedule Integration
Purpose: Link off-days with existing schedule and issue data
Pattern: Foreign key relationships via user IDs and dates
interface ScheduleIntegration {
userID: string; // Links to user management system
date: string; // Links to off-days date
scheduleCount: number; // Computed from schedules collection
issueCount: number; // Computed from issues collection
scheduleData: ScheduleDataI[]; // Populated via joins
}Query Pattern Example:
// Complex join operation for dashboard table
const dashboardData = await db.collection('userOffDays').aggregate([
{ $match: { organizationID: orgId } },
{ $unwind: "$dates" },
{ $lookup: {
from: "schedules",
let: { userId: "$userID", date: "$dates" },
pipeline: [
{ $match: {
$expr: {
$and: [
{ $eq: ["$assignedUser", "$$userId"] },
{ $eq: ["$scheduledDate", "$$date"] }
]
}
}}
],
as: "schedules"
}},
{ $lookup: {
from: "issues",
let: { userId: "$userID", date: "$dates" },
pipeline: [
{ $match: {
$expr: {
$and: [
{ $eq: ["$assignedTo", "$$userId"] },
{ $eq: ["$dueDate", "$$date"] }
]
}
}}
],
as: "issues"
}},
{ $group: {
_id: { userID: "$userID", date: "$dates" },
scheduleCount: { $size: "$schedules" },
issueCount: { $size: "$issues" },
scheduleData: { $first: "$schedules" }
}}
]);Data Hierarchy & Inheritance
Organizational Hierarchy
Organization (Firestore)
├── Site Off-Days (MongoDB)
│ ├── Override: disabled = false
│ └── Inherit: disabled = true
└── User Off-Days (MongoDB)
├── Individual assignments
└── Respect site/org rules
Priority Resolution Logic
const getEffectiveOffDays = (date: string, userID: string, siteID: string) => {
// 1. Check user-specific assignment
const userOffDay = await getUserOffDay(userID, date);
if (userOffDay) return true;
// 2. Check site-specific configuration
const siteConfig = await getSiteOffDay(siteID);
if (siteConfig && !siteConfig.disabled && siteConfig.dates.includes(date)) {
return true;
}
// 3. Check organization-wide setting
const orgOffDay = await getOrganizationOffDay(organizationID);
if (orgOffDay && orgOffDay.dates[date]) {
return true;
}
return false;
};Data Consistency & Integrity
Transaction Patterns
// Site off-days update with rollback
const updateSiteOffDays = async (siteID: string, newData: Partial<SiteOffDay>) => {
const session = await mongoose.startSession();
session.startTransaction();
try {
// Update site configuration
await SiteOffDay.findOneAndUpdate(
{ siteID, organizationID },
{ ...newData, updatedAt: new Date() },
{ upsert: true, session }
);
// Log audit trail
await AuditLog.create({
action: 'SITE_OFFDAYS_UPDATE',
targetType: 'SITE',
targetId: siteID,
changes: newData,
userId: currentUser.id
}, { session });
await session.commitTransaction();
return { success: true };
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
};Validation Rules
// MongoDB Schema Validation
const userOffDaySchema = {
$jsonSchema: {
bsonType: "object",
required: ["organizationID", "userID", "dates"],
properties: {
organizationID: {
bsonType: "string",
pattern: "^org_[a-zA-Z0-9_]+$"
},
userID: {
bsonType: "string",
pattern: "^user_[a-zA-Z0-9_]+$"
},
dates: {
bsonType: "array",
items: {
bsonType: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$"
}
},
createdAt: { bsonType: "date" },
updatedAt: { bsonType: "date" }
}
}
};Performance Optimization
Caching Strategy
// Redis caching for frequently accessed data
const cacheKeys = {
userOffDays: (userID: string) => `user_offdays:${userID}`,
siteOffDays: (siteID: string) => `site_offdays:${siteID}`,
orgOffDays: (orgID: string) => `org_offdays:${orgID}`,
userMetrics: (orgID: string) => `metrics:${orgID}:${moment().format('YYYY-MM-DD')}`
};
// Cache with TTL
const cacheUserOffDays = async (userID: string, data: UserOffDay) => {
await redis.setex(
cacheKeys.userOffDays(userID),
3600, // 1 hour TTL
JSON.stringify(data)
);
};Query Optimization
// Optimized dashboard query with pagination
const getDashboardData = async (filters: DashboardFilters) => {
const pipeline = [
// Stage 1: Match filters
{ $match: buildMatchStage(filters) },
// Stage 2: Lookup user information
{ $lookup: {
from: 'users',
localField: 'userID',
foreignField: '_id',
as: 'user',
pipeline: [
{ $project: { name: 1, department: 1, site: 1 } }
]
}},
// Stage 3: Apply search filter
{ $match: buildSearchStage(filters.search) },
// Stage 4: Sort
{ $sort: { [filters.sortBy]: filters.order === 'desc' ? -1 : 1 } },
// Stage 5: Pagination
{ $facet: {
data: [
{ $skip: (filters.page - 1) * filters.limit },
{ $limit: filters.limit }
],
totalCount: [{ $count: "count" }]
}}
];
return await UserOffDay.aggregate(pipeline);
};User Interface
The Non-Operational Day Management System features a sophisticated user interface designed for efficiency, usability, and real-time collaboration across multiple organizational levels.
Navigation & Routing
Route Structure
| Route | Component | Purpose | Access Level |
|---|---|---|---|
/admin/offdays | Organization OffDaysManager | Organization-wide off-days management | Admin |
/admin/auditors/useroffdays | Auditor OffDaysManager | Individual user assignments | Admin |
/admin/auditors/offdays-dashboard | OffDaysDashboard | Analytics and reporting | Admin |
/sites/{siteID}/settings | SiteOffDays (embedded) | Site-specific configuration | Site Admin |
Route Configuration:
// Lazy-loaded route definitions
const AdminRoutes = [
{
path: "/admin/offdays",
component: lazy(() => import("pages/offdays")),
exact: true,
permissions: ['non-operational-days.all']
},
{
path: "/admin/auditors/useroffdays",
component: lazy(() => import("pages/auditorsOffdays")),
exact: true,
permissions: ['non-operational-days.users']
},
{
path: "/admin/auditors/offdays-dashboard",
component: lazy(() => import("pages/auditorsOffDaysDashboard")),
exact: true,
permissions: ['non-operational-days.view']
}
];Sidebar Navigation Integration
File: src/components/global/Sidebar/constants/SubMenuList.constants.ts
const nonOperationalMenuItems = {
key: 'non-operational-days',
icon: CalendarIcon,
label: 'nav.nonOperationalDays',
children: [
{
key: 'organization-offdays',
path: '/admin/offdays',
label: 'nav.organizationOffDays',
permissions: ['non-operational-days.all']
},
{
key: 'user-offdays',
path: '/admin/auditors/useroffdays',
label: 'nav.userOffDays',
permissions: ['non-operational-days.users']
},
{
key: 'offdays-dashboard',
path: '/admin/auditors/offdays-dashboard',
label: 'nav.offDaysDashboard',
permissions: ['non-operational-days.view']
}
]
};Organization Level Interface
Organization Off-Days Management Page
Route: /admin/offdays
Component: src/components/offdays/OffDaysManager.tsx
UI Layout:
┌─────────────────────────────────────────────────────────────┐
│ Page Header │
├─────────────────────────────────────────────────────────────┤
│ Title: "Non-Operational Days" [Save Button] │
│ Subtitle: "Manage organization-wide..." │
├─────────────────────────────────────────────────────────────┤
│ │
│ Calendar Component (75%) │ Selected Dates List (25%) │
│ │ │
│ [Interactive Calendar] │ January │
│ • Month navigation │ ├─ 15 January │
│ • Year selector │ ├─ 16 January │
│ • Date highlighting │ │
│ │ February │
│ │ ├─ 14 February │
│ │ │
└─────────────────────────────────────────────────────────────┘
Key Features:
- Real-time Updates: Firestore live synchronization
- Responsive Design: Desktop 2-month, mobile 1-month view
- Visual Feedback: Loading states, success/error toasts
- Accessibility: Keyboard navigation, screen reader support
Component Breakdown:
<Layout>
<AdminPage>
<AdaptiveWrapper>
<OffDaysHeader>
<HeaderSection>
<Title>{t('message.offDaysPage.title')}</Title>
<Subtitle>{t('message.offDaysPage.subtitle')}</Subtitle>
</HeaderSection>
<SaveSection>
{isLoading && <LoadingSpinner />}
<SaveButton onClick={handleSave} disabled={isLoading}>
{t('button.save')}
</SaveButton>
</SaveSection>
</OffDaysHeader>
<OffDaysDatePicker
type="organization"
organizationOffDay={tempOrganizationOffDay}
handleUpdateOffDay={handleUpdateOffDay}
/>
</AdaptiveWrapper>
</AdminPage>
</Layout>Site Level Interface
Site Off-Days Configuration
Location: Embedded in Site Settings page
Component: src/components/sites/SiteOffDays/SiteOffDays.tsx
UI Layout:
┌─────────────────────────────────────────────────────────────┐
│ Site Off-Days Configuration │
├─────────────────────────────────────────────────────────────┤
│ ⚠️ Site-specific off-days override organization settings │
├─────────────────────────────────────────────────────────────┤
│ Enable Site Off-Days [Toggle Switch] [Save] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Calendar Component (75%) │ Selected Dates List (25%) │
│ │ │
│ [Interactive Calendar] │ Organization Days (•) │
│ • Organization days as dots │ Site Days (highlighted) │
│ • Site days highlighted │ │
│ • Visual inheritance │ │
│ │ │
└─────────────────────────────────────────────────────────────┘
Toggle Switch Component:
const Slider = ({ disabled, onClick }: SliderProps) => (
<SliderContainer disabled={disabled} onClick={onClick}>
<div className="circle" />
</SliderContainer>
);
// CSS-in-JS styling for visual toggle
const SliderContainer = styled.div<SliderProps>`
background: ${({ disabled }) => (!disabled ? '#574FCF' : '#C4C4C4')};
.circle {
left: ${({ disabled }) => (!disabled ? 'calc(100% - 8px)' : '0%')};
}
`;Visual Inheritance Pattern:
- Organization Days: Small dots below calendar dates
- Site Days: Highlighted background with border
- Tooltip System: Hover information for inherited days
User Level Interface
Auditor Off-Days Management
Route: /admin/auditors/useroffdays
Component: src/components/auditors/offDaysManager/OffDaysManager.tsx
UI Layout:
┌─────────────────────────────────────────────────────────────┐
│ Manage Non-Operational Days [Back] [Save] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Enhanced Calendar (60%) │ User Assignment (40%) │
│ │ │
│ [Interactive Calendar] │ Selected Date: Jan 15 │
│ • User count indicators │ │
│ • Multi-user assignments │ Assigned Users: │
│ • Visual user counts │ ☑️ John Doe │
│ │ ☑️ Jane Smith │
│ │ ☐ Mike Johnson │
│ │ ☐ Sarah Wilson │
│ │ │
│ │ [Add User] [Remove] │
└─────────────────────────────────────────────────────────────┘
User Assignment Interface:
const UserAssignmentPanel = ({ selectedDate, users, onUserToggle }) => (
<AssignmentPanel>
<DateHeader>
Selected Date: {moment(selectedDate).format('MMMM DD, YYYY')}
</DateHeader>
<UserList>
{users.map(user => (
<UserItem key={user.id}>
<Checkbox
checked={user.assigned}
onChange={() => onUserToggle(user.id)}
/>
<UserName>{user.name}</UserName>
<UserRole>{user.role}</UserRole>
</UserItem>
))}
</UserList>
<ActionButtons>
<Button variant="outline" onClick={handleAddUser}>
Add User
</Button>
<Button variant="danger" onClick={handleRemoveSelected}>
Remove Selected
</Button>
</ActionButtons>
</AssignmentPanel>
);Dashboard Interface
Analytics Dashboard
Route: /admin/auditors/offdays-dashboard
Component: src/components/auditors/offDaysDashboard/OffDaysDashboard.tsx
Dashboard Layout:
┌─────────────────────────────────────────────────────────────┐
│ Non-Operational Days Dashboard │
├─────────────────────────────────────────────────────────────┤
│ │
│ Main Content Area (75%) │ Sidebar (25%) │
│ │ │
│ ┌─ User Metrics ─────────────┐ │ ┌─ Monthly Chart ───────┐ │
│ │Today │Week │Month │Year │ │ │ │ │
│ │ 5 │ 12 │ 45 │ 150 │ │ │ [Line Chart] │ │
│ └─────────────────────────────┘ │ │ • Month selector │ │
│ │ │ • Gradient styling │ │
│ ┌─ Data Table ───────────────┐ │ └────────────────────────┘ │
│ │ [Manage] Search: [____] │ │ │
│ │ 👤 📅 [Filter] [👁️] [📅] │ │ ┌─ Upcoming Schedule ──┐ │
│ │ │ │ │Today: │ │
│ │ Name │Date │Sched │ │ │👤 John Doe │ │
│ │John Doe │Jan 15 │3 │ │ │👤 Jane Smith │ │
│ │Jane S. │Jan 16 │1 │ │ │ │ │
│ │ │ │ │ │ │This Week: │ │
│ │ [Pagination 1 2 3] │ │ │👤 Mike Johnson │ │
│ └─────────────────────────────┘ │ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
User Metrics Widget
Component: src/components/auditors/offDaysDashboard/UserMetrics.tsx
const UserMetrics = () => (
<MetricsContainer>
<Header>
<AnalyticsIcon />
<Title>User Metrics</Title>
</Header>
<MetricsGrid>
<MetricCard>
<CardHeader>Today</CardHeader>
<CardValue>{userCount?.today || 0}</CardValue>
</MetricCard>
<MetricCard>
<CardHeader>This Week</CardHeader>
<CardValue>{userCount?.week || 0}</CardValue>
</MetricCard>
<MetricCard>
<CardHeader>This Month</CardHeader>
<CardValue>{userCount?.month || 0}</CardValue>
</MetricCard>
<MetricCard>
<CardHeader>This Year</CardHeader>
<CardValue>{userCount?.year || 0}</CardValue>
</MetricCard>
</MetricsGrid>
</MetricsContainer>
);Advanced Data Table
Component: src/components/auditors/offDaysDashboard/OffDaysTable.tsx
Table Features:
- Dual View Modes: User-wise and Date-wise perspectives
- Advanced Filtering: Multi-dimensional filter system
- Real-time Search: Debounced search with highlighting
- Interactive Calendar: Date selection for date-wise view
- Drill-down Navigation: Links to detailed schedule and issue views
Filter System:
const FilterConfiguration = [
{
name: 'user',
label: 'User',
type: FilterFieldType.MULTI_SELECT,
options: userOptions,
placeholder: 'Select Users'
},
{
name: 'date',
label: 'Date Range',
type: FilterFieldType.DATE_RANGE_PRESET,
placeholder: 'Date Range'
},
{
name: 'sites',
label: 'Sites',
type: FilterFieldType.MULTI_SELECT,
options: siteOptions,
placeholder: 'Select Sites'
},
{
name: 'departments',
label: 'Department',
type: FilterFieldType.MULTI_SELECT,
options: departmentOptions,
placeholder: 'Select Department'
}
];View Toggle Component:
const ViewToggle = () => (
<SegmentedButton
items={[
{
value: 'userWise',
children: <ViewListIcon />
},
{
value: 'dateWise',
children: <CalendarIcon />
}
]}
value={viewType}
onItemClick={({ value }) => setViewType(value)}
/>
);Responsive Design System
Breakpoint Strategy
// Responsive breakpoints
$breakpoints: (
mobile: 320px,
tablet: 768px,
desktop: 992px,
large: 1275px,
xlarge: 1440px
);
// Calendar responsiveness
.calendar-container {
.DayPicker {
// Mobile: 1 month
@media (max-width: 682px) {
.CalendarMonthGrid {
grid-template-columns: 1fr;
}
}
// Tablet: 2 months if space allows
@media (min-width: 683px) and (max-width: 991px) {
.CalendarMonthGrid {
grid-template-columns: repeat(2, 1fr);
}
}
// Desktop: 1 month (992-1275px)
@media (min-width: 992px) and (max-width: 1274px) {
.CalendarMonthGrid {
grid-template-columns: 1fr;
}
}
// Large desktop: 2 months
@media (min-width: 1275px) {
.CalendarMonthGrid {
grid-template-columns: repeat(2, 1fr);
}
}
}
}Mobile Optimization
// Dynamic month count based on screen size
const useResponsiveCalendar = () => {
const [monthCount, setMonthCount] = useState(() =>
window.innerWidth > 1275 ? 2 : 1
);
useLayoutEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
if (width > 1275) setMonthCount(2);
else if (width < 1275 && width > 991) setMonthCount(1);
else if (width < 992 && width > 682) setMonthCount(2);
else setMonthCount(1);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return monthCount;
};Accessibility Features
WCAG 2.1 Compliance
- Keyboard Navigation: Full keyboard accessibility for calendar and tables
- Screen Reader Support: ARIA labels and semantic HTML structure
- Color Contrast: 4.5:1 ratio for all text elements
- Focus Management: Visible focus indicators and logical tab order
// Accessible calendar navigation
const AccessibleCalendarDay = ({ date, isSelected, isOffDay, onClick }) => (
<CalendarDay
role="button"
tabIndex={0}
aria-label={`${date.format('MMMM DD, YYYY')}${isOffDay ? ', Off Day' : ''}`}
aria-pressed={isSelected}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClick(date);
}
}}
onClick={() => onClick(date)}
>
{date.format('DD')}
{isOffDay && <OffDayIndicator aria-hidden="true" />}
</CalendarDay>
);Internationalization (i18n)
Translation Files: Multiple language support across all components
// Language files structure
const translations = {
'en': {
'nav.nonOperationalDays': 'Non-Operational Days',
'message.offDaysPage.title': 'Manage Non-Operational Days',
'message.offDaysPage.subtitle': 'Configure organization-wide off-days...',
'button.save': 'Save',
'addOn.offDays.success': 'Off-days updated successfully',
'addOn.offDays.fail': 'Failed to update off-days'
},
'es': {
'nav.nonOperationalDays': 'Días No Operacionales',
'message.offDaysPage.title': 'Gestionar Días No Operacionales',
// ... additional translations
}
};Performance Optimizations
Code Splitting & Lazy Loading
// Route-level code splitting
const OffDaysPage = lazy(() => import('components/offdays/OffDaysManager'));
const AuditorsOffDaysPage = lazy(() => import('components/auditors/offDaysManager/OffDaysManager'));
const DashboardPage = lazy(() => import('components/auditors/offDaysDashboard/OffDaysDashboard'));
// Component-level lazy loading
const ScheduleListModal = lazy(() => import('./ScheduleListModal'));
const ScheduleDetailModal = lazy(() => import('./ScheduleDetailModal'));Memoization Strategy
// Expensive calculations memoized
const memoizedTableData = useMemo(() => {
return nonOpsList?.docs?.map((nonOps, index) => ({
key: `${viewType}-${index}`,
value: {
name: viewType === 'userWise'
? nonOps?.name || ''
: nonOps?.data?.map(d => d?.name).join(', '),
// ... complex data transformation
}
})) || [];
}, [nonOpsList, viewType]);
// Debounced search to reduce API calls
const debouncedSearch = useMemo(
() => debounce((text: string) => fetchNonOps(text), 500),
[]
);Business Logic
The Non-Operational Day Management System implements sophisticated business logic that handles complex organizational hierarchies, data transformations, and real-time synchronization patterns.
Organizational Hierarchy Logic
Priority Resolution System
The system implements a three-tier priority system where higher-level settings can be overridden by more specific configurations:
/**
* Priority Resolution Order:
* 1. User-specific assignments (highest priority)
* 2. Site-specific configurations
* 3. Organization-wide settings (lowest priority)
*/
const resolveEffectiveOffDay = async (
date: string,
userID: string,
siteID: string,
organizationID: string
): Promise<boolean> => {
// Level 1: Check user-specific assignment
const userAssignment = await getUserOffDayAssignment(userID, date);
if (userAssignment !== null) {
return userAssignment; // Explicit user assignment takes precedence
}
// Level 2: Check site-specific configuration
const siteConfig = await getSiteOffDayConfiguration(siteID);
if (siteConfig && !siteConfig.disabled) {
return siteConfig.dates.includes(date);
}
// Level 3: Fall back to organization-wide setting
const orgConfig = await getOrganizationOffDay(organizationID);
return orgConfig?.dates?.[date] || false;
};Site Override Logic
Component: src/components/sites/SiteOffDays/hook/useSiteOffDays.ts:56
const handleOffDayToggle = () => {
setSiteOffDays({
...siteOffDays,
disabled: !siteOffDays.disabled
});
};
/**
* When disabled = false: Site off-days are active and override organization
* When disabled = true: Site inherits organization settings
*/
const getEffectiveSiteOffDays = (date: string) => {
if (siteOffDays.disabled) {
// Inherit from organization
return organizationOffDay?.dates?.[date] || false;
} else {
// Use site-specific configuration
return siteOffDays.dates[date] || false;
}
};Data Transformation Logic
User Assignment Data Mapping
Component: src/components/auditors/offDaysManager/OffDaysManager.tsx:79
The system performs complex bidirectional data transformations between frontend and API formats:
/**
* API Response Format: Array of user objects with date arrays
* Frontend Format: Date objects with user arrays
*/
// API to Frontend Transformation
const transformApiToFrontend = (apiData: Array<{ [userID: string]: string[] }>) => {
const frontendFormat: { [date: string]: string[] } = {};
apiData.forEach(userObject => {
Object.entries(userObject).forEach(([userID, dates]) => {
dates.forEach(date => {
if (!frontendFormat[date]) {
frontendFormat[date] = [];
}
frontendFormat[date].push(userID);
});
});
});
return frontendFormat;
};
// Frontend to API Transformation
const transformFrontendToApi = (
frontendData: { [date: string]: string[] },
initialUserIDs: string[]
) => {
// Convert date-user mapping to user-date mapping
const convertedData: { [userID: string]: string[] } = {};
Object.entries(frontendData).forEach(([date, userIDs]) => {
userIDs.forEach(userID => {
if (!convertedData[userID]) {
convertedData[userID] = [];
}
convertedData[userID].push(date);
});
});
// Handle removals: users not in final data get empty arrays
const finalUserIDs = Object.keys(convertedData);
const removedUserIDs = initialUserIDs.filter(
userID => !finalUserIDs.includes(userID)
);
// Create API format with removal logic
const apiFormat = Object.entries(convertedData).map(([userID, dates]) => ({
[userID]: dates
}));
// Add removed users with empty arrays
removedUserIDs.forEach(userID => {
apiFormat.push({ [userID]: [] });
});
return apiFormat;
};Site Data Processing
Component: src/components/sites/SiteOffDays/hook/useSiteOffDays.ts:97
/**
* Site off-days data transformation from API array to frontend boolean mapping
*/
const processedSiteOffDays = useMemo(() => {
return {
...siteOffDaysResponse,
dates: siteOffDaysResponse.dates.reduce<{ [date: string]: boolean }>(
(siteOffDaysMap, dateString) => {
siteOffDaysMap[dateString] = true;
return siteOffDaysMap;
},
{}
)
};
}, [siteOffDaysResponse]);
/**
* Reverse transformation for API submission
*/
const handleSubmitOffDay = () => {
const apiPayload = {
...siteOffDays,
dates: Object.keys(siteOffDays.dates || {}) // Convert boolean map back to array
};
dispatch(upsertSiteOffDays.request({
siteID,
updatedSiteOffDays: apiPayload
}));
};Calendar Business Logic
Dynamic Month Count Calculation
Component: src/components/global/OffDaysDatePicker/OffDaysDatePicker.tsx:89
/**
* Responsive calendar logic based on screen dimensions
*/
const handleResize = () => {
const width = window.innerWidth;
if (width > 1275) {
setMonthCount(2); // Large desktop: 2 months
} else if (width < 1275 && width > 991) {
setMonthCount(1); // Desktop: 1 month
} else if (width < 992 && width > 682) {
setMonthCount(2); // Tablet: 2 months (side-by-side)
} else if (width < 682) {
setMonthCount(1); // Mobile: 1 month
}
};
/**
* Calendar date rendering with inheritance visualization
*/
const renderDayContents = (day: moment.Moment) => {
const dateString = day.format('YYYY-MM-DD');
const isOrganizationOffDay = organizationOffDay?.dates?.[dateString];
const isSiteOffDay = siteOffDay?.dates?.[dateString];
return (
<CalendarDates>
{removeZero(day.format('DD'))}
{type === 'site' && isOrganizationOffDay && (
<InheritanceDot>
<span className="tooltip-text">{t('label.offDaysPage.non-op')}</span>
</InheritanceDot>
)}
</CalendarDates>
);
};Date Selection Logic
/**
* Calendar date toggle with state management
*/
const handleToggleDate = (date: moment.Moment) => {
const dateString = date.format('YYYY-MM-DD');
const isCurrentlySelected = offDayState?.dates?.[dateString];
const clonedDates = cloneDeep(offDayState);
if (isCurrentlySelected) {
// Remove date
delete clonedDates!.dates[dateString];
} else {
// Add date
clonedDates!.dates = {
...clonedDates!.dates,
[dateString]: true
};
}
setOffDayState(clonedDates);
handleUpdateOffDay(clonedDates!);
};Dashboard Analytics Logic
User Metrics Calculation
Component: src/components/auditors/offDaysDashboard/UserMetrics.tsx:22
The dashboard calculates real-time metrics across different time periods:
/**
* Server-side aggregation pipeline for user metrics
*/
const calculateUserMetrics = async (organizationID: string) => {
const now = new Date();
const startOfDay = moment().startOf('day').toDate();
const startOfWeek = moment().startOf('week').toDate();
const startOfMonth = moment().startOf('month').toDate();
const startOfYear = moment().startOf('year').toDate();
const pipeline = [
{ $match: { organizationID } },
{ $unwind: "$dates" },
{
$addFields: {
dateObj: { $dateFromString: { dateString: "$dates" } }
}
},
{
$group: {
_id: null,
today: {
$sum: {
$cond: [
{
$and: [
{ $gte: ["$dateObj", startOfDay] },
{ $lt: ["$dateObj", moment(startOfDay).add(1, 'day').toDate()] }
]
},
1, 0
]
}
},
week: {
$sum: {
$cond: [{ $gte: ["$dateObj", startOfWeek] }, 1, 0]
}
},
month: {
$sum: {
$cond: [{ $gte: ["$dateObj", startOfMonth] }, 1, 0]
}
},
year: {
$sum: {
$cond: [{ $gte: ["$dateObj", startOfYear] }, 1, 0]
}
}
}
}
];
return await UserOffDay.aggregate(pipeline);
};Monthly Chart Data Processing
Component: src/components/auditors/offDaysDashboard/TotalNonOffDays.tsx:46
/**
* Chart data transformation with chronological sorting
*/
const processChartData = (monthlyNonOps: MonthlyNonOpsI) => {
let monthLabels: string[] = [];
let monthValues: number[] = [];
if (monthlyNonOps) {
// Sort dates chronologically
Object.keys(monthlyNonOps)
.sort((a: string, b: string) => moment(a).diff(moment(b), 'days'))
.forEach((date: string) => {
if (date) {
monthLabels.push(moment(date).format("D MMM 'YY"));
monthValues.push(monthlyNonOps[date] || 0);
}
});
}
return {
labels: monthLabels,
datasets: [{
fill: true,
label: 'Non Operational Days',
data: monthValues,
borderColor: '#574FCF',
backgroundColor: (context: any) => {
const ctx = context.chart.ctx;
const gradient = ctx.createLinearGradient(0, 0, 0, context.chart.height);
gradient.addColorStop(0, 'rgba(87, 79, 207, 0.30)');
gradient.addColorStop(0.7, 'rgba(87, 79, 207, 0.00)');
return gradient;
}
}]
};
};Table View Logic
Dual View Mode Processing
Component: src/components/auditors/offDaysDashboard/OffDaysTable.tsx:115
/**
* Complex table data mapping for dual view modes
*/
const mappedTableData: TableRow[] = useMemo(() => {
return nonOpsList?.docs?.map((nonOps, index) => ({
key: `${viewType}-${index}`,
value: {
// Dynamic name field based on view type
name: viewType === 'userWise'
? nonOps?.name || ''
: nonOps?.data?.map((nonOpsDetails) => nonOpsDetails?.name).join(', ') || '',
// Dynamic date field based on view type
date: viewType === 'userWise'
? nonOps?.data?.map((nonOpsDetails) => nonOpsDetails?.date).join(', ') || ''
: nonOps?.date || '',
// Aggregated schedule and issue counts
scheduleCount: nonOps?.data?.map((nonOpsDetails) =>
nonOpsDetails?.scheduleCount).join(', '),
issueCount: nonOps?.data?.map((nonOpsDetails) =>
nonOpsDetails?.issueCount).join(', '),
// Raw data for drill-down operations
scheduleData: nonOps?.data || [],
userID: nonOps?.userID || ''
}
})) || [];
}, [nonOpsList, viewType]);
/**
* Column configuration based on view mode
*/
const columnConfigs: ColumnConfigs = useMemo(() => {
if (viewType === 'userWise') {
return [
{ name: 'name', title: 'Name', width: '30%', onSort: handleSort },
{ name: 'date', title: 'Date', width: '20%', customCellRender: customDateCell },
{ name: 'scheduleCount', title: 'Schedules on this date', width: '25%' },
{ name: 'issueCount', title: 'Issues due on this date', width: '25%' }
];
} else {
return [
{ name: 'date', title: 'Date', width: '20%', onSort: handleSort },
{ name: 'name', title: 'Name', width: '30%', customCellRender: customNameCell },
{ name: 'scheduleCount', title: 'Schedules on this date', width: '25%' },
{ name: 'issueCount', title: 'Issues due on this date', width: '25%' }
];
}
}, [viewType]);Advanced Filtering Logic
/**
* Multi-dimensional filter processing
*/
const buildFilterQuery = (filters: DashboardFilters) => {
let queryParams: any = {
type: filters.viewType,
sortBy: filters.sortBy,
order: filters.order,
limit: filters.limit,
page: filters.page
};
if (filters.viewType === 'userWise') {
// User-wise view filters
if (filters.departments?.length) {
queryParams.department = filters.departments;
}
if (filters.sites?.length) {
queryParams.site = filters.sites;
}
if (filters.users?.length) {
queryParams.users = filters.users;
}
if (filters.dateRange?.startDate) {
queryParams.startDate = formatDate(filters.dateRange.startDate);
}
if (filters.dateRange?.endDate) {
queryParams.endDate = formatDate(filters.dateRange.endDate);
}
if (filters.search) {
queryParams.search = filters.search;
}
} else {
// Date-wise view filters
if (filters.selectedDates?.length) {
queryParams.dates = filters.selectedDates;
}
}
return queryParams;
};Search and Pagination Logic
Debounced Search Implementation
/**
* Optimized search with debouncing to reduce API calls
*/
const handleSearch = debounce(async (searchText: string) => {
const params = buildFilterQuery({
...currentFilters,
search: searchText,
page: 1 // Reset to first page on new search
});
const results = await fetchNonOpsList(params);
setNonOpsList(results);
}, 500);
const handleSearchInput = useCallback((text: string) => {
setSearchText(text);
handleSearch(text);
}, [handleSearch]);Calendar Date Selection for Date-wise View
/**
* Multi-date selection logic for date-wise table view
*/
const handleSelectedDates = (date: Moment | null) => {
if (date) {
const dateString = date.format('YYYY-MM-DD');
if (selectedDates?.includes(dateString)) {
// Remove date if already selected
setSelectedDates(
selectedDates.filter(selectedDate => selectedDate !== dateString)
);
} else {
// Add date to selection
setSelectedDates([...selectedDates, dateString]);
}
}
};
/**
* Calendar day rendering with selection indicators
*/
const renderDayContent = (day: moment.Moment) => {
const dateString = day.format('YYYY-MM-DD');
const isSelected = selectedDates?.includes(dateString);
const hasData = nonOpsList?.docs?.some(doc => doc?.date === dateString);
return (
<CalendarDates isActive={isSelected}>
{hasData && <DataIndicatorDot />}
{removeZero(day.format('DD'))}
</CalendarDates>
);
};Integration Navigation Logic
Cross-module Navigation
/**
* Navigation to related modules with context preservation
*/
const handleIssueNavigation = (rowData: TableRow, index: number) => {
let selectedUser = '';
let selectedDate = '';
if (viewType === 'userWise') {
selectedUser = rowData?.value?.userID || '';
selectedDate = rowData.value?.scheduleData?.[index]?.date || '';
} else {
selectedUser = rowData.value?.scheduleData?.[index]?.userId || '';
selectedDate = rowData.value?.date || '';
}
// Navigate with query parameters for filtered view
history.push(
`/issues?qUser=${selectedUser}&qStartDate=${selectedDate}&qEndDate=${selectedDate}`
);
};
/**
* Schedule detail modal navigation
*/
const handleScheduleDetailNavigation = (scheduleData: TableRow, index: number) => {
setSelectedSchedule(scheduleData.value?.scheduleData?.[index]?.scheduleData || []);
setScheduleUser(
viewType === 'userWise'
? scheduleData.value?.name
: scheduleData.value?.scheduleData?.[index]?.name
);
setScheduleDate(
viewType === 'userWise'
? scheduleData.value?.scheduleData?.[index]?.date
: scheduleData.value?.date
);
setShowScheduleList(true);
};Error Handling and Recovery Logic
Hierarchical Error Handling
/**
* Component-level error boundaries with fallback strategies
*/
const handleApiError = (error: any, operation: string) => {
// Log error for monitoring
Monitoring.logEvent(`OffDays_${operation}_Error`, {
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
});
// User feedback based on error type
if (error.code === 'NETWORK_ERROR') {
toast.error(t('error.network.message'));
// Attempt retry with exponential backoff
scheduleRetry(operation);
} else if (error.code === 'PERMISSION_DENIED') {
toast.error(t('error.permission.message'));
// Redirect to unauthorized page
history.push('/unauthorized');
} else {
toast.error(t('addOn.offDays.fail'));
}
};
/**
* Optimistic updates with rollback capability
*/
const optimisticUpdate = async (newState: any, apiCall: () => Promise<any>) => {
const previousState = cloneDeep(currentState);
// Apply optimistic update
setState(newState);
try {
await apiCall();
// Success - state is already updated
} catch (error) {
// Rollback on failure
setState(previousState);
handleApiError(error, 'optimistic_update');
}
};Permission and Access Control Logic
Dynamic Permission Checking
/**
* Granular permission validation
*/
const checkOffDaysPermissions = (level: 'organization' | 'site' | 'user') => {
const userPermissions = userAccess.admin['non-operational-days'];
switch (level) {
case 'organization':
return userPermissions.all.permissions?.includes('write') ||
userPermissions.organization?.includes('write');
case 'site':
return userPermissions.all.permissions?.includes('write') ||
userPermissions.site?.includes('write');
case 'user':
return userPermissions.all.permissions?.includes('write') ||
userPermissions.users?.includes('write');
default:
return false;
}
};
/**
* Component-level permission enforcement
*/
const PermissionGuard = ({ level, children }: PermissionGuardProps) => {
const hasPermission = checkOffDaysPermissions(level);
if (!hasPermission) {
return <UnauthorizedView />;
}
return <>{children}</>;
};Performance and Optimization Logic
Intelligent Data Loading
/**
* Progressive data loading with priority
*/
const loadDashboardData = async () => {
// Priority 1: Load critical metrics first
const metricsPromise = fetchUserCount();
// Priority 2: Load table data (can be shown with loading state)
const tablePromise = fetchNonOpsList(currentFilters);
// Priority 3: Load chart data (less critical)
const chartPromise = fetchMonthNonOps(selectedMonth.value);
// Priority 4: Load upcoming schedule (sidebar widget)
const schedulePromise = fetchUpcomingLeaves();
try {
// Load high-priority data first
const [metrics] = await Promise.all([metricsPromise]);
setUserCount(metrics);
// Load remaining data in background
const [tableData, chartData, scheduleData] = await Promise.all([
tablePromise,
chartPromise,
schedulePromise
]);
setNonOpsList(tableData);
setMonthlyNonOps(chartData);
setUpcomingSchedule(scheduleData);
} catch (error) {
handleApiError(error, 'dashboard_load');
}
};This comprehensive business logic documentation covers all the critical computational patterns, data transformations, and decision-making algorithms that power the Non-Operational Day Management System.
Integration Points
The Non-Operational Day Management System integrates deeply with multiple modules within the audit administration platform, creating a comprehensive ecosystem for workforce management and scheduling.
Core System Integration
User Management System Integration
Integration Point: User data, roles, and permissions
Files: src/hooks/useUserDataOptions.ts
/**
* User data integration for off-days assignment
*/
const useUserDataOptions = () => {
const { userMap, userOptions } = useSelector(state => state.users);
// Filter users by organization and active status
const availableUsers = useMemo(() => {
return userOptions.filter(user =>
user.organizationID === currentOrganization &&
user.status === 'active' &&
user.role.includes('auditor')
);
}, [userOptions, currentOrganization]);
return { userMap, availableUsers };
};Integration Features:
- User Selection: Multi-select dropdowns populated from user management
- Role-based Filtering: Only auditors shown in assignment interfaces
- Permission Validation: User access controls for off-days management
- Profile Information: User names, departments, and site assignments
Site Management Integration
Integration Point: Site configurations and hierarchies
Files: src/hooks/useSiteOptions.ts
/**
* Site data integration for filtering and assignment
*/
const useSiteOptions = () => {
const { siteMap, siteOptions } = useSelector(state => state.sites);
const activeSites = useMemo(() => {
return siteOptions.filter(site =>
site.status === 'active' &&
site.organizationID === currentOrganization
);
}, [siteOptions, currentOrganization]);
return { siteMap, activeSites };
};Integration Features:
- Site-specific Off-days: Override organization settings per site
- Filtering by Site: Dashboard filters include site selection
- Site Hierarchy: Respects organizational site structure
- Site Settings: Embedded in site configuration pages
Department Management Integration
Integration Point: Departmental structure and assignments
Files: src/hooks/useDepartmentOptions.ts
/**
* Department data integration for organizational filtering
*/
const useDepartmentOptions = () => {
const { departmentMap, departmentOptions } = useSelector(state => state.departments);
return {
departmentMap,
departmentOptions: departmentOptions.filter(dept => dept.active)
};
};Schedule Management Integration
Schedule System Integration
Purpose: Link off-days with existing schedules and assignments
Data Flow: Off-days impact schedule availability and conflict detection
/**
* Schedule conflict detection
*/
const checkScheduleConflicts = async (userID: string, date: string) => {
// Check if user has off-day assignment
const hasOffDay = await checkUserOffDay(userID, date);
if (hasOffDay) {
return {
conflict: true,
type: 'OFF_DAY',
message: 'User is assigned off-day on this date'
};
}
// Check site-level restrictions
const userSite = await getUserSite(userID);
const siteOffDay = await checkSiteOffDay(userSite, date);
if (siteOffDay) {
return {
conflict: true,
type: 'SITE_OFF_DAY',
message: 'Site is non-operational on this date'
};
}
return { conflict: false };
};Integration Points:
- Schedule Creation: Validate availability during schedule assignment
- Schedule Modification: Check conflicts when updating existing schedules
- Schedule Dashboard: Display schedule counts for off-day dates
- Bulk Scheduling: Mass schedule operations respect off-day settings
Issue Management Integration
Purpose: Display issue due dates that conflict with off-days
Navigation: Direct links from off-days dashboard to issues page
/**
* Issue dashboard integration
*/
const handleIssueNavigation = (userID: string, date: string) => {
const issueFilterParams = {
qUser: userID,
qStartDate: date,
qEndDate: date,
status: 'open'
};
history.push(`/issues?${queryString.stringify(issueFilterParams)}`);
};Integration Features:
- Issue Count Display: Show number of issues due on off-days
- Contextual Navigation: Filter issues by user and date
- Conflict Alerts: Highlight issues due on non-operational days
- Workflow Integration: Issue resolution considers off-day status
Permission System Integration
Role-based Access Control
Integration Point: Admin permission system
Permission Structure: userAccess.admin['non-operational-days']
/**
* Permission hierarchy validation
*/
const permissionLevels = {
'non-operational-days.all': {
organization: true,
site: true,
user: true,
dashboard: true
},
'non-operational-days.organization': {
organization: true,
site: false,
user: false,
dashboard: true
},
'non-operational-days.site': {
organization: false,
site: true,
user: false,
dashboard: true
},
'non-operational-days.users': {
organization: false,
site: false,
user: true,
dashboard: true
},
'non-operational-days.view': {
organization: false,
site: false,
user: false,
dashboard: true
}
};Audit and Compliance Integration
Audit Trail Integration
Purpose: Track all off-days changes for compliance
Integration Point: System-wide audit logging
/**
* Audit trail for off-days changes
*/
const logOffDayChange = async (action: string, data: any) => {
await AuditLog.create({
module: 'NON_OPERATIONAL_DAYS',
action: action,
entityType: data.type, // 'ORGANIZATION', 'SITE', 'USER'
entityId: data.id,
changes: {
before: data.previousState,
after: data.newState,
changedFields: data.changedFields
},
userId: currentUser.id,
timestamp: new Date(),
metadata: {
userAgent: navigator.userAgent,
ipAddress: clientIP,
organizationId: currentUser.organizationId
}
});
};Analytics and Reporting Integration
Executive Dashboard Integration
Purpose: Provide high-level metrics for executive dashboards
Data Export: Off-days metrics available for executive reporting
/**
* Executive dashboard metrics
*/
const getExecutiveMetrics = async (organizationID: string, timeframe: string) => {
return {
nonOperationalDaysCount: await getTotalOffDays(organizationID, timeframe),
affectedUsersCount: await getAffectedUsersCount(organizationID, timeframe),
productivityImpact: await calculateProductivityImpact(organizationID, timeframe),
scheduleConflicts: await getScheduleConflicts(organizationID, timeframe),
trends: await getOffDaysTrends(organizationID, timeframe)
};
};Notification System Integration
Email Notification Integration
Purpose: Notify stakeholders of off-days changes
Integration Point: System notification service
/**
* Notification triggers for off-days changes
*/
const triggerNotifications = async (changeType: string, data: any) => {
const notifications = [];
if (changeType === 'ORGANIZATION_OFF_DAY_CHANGE') {
// Notify all site managers
notifications.push({
type: 'EMAIL',
recipients: await getSiteManagers(data.organizationID),
template: 'organization_offday_change',
data: data
});
}
if (changeType === 'USER_OFF_DAY_ASSIGNMENT') {
// Notify assigned user and manager
notifications.push({
type: 'EMAIL',
recipients: [data.userEmail, data.managerEmail],
template: 'user_offday_assignment',
data: data
});
}
await NotificationService.sendBatch(notifications);
};Bulk Operations Integration
Bulk Management Integration
Purpose: Mass operations for off-days management
Files: src/pages/bulkOpsRevamp/bulkOpsUtils.ts
/**
* Bulk off-days operations
*/
const bulkOffDaysOperations = {
assignMultipleUsers: async (userIDs: string[], dates: string[]) => {
const operations = userIDs.map(userID => ({
userID,
dates,
action: 'ASSIGN'
}));
return await executeBulkOffDaysUpdate(operations);
},
copyFromTemplate: async (templateID: string, targetUserIDs: string[]) => {
const template = await getOffDaysTemplate(templateID);
const operations = targetUserIDs.map(userID => ({
userID,
dates: template.dates,
action: 'REPLACE'
}));
return await executeBulkOffDaysUpdate(operations);
}
};Testing
The Non-Operational Day Management System includes comprehensive testing strategies covering unit tests, integration tests, and end-to-end user scenarios.
Unit Testing
Component Testing Strategy
Testing Framework: Jest + React Testing Library
Test Files: Component-specific .test.tsx files
Example: src/components/sites/SiteOffDays/SiteOffDays.test.tsx
/**
* Site Off-Days Component Tests
*/
describe('SiteOffDays Component', () => {
const mockProps = {
organizationOffDay: {
organization: 'test-org',
dates: { '2024-01-15': true }
},
siteKey: 'test-site'
};
beforeEach(() => {
jest.clearAllMocks();
// Mock Redux hooks
(useSelector as jest.Mock).mockReturnValue({
isFetchLoading: false,
isUpsertLoading: false,
siteOffDays: { disabled: false, dates: {} }
});
});
test('renders loading state correctly', () => {
(useSelector as jest.Mock).mockReturnValueOnce({
isFetchLoading: true
});
render(<SiteOffDays {...mockProps} />);
expect(screen.getByTestId('loading-container')).toBeInTheDocument();
});
test('renders toggle switch correctly', () => {
render(<SiteOffDays {...mockProps} />);
const toggle = screen.getByTestId('offday-toggle');
expect(toggle).toBeInTheDocument();
});
test('handles toggle switch interaction', () => {
const mockDispatch = jest.fn();
(useDispatch as jest.Mock).mockReturnValue(mockDispatch);
render(<SiteOffDays {...mockProps} />);
const toggle = screen.getByTestId('offday-toggle');
fireEvent.click(toggle);
// Verify state change logic
expect(mockDispatch).toHaveBeenCalled();
});
test('handles save button interaction', () => {
render(<SiteOffDays {...mockProps} />);
const saveButton = screen.getByTestId('submit-button');
fireEvent.click(saveButton);
// Verify API call is triggered
expect(mockAPICall).toHaveBeenCalledWith(expectedPayload);
});
});Hook Testing
/**
* Custom Hook Tests
*/
describe('useSiteOffDays Hook', () => {
const mockSiteID = 'test-site-123';
const mockCallbacks = {
onFetchError: jest.fn(),
onUpsertError: jest.fn(),
onUpsertSuccess: jest.fn()
};
test('fetches site off-days on mount', () => {
const { result } = renderHook(() =>
useSiteOffDays({ siteID: mockSiteID, ...mockCallbacks })
);
expect(mockDispatch).toHaveBeenCalledWith(
fetchSiteOffDays.request({ siteID: mockSiteID })
);
});
test('handles toggle functionality', () => {
const { result } = renderHook(() =>
useSiteOffDays({ siteID: mockSiteID, ...mockCallbacks })
);
act(() => {
result.current[1].handleOffDayToggle();
});
// Verify state change
expect(result.current[0].siteOffDays.disabled).toBe(true);
});
});Integration Testing
API Integration Tests
/**
* API Service Integration Tests
*/
describe('Auditor Off-Days API Integration', () => {
beforeEach(() => {
fetchMock.resetMocks();
});
test('fetchUserCount returns formatted data', async () => {
const mockResponse = {
data: { today: 5, week: 12, month: 45, year: 150 }
};
fetchMock.mockResponseOnce(JSON.stringify(mockResponse));
const result = await fetchUserCount();
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining('/users/off-days/count'),
expect.objectContaining({
method: 'GET',
headers: expect.objectContaining({
Authorization: expect.any(String)
})
})
);
expect(result).toEqual(mockResponse.data);
});
test('handles API error responses', async () => {
fetchMock.mockRejectOnce(new Error('Network error'));
await expect(fetchUserCount()).rejects.toThrow('Network error');
});
test('fetchNonOpsList handles complex query parameters', async () => {
const params = {
type: 'user',
search: 'john',
sortBy: 'name',
order: 'asc',
limit: 10,
page: 1
};
fetchMock.mockResponseOnce(JSON.stringify({ data: { docs: [] } }));
await fetchNonOpsList(params);
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining('type=user&search=john&sortBy=name'),
expect.any(Object)
);
});
});Redux Integration Tests
/**
* Redux Saga Integration Tests
*/
describe('Site Off-Days Saga Integration', () => {
test('handles successful fetch request', () => {
const generator = runSaga(
{
dispatch: (action) => dispatched.push(action),
getState: () => ({}),
},
fetchSiteOffDaysSaga,
fetchSiteOffDays.request({ siteID: 'test-site' })
);
expect(generator.next().value).toEqual(
call(siteOffDaysService.fetchSiteOffDays, 'test-site')
);
const mockData = { siteID: 'test-site', dates: ['2024-01-15'] };
expect(generator.next(mockData).value).toEqual(
put(fetchSiteOffDays.success(mockData))
);
});
test('handles fetch error', () => {
const generator = runSaga(
mockStore,
fetchSiteOffDaysSaga,
fetchSiteOffDays.request({ siteID: 'test-site' })
);
generator.next();
const error = new Error('API Error');
expect(generator.throw(error).value).toEqual(
put(fetchSiteOffDays.failure(error.message))
);
});
});End-to-End Testing
User Workflow Tests
/**
* E2E Test Scenarios
*/
describe('Off-Days Management E2E', () => {
beforeEach(async () => {
await page.goto('/admin/offdays');
await page.waitForSelector('[data-testid="calendar"]');
});
test('organization admin can set off-days', async () => {
// Select date on calendar
await page.click('[data-date="2024-01-15"]');
// Verify date is highlighted
const selectedDate = await page.$('[data-date="2024-01-15"].selected');
expect(selectedDate).toBeTruthy();
// Save changes
await page.click('[data-testid="submit-button"]');
// Verify success message
await page.waitForSelector('.toast-success');
const successMessage = await page.textContent('.toast-success');
expect(successMessage).toContain('Off-days updated successfully');
});
test('site admin can override organization settings', async () => {
await page.goto('/sites/test-site/settings');
// Enable site off-days
await page.click('[data-testid="offday-toggle"]');
// Add site-specific date
await page.click('[data-date="2024-02-20"]');
// Save configuration
await page.click('[data-testid="submit-button"]');
// Verify override is active
const toggle = await page.$('[data-testid="offday-toggle"][data-enabled="true"]');
expect(toggle).toBeTruthy();
});
test('dashboard shows correct metrics', async () => {
await page.goto('/admin/auditors/offdays-dashboard');
// Wait for metrics to load
await page.waitForSelector('[data-testid="user-metrics"]');
// Verify metric cards display numbers
const todayCount = await page.textContent('[data-metric="today"]');
expect(parseInt(todayCount)).toBeGreaterThanOrEqual(0);
// Test table filtering
await page.click('[data-testid="filter-toggle"]');
await page.selectOption('[data-testid="site-filter"]', 'site-123');
await page.click('[data-testid="apply-filters"]');
// Verify filtered results
await page.waitForSelector('[data-testid="table-row"]');
const tableRows = await page.$$('[data-testid="table-row"]');
expect(tableRows.length).toBeGreaterThan(0);
});
});Performance Testing
Load Testing
/**
* Performance Test Suite
*/
describe('Off-Days Performance', () => {
test('calendar renders within performance budget', async () => {
const startTime = performance.now();
render(<OffDaysDatePicker {...mockProps} />);
const endTime = performance.now();
const renderTime = endTime - startTime;
// Calendar should render within 100ms
expect(renderTime).toBeLessThan(100);
});
test('dashboard data loads within acceptable time', async () => {
const startTime = performance.now();
const { findByTestId } = render(<OffDaysDashboard />);
await findByTestId('user-metrics');
const endTime = performance.now();
const loadTime = endTime - startTime;
// Dashboard should load within 2 seconds
expect(loadTime).toBeLessThan(2000);
});
test('table pagination performs efficiently', async () => {
const mockLargeDataset = Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `User ${i}`,
dates: ['2024-01-15']
}));
jest.spyOn(api, 'fetchNonOpsList').mockResolvedValue({
docs: mockLargeDataset.slice(0, 10),
totalDocs: 1000,
totalPages: 100
});
const startTime = performance.now();
render(<OffDaysTable />);
const endTime = performance.now();
const renderTime = endTime - startTime;
// Table with 10 rows should render quickly
expect(renderTime).toBeLessThan(50);
});
});Accessibility Testing
A11y Compliance Tests
/**
* Accessibility Test Suite
*/
describe('Off-Days Accessibility', () => {
test('calendar has proper ARIA labels', async () => {
render(<OffDaysDatePicker {...mockProps} />);
const calendarDays = screen.getAllByRole('button');
calendarDays.forEach(day => {
expect(day).toHaveAttribute('aria-label');
expect(day).toHaveAttribute('tabIndex');
});
});
test('supports keyboard navigation', async () => {
render(<OffDaysDatePicker {...mockProps} />);
const firstDay = screen.getAllByRole('button')[0];
firstDay.focus();
fireEvent.keyDown(firstDay, { key: 'ArrowRight' });
// Verify focus moves to next day
const nextDay = screen.getAllByRole('button')[1];
expect(nextDay).toHaveFocus();
});
test('screen reader announces changes', async () => {
const { container } = render(<OffDaysDatePicker {...mockProps} />);
const liveRegion = container.querySelector('[aria-live]');
expect(liveRegion).toBeInTheDocument();
// Simulate date selection
const dayButton = screen.getAllByRole('button')[5];
fireEvent.click(dayButton);
// Verify announcement
await waitFor(() => {
expect(liveRegion).toHaveTextContent(/selected/i);
});
});
});Dependencies
The Non-Operational Day Management System relies on a carefully curated set of dependencies that provide functionality for UI components, data management, and development tooling.
Core Framework Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
react | ^17.0.2 | Core React framework | All component rendering and state management |
react-dom | ^17.0.2 | React DOM rendering | Component mounting and DOM manipulation |
typescript | ^4.5.4 | Type safety and development tooling | Type definitions for all components and APIs |
State Management Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
react-redux | ^7.2.6 | React bindings for Redux | Site off-days state management |
redux | ^4.1.2 | Predictable state container | Site off-days reducer and actions |
redux-saga | ^1.1.3 | Side effect management | Async API calls for site off-days |
react-redux-firebase | ^3.11.0 | Firebase-Redux integration | Organization off-days real-time sync |
State Management Usage:
// Redux usage in site off-days
const siteOffDaysReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_SITE_OFF_DAYS_SUCCESS:
return { ...state, data: action.payload };
// ... other cases
}
};
// Firebase integration for organization off-days
const firestoreConnect = [
{
collection: 'organizationOffDay',
doc: organizationID,
storeAs: 'organizationOffDay'
}
];UI Component Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
react-dates | ^21.8.0 | Advanced calendar components | Main calendar picker for all off-days interfaces |
styled-components | ^5.3.3 | CSS-in-JS styling | Component styling and theming |
react-select | ^5.2.1 | Advanced select components | Year selector and filter dropdowns |
@nimbly-technologies/audit-component | ^1.2.3 | Internal component library | Segmented buttons and form controls |
UI Component Usage Examples:
// React-dates calendar integration
<DayPickerSingleDateController
numberOfMonths={monthCount}
onDateChange={handleToggleDate}
isDayHighlighted={(date) => offDayState.dates[date.format('YYYY-MM-DD')]}
renderDayContents={renderDayContents}
/>
// Styled-components theming
const CalendarContainer = styled.div`
.CalendarDay {
border: 1px solid #eae9f9;
border-radius: 5px;
color: #867ff8;
}
`;Data Visualization Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
chart.js | ^3.7.0 | Chart rendering engine | Monthly non-operational days trend chart |
react-chartjs-2 | ^4.0.1 | React wrapper for Chart.js | Chart component integration |
Chart Usage Example:
<Line
options={{
responsive: true,
scales: {
y: { min: 0, max: 100 }
}
}}
data={{
labels: monthLabels,
datasets: [{
label: 'Non Operational Days',
data: monthValues,
borderColor: '#574FCF',
backgroundColor: gradientFill
}]
}}
/>Date and Time Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
moment | ^2.29.1 | Date manipulation and formatting | Date calculations, formatting, and calendar logic |
Moment.js Usage:
// Date formatting for display
const formattedDate = moment(date).format('DD MMMM YYYY');
// Date range calculations
const startOfWeek = moment().startOf('week').toDate();
const isToday = moment(date).isSame(moment(), 'day');
// Year filtering
const isCurrentYear = moment(date).isSame(selectedYear, 'year');API and HTTP Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
query-string | ^7.1.0 | URL query parameter handling | API request parameter serialization |
Query String Usage:
// API parameter building
const queryParams = queryString.stringify({
type: 'user',
search: searchText,
sortBy: 'name',
order: 'asc',
limit: 10,
page: currentPage
});
const url = `${apiURL}/users/off-days/userOffDaysData?${queryParams}`;Utility Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
lodash | ^4.17.21 | Utility functions | Deep cloning, debouncing, and data manipulation |
clonedeep | ^2.1.0 | Deep object cloning | State immutability in calendar updates |
Utility Usage Examples:
// Deep cloning for immutable updates
const clonedDates = cloneDeep(offDayState);
clonedDates.dates[dateString] = true;
// Debounced search
const debouncedSearch = debounce((text: string) => fetchData(text), 500);Firebase Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
firebase | ^9.6.1 | Firebase SDK | Authentication and Firestore integration |
Firebase Usage:
// Firestore operations
const ref = firestore.collection('organizationOffDay').doc(organizationID);
await ref.set({
dates: newDates,
updatedBy: auth.uid,
updatedAt: firestore.Timestamp.now()
});Internationalization Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
react-i18next | ^11.15.3 | Internationalization framework | Multi-language support for all UI text |
i18next | ^21.6.10 | i18n core library | Translation key management |
i18n Usage:
const { t } = useTranslation();
// Translation usage in components
<Title>{t('message.offDaysPage.title')}</Title>
<Button>{t('button.save')}</Button>
toast.success(t('addOn.offDays.success'));Development Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
@testing-library/react | ^12.1.2 | React component testing | Unit tests for all off-days components |
@testing-library/jest-dom | ^5.16.1 | Custom Jest matchers | Enhanced assertions for component testing |
jest | ^27.4.7 | Testing framework | Test runner and assertion library |
Type Definition Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
@types/react | ^17.0.38 | React type definitions | Component prop types and hooks |
@types/styled-components | ^5.1.21 | Styled-components types | CSS-in-JS type safety |
@types/moment | ^2.13.0 | Moment.js type definitions | Date manipulation type safety |
@nimbly-technologies/nimbly-common | ^2.1.0 | Shared type definitions | Common types across Nimbly systems |
Type Usage Examples:
import { SiteOffdayMongo } from '@nimbly-technologies/nimbly-common';
interface OffDayProps {
type: 'site' | 'organization';
organizationOffDay: OffDays;
siteOffDay?: SiteOffdayMongo;
handleUpdateOffDay: (offDays: OffDays) => void;
}Notification Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
react-toastify | ^8.1.0 | Toast notification system | Success/error feedback for all operations |
Toast Usage:
// Success notifications
toast.success(t('addOn.offDays.success'));
// Error notifications
toast.error(t('addOn.offDays.fail'));
// Custom toast configuration
toast.configure({
position: 'top-right',
autoClose: 3000,
hideProgressBar: false
});Performance Monitoring Dependencies
| Package | Version | Purpose | Usage in Off-Days Module |
|---|---|---|---|
Custom Monitoring | Internal | Error tracking and performance monitoring | Error logging and performance metrics |
Monitoring Usage:
// Error logging
Monitoring.logEvent('OffDaysManager -> handleSave', error);
// Performance tracking
Monitoring.trackTiming('calendar_render_time', renderDuration);Bundle Analysis
Bundle Size Impact
- react-dates: ~150KB (largest single dependency)
- chart.js: ~120KB (dashboard charts)
- moment: ~70KB (date operations)
- styled-components: ~45KB (styling)
- Total Off-Days Module: ~800KB (minified + gzipped: ~200KB)
Code Splitting Strategy
// Route-level splitting
const OffDaysPage = lazy(() => import('pages/offdays'));
const DashboardPage = lazy(() => import('pages/auditorsOffDaysDashboard'));
// Component-level splitting for heavy features
const ChartComponent = lazy(() => import('./TotalNonOffDays'));
const DataTable = lazy(() => import('./OffDaysTable'));This comprehensive dependency analysis ensures proper version management, security updates, and optimal bundle sizing for the Non-Operational Day Management System.