Fix Manager tab KPIs and workload metrics
- setupQueueCount: add policies: { some: {} } filter (match setup-queue API)
- recentTasks: scope to Shape clients, order by updatedAt not createdAt
- Workload API: scope all metrics to Shape clients only
- Workload API: filter tasks by task.department not assignee department
- Workload API: exclude NA/CANCELLED from totalTasks and completion rate denominator
- Workload API: scope userAssignments filter to match department/client scope
This commit is contained in:
parent
c40dd68360
commit
5934ff7c4f
2 changed files with 29 additions and 23 deletions
|
|
@ -25,6 +25,7 @@ export default async function ManagerPage() {
|
||||||
prisma.client.count({
|
prisma.client.count({
|
||||||
where: {
|
where: {
|
||||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||||
|
policies: { some: {} },
|
||||||
OR: [
|
OR: [
|
||||||
{ claimsAdvocateId: null },
|
{ claimsAdvocateId: null },
|
||||||
{ setupCompletedAt: null },
|
{ setupCompletedAt: null },
|
||||||
|
|
@ -70,26 +71,20 @@ export default async function ManagerPage() {
|
||||||
orderBy: [{ department: 'asc' }, { displayName: 'asc' }],
|
orderBy: [{ department: 'asc' }, { displayName: 'asc' }],
|
||||||
})
|
})
|
||||||
|
|
||||||
// Fetch recent tasks across the team
|
// Fetch recent tasks across the team — Shape clients, recently updated/completed
|
||||||
const recentTasks = await prisma.task.findMany({
|
const recentTasks = await prisma.task.findMany({
|
||||||
take: 10,
|
take: 10,
|
||||||
orderBy: { createdAt: 'desc' },
|
where: {
|
||||||
|
status: { in: ['COMPLETED', 'IN_PROGRESS', 'NOT_STARTED'] },
|
||||||
|
client: { designation: { name: { in: ['Shape', 'Shape 2'] } } },
|
||||||
|
},
|
||||||
|
orderBy: { updatedAt: 'desc' },
|
||||||
include: {
|
include: {
|
||||||
client: {
|
client: { select: { name: true } },
|
||||||
select: {
|
|
||||||
name: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
assignments: {
|
assignments: {
|
||||||
include: {
|
include: { user: { select: { displayName: true } } },
|
||||||
user: {
|
},
|
||||||
select: {
|
},
|
||||||
displayName: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
|
||||||
import { getServerSession } from 'next-auth'
|
import { getServerSession } from 'next-auth'
|
||||||
import { authOptions } from '@/lib/auth'
|
import { authOptions } from '@/lib/auth'
|
||||||
import { prisma } from '@/lib/db'
|
import { prisma } from '@/lib/db'
|
||||||
|
import { DepartmentType } from '@prisma/client'
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -18,10 +19,18 @@ export async function GET(request: NextRequest) {
|
||||||
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
|
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||||||
const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
|
const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
|
||||||
|
|
||||||
// Base task filter — scoped to assignees in the department when filtering
|
// Shape client IDs for scoping
|
||||||
const deptTaskWhere = departmentFilter
|
const shapeClients = await prisma.client.findMany({
|
||||||
? { assignments: { some: { user: { department: { equals: departmentFilter, mode: 'insensitive' as const }, isActive: true } } } }
|
where: { designation: { name: { in: ['Shape', 'Shape 2'] } } },
|
||||||
: {}
|
select: { id: true },
|
||||||
|
})
|
||||||
|
const shapeClientIds = shapeClients.map((c) => c.id)
|
||||||
|
|
||||||
|
// Base task filter — scoped to Shape clients only
|
||||||
|
const deptTaskWhere = {
|
||||||
|
clientId: { in: shapeClientIds },
|
||||||
|
...(departmentFilter ? { department: departmentFilter.toUpperCase() as DepartmentType } : {}),
|
||||||
|
}
|
||||||
|
|
||||||
// Base user filter
|
// Base user filter
|
||||||
const deptUserWhere = departmentFilter
|
const deptUserWhere = departmentFilter
|
||||||
|
|
@ -112,6 +121,7 @@ export async function GET(request: NextRequest) {
|
||||||
email: true,
|
email: true,
|
||||||
department: true,
|
department: true,
|
||||||
taskAssignments: {
|
taskAssignments: {
|
||||||
|
where: { task: deptTaskWhere },
|
||||||
include: {
|
include: {
|
||||||
task: {
|
task: {
|
||||||
select: {
|
select: {
|
||||||
|
|
@ -149,12 +159,12 @@ export async function GET(request: NextRequest) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
// Total tasks
|
// Total tasks
|
||||||
prisma.task.count({ where: deptTaskWhere }),
|
prisma.task.count({ where: { ...deptTaskWhere, status: { notIn: ['NA', 'CANCELLED'] } } }),
|
||||||
// Active users with task assignments
|
// Active users with task assignments
|
||||||
prisma.user.count({
|
prisma.user.count({
|
||||||
where: {
|
where: {
|
||||||
...deptUserWhere,
|
...deptUserWhere,
|
||||||
taskAssignments: { some: {} },
|
taskAssignments: { some: { task: deptTaskWhere } },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
@ -192,13 +202,14 @@ export async function GET(request: NextRequest) {
|
||||||
// Sort users by active tasks (highest workload first)
|
// Sort users by active tasks (highest workload first)
|
||||||
userMetrics.sort((a, b) => b.activeTasks - a.activeTasks)
|
userMetrics.sort((a, b) => b.activeTasks - a.activeTasks)
|
||||||
|
|
||||||
// Calculate summary metrics
|
// Calculate summary metrics (exclude NA/CANCELLED from all rate calculations)
|
||||||
const totalActive = tasksByStatus
|
const totalActive = tasksByStatus
|
||||||
.filter((s) => !['COMPLETED', 'CANCELLED', 'NA'].includes(s.status))
|
.filter((s) => !['COMPLETED', 'CANCELLED', 'NA'].includes(s.status))
|
||||||
.reduce((sum, s) => sum + s._count.id, 0)
|
.reduce((sum, s) => sum + s._count.id, 0)
|
||||||
|
|
||||||
const totalCompleted = tasksByStatus.find((s) => s.status === 'COMPLETED')?._count.id || 0
|
const totalCompleted = tasksByStatus.find((s) => s.status === 'COMPLETED')?._count.id || 0
|
||||||
|
|
||||||
|
// totalTasks already excludes NA/CANCELLED (see query above)
|
||||||
const completionRate = totalTasks > 0 ? Math.round((totalCompleted / totalTasks) * 100) : 0
|
const completionRate = totalTasks > 0 ? Math.round((totalCompleted / totalTasks) * 100) : 0
|
||||||
|
|
||||||
// Calculate workload distribution
|
// Calculate workload distribution
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue