TAT issue in dashboard scren for upcoming deadline

This commit is contained in:
laxmanhalaki 2025-11-07 20:50:20 +05:30
parent c7c9b62358
commit 826c0eedea

View File

@ -10,6 +10,7 @@ import { Op, QueryTypes } from 'sequelize';
import { sequelize } from '@config/database';
import dayjs from 'dayjs';
import logger from '@utils/logger';
import { calculateSLAStatus } from '@utils/tatTimeUtils';
interface DateRangeFilter {
start: Date;
@ -226,7 +227,7 @@ export class DashboardService {
FROM approval_levels al
JOIN workflow_requests wf ON al.request_id = wf.request_id
WHERE al.approver_id = :userId
AND al.status = 'PENDING'
AND al.status = 'IN_PROGRESS'
AND wf.status IN ('PENDING', 'IN_PROGRESS')
AND wf.is_draft = false
AND al.level_number = wf.current_level
@ -461,7 +462,7 @@ export class DashboardService {
WHERE al.request_id = wf.request_id
AND al.approver_id = :userId
AND al.level_number = wf.current_level
AND al.status = 'PENDING'
AND al.status = 'IN_PROGRESS'
)
)` : ''}
`;
@ -484,23 +485,19 @@ export class DashboardService {
AND ta.is_breached = true
) AS breach_count,
(
SELECT SUM(al.tat_hours)
FROM approval_levels al
WHERE al.request_id = wf.request_id
) AS total_allocated_tat,
-- Calculate current level's remaining TAT dynamically
(
SELECT
CASE
WHEN al.level_start_time IS NOT NULL AND al.tat_hours IS NOT NULL THEN
GREATEST(0, al.tat_hours - (EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0))
ELSE al.tat_hours
END
SELECT al.tat_hours
FROM approval_levels al
WHERE al.request_id = wf.request_id
AND al.level_number = wf.current_level
LIMIT 1
) AS current_level_remaining_hours
) AS current_level_tat_hours,
(
SELECT al.level_start_time
FROM approval_levels al
WHERE al.request_id = wf.request_id
AND al.level_number = wf.current_level
LIMIT 1
) AS current_level_start_time
FROM workflow_requests wf
${whereClause}
AND (
@ -523,19 +520,40 @@ export class DashboardService {
type: QueryTypes.SELECT
});
return criticalRequests.map((req: any) => ({
// Calculate working hours TAT for each critical request's current level
const criticalWithSLA = await Promise.all(criticalRequests.map(async (req: any) => {
const priority = (req.priority || 'standard').toLowerCase();
const currentLevelTatHours = parseFloat(req.current_level_tat_hours) || 0;
const currentLevelStartTime = req.current_level_start_time;
let currentLevelRemainingHours = currentLevelTatHours;
if (currentLevelStartTime && currentLevelTatHours > 0) {
try {
// Use working hours calculation for current level
const slaData = await calculateSLAStatus(currentLevelStartTime, currentLevelTatHours, priority);
currentLevelRemainingHours = slaData.remainingHours;
} catch (error) {
logger.error(`[Dashboard] Error calculating SLA for critical request ${req.request_id}:`, error);
}
}
return {
requestId: req.request_id,
requestNumber: req.request_number,
title: req.title,
priority: (req.priority || '').toLowerCase(),
priority,
status: (req.status || '').toLowerCase(),
currentLevel: req.current_level,
totalLevels: req.total_levels,
submissionDate: req.submission_date,
totalTATHours: parseFloat(req.current_level_remaining_hours) || 0, // Use current level remaining
totalTATHours: currentLevelRemainingHours, // Current level remaining hours
breachCount: req.breach_count || 0,
isCritical: req.breach_count > 0 || req.priority === 'EXPRESS'
};
}));
return criticalWithSLA;
}
/**
@ -546,14 +564,14 @@ export class DashboardService {
const user = await User.findByPk(userId);
const isAdmin = (user as any)?.isAdmin || false;
// For regular users: only show levels where they are approver OR requests they initiated
// For regular users: only show CURRENT LEVEL where they are the approver
// For admins: show all current active levels
let whereClause = `
WHERE al.status IN ('PENDING', 'IN_PROGRESS')
WHERE wf.status IN ('PENDING', 'IN_PROGRESS')
AND wf.is_draft = false
${!isAdmin ? `AND (
al.approver_id = :userId
OR wf.initiator_id = :userId
)` : ''}
AND al.status = 'IN_PROGRESS'
AND al.level_number = wf.current_level
${!isAdmin ? `AND al.approver_id = :userId` : ''}
`;
const deadlines = await sequelize.query(`
@ -568,49 +586,61 @@ export class DashboardService {
wf.request_number,
wf.title AS request_title,
wf.priority,
-- Calculate elapsed hours dynamically
CASE
WHEN al.level_start_time IS NOT NULL THEN
EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0
ELSE 0
END AS elapsed_hours,
-- Calculate remaining hours dynamically
CASE
WHEN al.level_start_time IS NOT NULL AND al.tat_hours IS NOT NULL THEN
GREATEST(0, al.tat_hours - (EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0))
ELSE al.tat_hours
END AS remaining_hours,
-- Calculate percentage used dynamically
CASE
WHEN al.level_start_time IS NOT NULL AND al.tat_hours IS NOT NULL AND al.tat_hours > 0 THEN
LEAST(100, ((EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0) / al.tat_hours) * 100)
ELSE 0
END AS tat_percentage_used
wf.current_level,
wf.total_levels
FROM approval_levels al
JOIN workflow_requests wf ON al.request_id = wf.request_id
${whereClause}
ORDER BY tat_percentage_used DESC, remaining_hours ASC
ORDER BY al.level_start_time ASC
LIMIT :limit
`, {
replacements: { userId, limit },
type: QueryTypes.SELECT
});
return deadlines.map((d: any) => ({
// Calculate working hours TAT for each deadline
const deadlinesWithSLA = await Promise.all(deadlines.map(async (d: any) => {
const priority = (d.priority || 'standard').toLowerCase();
const tatHours = parseFloat(d.tat_hours) || 0;
const levelStartTime = d.level_start_time;
let elapsedHours = 0;
let remainingHours = tatHours;
let tatPercentageUsed = 0;
if (levelStartTime && tatHours > 0) {
try {
// Use working hours calculation (same as RequestDetail screen)
const slaData = await calculateSLAStatus(levelStartTime, tatHours, priority);
elapsedHours = slaData.elapsedHours;
remainingHours = slaData.remainingHours;
tatPercentageUsed = slaData.percentageUsed;
} catch (error) {
logger.error(`[Dashboard] Error calculating SLA for level ${d.level_id}:`, error);
}
}
return {
levelId: d.level_id,
requestId: d.request_id,
requestNumber: d.request_number,
requestTitle: d.request_title,
levelNumber: d.level_number,
currentLevel: d.current_level,
totalLevels: d.total_levels,
approverName: d.approver_name,
approverEmail: d.approver_email,
tatHours: parseFloat(d.tat_hours) || 0,
elapsedHours: parseFloat(d.elapsed_hours) || 0,
remainingHours: parseFloat(d.remaining_hours) || 0,
tatPercentageUsed: parseFloat(d.tat_percentage_used) || 0,
levelStartTime: d.level_start_time,
priority: (d.priority || '').toLowerCase()
tatHours,
elapsedHours,
remainingHours,
tatPercentageUsed,
levelStartTime,
priority
};
}));
// Sort by TAT percentage used (descending) and return
return deadlinesWithSLA.sort((a, b) => b.tatPercentageUsed - a.tatPercentageUsed);
}
/**