fix(setup-queue): surface dead-policy clients hidden behind badge count
The Manager nav badge, dashboard tile, and metrics gauge counted Shape/ Shape 2 clients missing an advocate/setup with ANY policy, while the Setup Queue page itself required an ACTIVE (non-dead-status) policy. Clients with only Cancelled/Expired/etc policies inflated the badge but never appeared anywhere except a bare "(N skipped)" note with no names. - setup/page.tsx: replace the bare skipped count with a full "No Active Policies" card listing each client (linked), policy count, what's missing, and a Mark N/A action - Align the active-policy filter across setup-queue API, manager dashboard count, and the Prometheus setup-queue gauge so all four surfaces agree on what's actually actionable
This commit is contained in:
parent
01ecfacd04
commit
6a5006822b
4 changed files with 55 additions and 9 deletions
|
|
@ -26,7 +26,7 @@ export default async function ManagerPage() {
|
|||
prisma.client.count({
|
||||
where: {
|
||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||
policies: { some: {} },
|
||||
policies: { some: { status: { notIn: ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] } } },
|
||||
setupNaAt: null,
|
||||
OR: [
|
||||
{ claimsAdvocateId: null },
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default async function SetupQueuePage() {
|
|||
|
||||
const DEAD_STATUSES = ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken']
|
||||
|
||||
const [clients, skippedCount, naClients] = await Promise.all([
|
||||
const [clients, skippedClients, naClients] = await Promise.all([
|
||||
prisma.client.findMany({
|
||||
where: {
|
||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||
|
|
@ -59,13 +59,15 @@ export default async function SetupQueuePage() {
|
|||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
}),
|
||||
prisma.client.count({
|
||||
prisma.client.findMany({
|
||||
where: {
|
||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||
setupNaAt: null,
|
||||
OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }],
|
||||
NOT: { policies: { some: { status: { notIn: DEAD_STATUSES } } } },
|
||||
},
|
||||
select: { id: true, name: true, claimsAdvocateId: true, setupCompletedAt: true, _count: { select: { policies: true } } },
|
||||
orderBy: { name: 'asc' },
|
||||
}),
|
||||
prisma.client.findMany({
|
||||
where: {
|
||||
|
|
@ -110,11 +112,6 @@ export default async function SetupQueuePage() {
|
|||
<h1 className="text-3xl font-bold">New Client Setup</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
Clients that need a claims advocate or renewal group configuration.
|
||||
{skippedCount > 0 && (
|
||||
<span className="ml-2 text-xs text-muted-foreground/70">
|
||||
({skippedCount} skipped — no active policies)
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="secondary" className="text-base px-3 py-1">
|
||||
|
|
@ -216,6 +213,52 @@ export default async function SetupQueuePage() {
|
|||
</Card>
|
||||
)}
|
||||
|
||||
{skippedClients.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">No Active Policies ({skippedClients.length})</CardTitle>
|
||||
<CardDescription>
|
||||
Shape/Shape 2 clients missing an advocate or setup completion, but with no
|
||||
active (non-dead) policies — likely lost business or a data cleanup issue.
|
||||
Not shown in the queue above since there's nothing to configure yet.
|
||||
Mark N/A once confirmed, or assign an advocate if the client is still active.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b bg-muted/40">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Client</th>
|
||||
<th className="px-4 py-3 text-center font-medium text-muted-foreground">Total Policies</th>
|
||||
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Missing</th>
|
||||
<th className="px-4 py-3" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{skippedClients.map((c) => (
|
||||
<tr key={c.id} className="hover:bg-muted/30 transition-colors">
|
||||
<td className="px-4 py-3 font-medium">
|
||||
<Link href={`/clients/${c.id}`} className="hover:underline text-primary">
|
||||
{c.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-center text-muted-foreground">{c._count.policies}</td>
|
||||
<td className="px-4 py-3 text-muted-foreground">
|
||||
{[!c.claimsAdvocateId && 'Advocate', !c.setupCompletedAt && 'Setup'].filter(Boolean).join(', ')}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<SetupNaButton clientId={c.id} clientName={c.name} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{naClients.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ export async function GET(request: NextRequest) {
|
|||
const { searchParams } = new URL(request.url)
|
||||
const countOnly = searchParams.get('count') === 'true'
|
||||
|
||||
const DEAD_STATUSES = ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken']
|
||||
|
||||
const where = {
|
||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||
policies: { some: {} },
|
||||
policies: { some: { status: { notIn: DEAD_STATUSES } } },
|
||||
setupNaAt: null,
|
||||
OR: [
|
||||
{ claimsAdvocateId: null },
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export async function GET(request: NextRequest) {
|
|||
prisma.client.count({
|
||||
where: {
|
||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||
policies: { some: { status: { notIn: ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] } } },
|
||||
setupNaAt: null,
|
||||
OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }],
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue