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({
|
prisma.client.count({
|
||||||
where: {
|
where: {
|
||||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||||
policies: { some: {} },
|
policies: { some: { status: { notIn: ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] } } },
|
||||||
setupNaAt: null,
|
setupNaAt: null,
|
||||||
OR: [
|
OR: [
|
||||||
{ claimsAdvocateId: null },
|
{ claimsAdvocateId: null },
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export default async function SetupQueuePage() {
|
||||||
|
|
||||||
const DEAD_STATUSES = ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken']
|
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({
|
prisma.client.findMany({
|
||||||
where: {
|
where: {
|
||||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||||
|
|
@ -59,13 +59,15 @@ export default async function SetupQueuePage() {
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'asc' },
|
orderBy: { createdAt: 'asc' },
|
||||||
}),
|
}),
|
||||||
prisma.client.count({
|
prisma.client.findMany({
|
||||||
where: {
|
where: {
|
||||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||||
setupNaAt: null,
|
setupNaAt: null,
|
||||||
OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }],
|
OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }],
|
||||||
NOT: { policies: { some: { status: { notIn: DEAD_STATUSES } } } },
|
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({
|
prisma.client.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
|
@ -110,11 +112,6 @@ export default async function SetupQueuePage() {
|
||||||
<h1 className="text-3xl font-bold">New Client Setup</h1>
|
<h1 className="text-3xl font-bold">New Client Setup</h1>
|
||||||
<p className="text-muted-foreground mt-1">
|
<p className="text-muted-foreground mt-1">
|
||||||
Clients that need a claims advocate or renewal group configuration.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="secondary" className="text-base px-3 py-1">
|
<Badge variant="secondary" className="text-base px-3 py-1">
|
||||||
|
|
@ -216,6 +213,52 @@ export default async function SetupQueuePage() {
|
||||||
</Card>
|
</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 && (
|
{naClients.length > 0 && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,11 @@ export async function GET(request: NextRequest) {
|
||||||
const { searchParams } = new URL(request.url)
|
const { searchParams } = new URL(request.url)
|
||||||
const countOnly = searchParams.get('count') === 'true'
|
const countOnly = searchParams.get('count') === 'true'
|
||||||
|
|
||||||
|
const DEAD_STATUSES = ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken']
|
||||||
|
|
||||||
const where = {
|
const where = {
|
||||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||||
policies: { some: {} },
|
policies: { some: { status: { notIn: DEAD_STATUSES } } },
|
||||||
setupNaAt: null,
|
setupNaAt: null,
|
||||||
OR: [
|
OR: [
|
||||||
{ claimsAdvocateId: null },
|
{ claimsAdvocateId: null },
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export async function GET(request: NextRequest) {
|
||||||
prisma.client.count({
|
prisma.client.count({
|
||||||
where: {
|
where: {
|
||||||
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
designation: { name: { in: ['Shape', 'Shape 2'] } },
|
||||||
|
policies: { some: { status: { notIn: ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] } } },
|
||||||
setupNaAt: null,
|
setupNaAt: null,
|
||||||
OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }],
|
OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue