708 lines
19 KiB
TypeScript
708 lines
19 KiB
TypeScript
import apiClient from './authApi';
|
|
|
|
export interface RequestStats {
|
|
totalRequests: number;
|
|
openRequests: number;
|
|
approvedRequests: number;
|
|
rejectedRequests: number;
|
|
closedRequests: number;
|
|
pausedRequests?: number;
|
|
draftRequests: number;
|
|
changeFromPrevious: {
|
|
total: string;
|
|
open: string;
|
|
approved: string;
|
|
rejected: string;
|
|
};
|
|
}
|
|
|
|
export interface TATEfficiency {
|
|
avgTATCompliance: number;
|
|
avgCycleTimeHours: number;
|
|
avgCycleTimeDays: number;
|
|
delayedWorkflows: number;
|
|
totalCompleted: number;
|
|
compliantWorkflows: number;
|
|
changeFromPrevious: {
|
|
compliance: string;
|
|
cycleTime: string;
|
|
};
|
|
}
|
|
|
|
export interface ApproverLoad {
|
|
pendingActions: number;
|
|
completedToday: number;
|
|
completedThisWeek: number;
|
|
changeFromPrevious: {
|
|
pending: string;
|
|
completed: string;
|
|
};
|
|
}
|
|
|
|
export interface EngagementStats {
|
|
workNotesAdded: number;
|
|
attachmentsUploaded: number;
|
|
changeFromPrevious: {
|
|
workNotes: string;
|
|
attachments: string;
|
|
};
|
|
}
|
|
|
|
export interface AIInsights {
|
|
avgConclusionRemarkLength: number;
|
|
aiSummaryAdoptionPercent: number;
|
|
totalWithConclusion: number;
|
|
aiGeneratedCount: number;
|
|
manualCount: number;
|
|
changeFromPrevious: {
|
|
adoption: string;
|
|
length: string;
|
|
};
|
|
}
|
|
|
|
export interface DashboardKPIs {
|
|
requestVolume: RequestStats;
|
|
tatEfficiency: TATEfficiency;
|
|
approverLoad: ApproverLoad;
|
|
engagement: EngagementStats;
|
|
aiInsights: AIInsights;
|
|
dateRange: {
|
|
start: string;
|
|
end: string;
|
|
label: string;
|
|
};
|
|
}
|
|
|
|
export interface RecentActivity {
|
|
activityId: string;
|
|
requestId: string;
|
|
requestNumber: string;
|
|
requestTitle: string;
|
|
type: string;
|
|
action: string;
|
|
details?: any;
|
|
userId: string;
|
|
userName: string;
|
|
timestamp: string;
|
|
priority: string;
|
|
}
|
|
|
|
export interface CriticalRequest {
|
|
requestId: string;
|
|
requestNumber: string;
|
|
title: string;
|
|
priority: string;
|
|
status: string;
|
|
currentLevel: number;
|
|
totalLevels: number;
|
|
submissionDate: string;
|
|
totalTATHours: number;
|
|
originalTATHours: number;
|
|
breachCount: number;
|
|
isCritical: boolean;
|
|
approverId?: string | null;
|
|
approverEmail?: string | null;
|
|
}
|
|
|
|
export interface AIRemarkUtilization {
|
|
totalUsage: number;
|
|
totalEdits: number;
|
|
editRate: number;
|
|
monthlyTrends: Array<{
|
|
month: string;
|
|
aiUsage: number;
|
|
manualEdits: number;
|
|
}>;
|
|
}
|
|
|
|
export interface ApproverPerformance {
|
|
approverId: string;
|
|
approverName: string;
|
|
totalApproved: number;
|
|
approvedCount: number;
|
|
rejectedCount: number;
|
|
closedCount: number;
|
|
tatCompliancePercent: number;
|
|
avgResponseHours: number;
|
|
pendingCount: number;
|
|
withinTatCount: number;
|
|
breachedCount: number;
|
|
}
|
|
|
|
export interface UpcomingDeadline {
|
|
levelId: string;
|
|
requestId: string;
|
|
requestNumber: string;
|
|
requestTitle: string;
|
|
levelNumber: number;
|
|
approverName: string;
|
|
approverEmail: string;
|
|
tatHours: number;
|
|
elapsedHours: number;
|
|
remainingHours: number;
|
|
tatPercentageUsed: number;
|
|
levelStartTime: string;
|
|
priority: string;
|
|
}
|
|
|
|
export interface DepartmentStats {
|
|
department: string;
|
|
totalRequests: number;
|
|
approved: number;
|
|
rejected: number;
|
|
inProgress: number;
|
|
approvalRate: number;
|
|
}
|
|
|
|
export interface PriorityDistribution {
|
|
priority: string;
|
|
totalCount: number;
|
|
avgCycleTimeHours: number;
|
|
approvedCount: number;
|
|
breachedCount: number;
|
|
complianceRate: number;
|
|
}
|
|
|
|
export type DateRange = 'all' | 'today' | 'week' | 'month' | 'quarter' | 'year' | 'last30days' | 'custom';
|
|
|
|
class DashboardService {
|
|
/**
|
|
* Get all KPI metrics
|
|
*/
|
|
async getKPIs(dateRange?: DateRange, startDate?: Date, endDate?: Date, viewAsUser?: boolean): Promise<DashboardKPIs> {
|
|
try {
|
|
const params: any = { dateRange };
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
if (viewAsUser) {
|
|
params.viewAsUser = 'true';
|
|
}
|
|
const response = await apiClient.get('/dashboard/kpis', { params });
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch KPIs:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get request statistics
|
|
*/
|
|
async getRequestStats(
|
|
dateRange?: DateRange,
|
|
startDate?: string,
|
|
endDate?: string,
|
|
status?: string,
|
|
priority?: string,
|
|
templateType?: string,
|
|
department?: string,
|
|
initiator?: string,
|
|
approver?: string,
|
|
approverType?: 'current' | 'any',
|
|
search?: string,
|
|
slaCompliance?: string,
|
|
viewAsUser?: boolean
|
|
): Promise<RequestStats> {
|
|
try {
|
|
const params: any = { dateRange };
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate;
|
|
params.endDate = endDate;
|
|
}
|
|
// Add filters
|
|
if (status && status !== 'all') {
|
|
params.status = status;
|
|
}
|
|
if (priority && priority !== 'all') {
|
|
params.priority = priority;
|
|
}
|
|
if (templateType && templateType !== 'all') {
|
|
params.templateType = templateType;
|
|
}
|
|
if (department && department !== 'all') {
|
|
params.department = department;
|
|
}
|
|
if (initiator && initiator !== 'all') {
|
|
params.initiator = initiator;
|
|
}
|
|
if (approver && approver !== 'all') {
|
|
params.approver = approver;
|
|
}
|
|
if (approverType) {
|
|
params.approverType = approverType;
|
|
}
|
|
if (search) {
|
|
params.search = search;
|
|
}
|
|
if (slaCompliance && slaCompliance !== 'all') {
|
|
params.slaCompliance = slaCompliance;
|
|
}
|
|
// Pass viewAsUser flag to tell backend to treat admin as normal user
|
|
if (viewAsUser) {
|
|
params.viewAsUser = 'true';
|
|
}
|
|
const response = await apiClient.get('/dashboard/stats/requests', { params });
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch request stats:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get TAT efficiency metrics
|
|
*/
|
|
async getTATEfficiency(dateRange?: DateRange): Promise<TATEfficiency> {
|
|
try {
|
|
const response = await apiClient.get('/dashboard/stats/tat-efficiency', {
|
|
params: { dateRange }
|
|
});
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch TAT efficiency:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get approver load
|
|
*/
|
|
async getApproverLoad(dateRange?: DateRange): Promise<ApproverLoad> {
|
|
try {
|
|
const response = await apiClient.get('/dashboard/stats/approver-load', {
|
|
params: { dateRange }
|
|
});
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch approver load:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get engagement statistics
|
|
*/
|
|
async getEngagementStats(dateRange?: DateRange): Promise<EngagementStats> {
|
|
try {
|
|
const response = await apiClient.get('/dashboard/stats/engagement', {
|
|
params: { dateRange }
|
|
});
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch engagement stats:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get AI insights
|
|
*/
|
|
async getAIInsights(dateRange?: DateRange): Promise<AIInsights> {
|
|
try {
|
|
const response = await apiClient.get('/dashboard/stats/ai-insights', {
|
|
params: { dateRange }
|
|
});
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch AI insights:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get recent activity feed with pagination
|
|
*/
|
|
async getRecentActivity(page: number = 1, limit: number = 10, viewAsUser?: boolean): Promise<{
|
|
activities: RecentActivity[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { page, limit };
|
|
if (viewAsUser) {
|
|
params.viewAsUser = 'true';
|
|
}
|
|
const response = await apiClient.get('/dashboard/activity/recent', { params });
|
|
return {
|
|
activities: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch recent activity:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get critical requests with pagination
|
|
*/
|
|
async getCriticalRequests(page: number = 1, limit: number = 10, viewAsUser?: boolean): Promise<{
|
|
criticalRequests: CriticalRequest[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { page, limit };
|
|
if (viewAsUser) {
|
|
params.viewAsUser = 'true';
|
|
}
|
|
const response = await apiClient.get('/dashboard/requests/critical', { params });
|
|
return {
|
|
criticalRequests: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch critical requests:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get upcoming deadlines with pagination
|
|
*/
|
|
async getUpcomingDeadlines(page: number = 1, limit: number = 10, viewAsUser?: boolean): Promise<{
|
|
deadlines: UpcomingDeadline[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { page, limit };
|
|
if (viewAsUser) {
|
|
params.viewAsUser = 'true';
|
|
}
|
|
const response = await apiClient.get('/dashboard/deadlines/upcoming', { params });
|
|
return {
|
|
deadlines: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch upcoming deadlines:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get department-wise statistics
|
|
*/
|
|
async getDepartmentStats(dateRange?: DateRange, startDate?: Date, endDate?: Date): Promise<DepartmentStats[]> {
|
|
try {
|
|
const params: any = { dateRange };
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
const response = await apiClient.get('/dashboard/stats/by-department', { params });
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch department stats:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get priority distribution
|
|
*/
|
|
async getPriorityDistribution(dateRange?: DateRange, startDate?: Date, endDate?: Date): Promise<PriorityDistribution[]> {
|
|
try {
|
|
const params: any = { dateRange };
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
const response = await apiClient.get('/dashboard/stats/priority-distribution', { params });
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch priority distribution:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get AI Remark Utilization with monthly trends
|
|
*/
|
|
async getAIRemarkUtilization(dateRange?: DateRange, startDate?: Date, endDate?: Date): Promise<AIRemarkUtilization> {
|
|
try {
|
|
const params: any = { dateRange };
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
const response = await apiClient.get('/dashboard/stats/ai-remark-utilization', { params });
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch AI remark utilization:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Approver Performance metrics with pagination
|
|
* Supports priority and SLA filters for consistent stats behavior
|
|
*/
|
|
async getApproverPerformance(
|
|
dateRange?: DateRange,
|
|
page: number = 1,
|
|
limit: number = 10,
|
|
startDate?: Date,
|
|
endDate?: Date,
|
|
priority?: string,
|
|
slaCompliance?: string
|
|
): Promise<{
|
|
performance: ApproverPerformance[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = {
|
|
dateRange,
|
|
page,
|
|
limit: limit || 10 // Explicitly set limit (default 10 if not provided)
|
|
};
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
if (priority && priority !== 'all') {
|
|
params.priority = priority;
|
|
}
|
|
if (slaCompliance && slaCompliance !== 'all') {
|
|
params.slaCompliance = slaCompliance;
|
|
}
|
|
|
|
|
|
const response = await apiClient.get('/dashboard/stats/approver-performance', { params });
|
|
return {
|
|
performance: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch approver performance:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Request Lifecycle Report
|
|
*/
|
|
async getLifecycleReport(
|
|
page: number = 1,
|
|
limit: number = 50,
|
|
dateRange?: DateRange,
|
|
startDate?: Date,
|
|
endDate?: Date
|
|
): Promise<{
|
|
lifecycleData: any[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { page, limit };
|
|
if (dateRange) params.dateRange = dateRange;
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
const response = await apiClient.get('/dashboard/reports/lifecycle', { params });
|
|
return {
|
|
lifecycleData: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch lifecycle report:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get enhanced User Activity Log Report
|
|
*/
|
|
async getActivityLogReport(
|
|
page: number = 1,
|
|
limit: number = 50,
|
|
dateRange?: DateRange,
|
|
filterUserId?: string,
|
|
filterType?: string,
|
|
filterCategory?: string,
|
|
filterSeverity?: string,
|
|
startDate?: Date,
|
|
endDate?: Date
|
|
): Promise<{
|
|
activities: any[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { page, limit, filterUserId, filterType, filterCategory, filterSeverity };
|
|
if (dateRange) params.dateRange = dateRange;
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
const response = await apiClient.get('/dashboard/reports/activity-log', { params });
|
|
return {
|
|
activities: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch activity log report:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get list of departments (metadata for filtering)
|
|
*/
|
|
async getDepartments(): Promise<string[]> {
|
|
try {
|
|
const response = await apiClient.get('/dashboard/metadata/departments');
|
|
return response.data.data.departments || [];
|
|
} catch (error) {
|
|
console.error('Failed to fetch departments:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Workflow Aging Report
|
|
*/
|
|
async getWorkflowAgingReport(
|
|
threshold: number = 7,
|
|
page: number = 1,
|
|
limit: number = 50,
|
|
dateRange?: DateRange,
|
|
startDate?: Date,
|
|
endDate?: Date
|
|
): Promise<{
|
|
agingData: any[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { threshold, page, limit };
|
|
if (dateRange) params.dateRange = dateRange;
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
const response = await apiClient.get('/dashboard/reports/workflow-aging', { params });
|
|
return {
|
|
agingData: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch workflow aging report:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get single approver stats only (dedicated endpoint for performance)
|
|
* Only respects date, priority, and SLA filters
|
|
*/
|
|
async getSingleApproverStats(
|
|
approverId: string,
|
|
dateRange?: DateRange,
|
|
startDate?: Date,
|
|
endDate?: Date,
|
|
priority?: string,
|
|
slaCompliance?: string
|
|
): Promise<ApproverPerformance> {
|
|
try {
|
|
const params: any = { approverId };
|
|
if (dateRange) params.dateRange = dateRange;
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
if (priority && priority !== 'all') params.priority = priority;
|
|
if (slaCompliance && slaCompliance !== 'all') params.slaCompliance = slaCompliance;
|
|
|
|
const response = await apiClient.get('/dashboard/stats/single-approver', { params });
|
|
return response.data.data;
|
|
} catch (error) {
|
|
console.error('Failed to fetch single approver stats:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get requests filtered by approver ID for detailed performance analysis
|
|
*/
|
|
async getRequestsByApprover(
|
|
approverId: string,
|
|
page: number = 1,
|
|
limit: number = 50,
|
|
dateRange?: DateRange,
|
|
startDate?: Date,
|
|
endDate?: Date,
|
|
status?: string,
|
|
priority?: string,
|
|
slaCompliance?: string,
|
|
search?: string
|
|
): Promise<{
|
|
requests: any[],
|
|
pagination: {
|
|
currentPage: number,
|
|
totalPages: number,
|
|
totalRecords: number,
|
|
limit: number
|
|
}
|
|
}> {
|
|
try {
|
|
const params: any = { approverId, page, limit };
|
|
if (dateRange) params.dateRange = dateRange;
|
|
if (dateRange === 'custom' && startDate && endDate) {
|
|
params.startDate = startDate.toISOString();
|
|
params.endDate = endDate.toISOString();
|
|
}
|
|
if (status) params.status = status;
|
|
if (priority) params.priority = priority;
|
|
if (slaCompliance) params.slaCompliance = slaCompliance;
|
|
if (search) params.search = search;
|
|
|
|
const response = await apiClient.get('/dashboard/requests/by-approver', { params });
|
|
return {
|
|
requests: response.data.data,
|
|
pagination: response.data.pagination
|
|
};
|
|
} catch (error) {
|
|
console.error('Failed to fetch requests by approver:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
export const dashboardService = new DashboardService();
|
|
export default dashboardService;
|
|
|