diff --git a/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.test.tsx b/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.test.tsx index ea25585a7..b0a6c75f0 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.test.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.test.tsx @@ -90,9 +90,11 @@ describe('MemberRow device status', () => { expect(screen.getByText('Not Installed').className).toContain('text-muted-foreground'); }); - it('shows "Not Installed" by default when deviceStatus is omitted', () => { + it('shows dash when deviceStatus is omitted (no compliance obligation)', () => { renderMemberRow(); - expect(screen.getByText('Not Installed')).toBeInTheDocument(); + expect(screen.queryByText('Not Installed')).not.toBeInTheDocument(); + expect(screen.queryByText('Compliant')).not.toBeInTheDocument(); + expect(screen.queryByText('Non-Compliant')).not.toBeInTheDocument(); }); it('shows "Compliant" with green dot when deviceStatus is compliant', () => { @@ -177,4 +179,58 @@ describe('MemberRow device status', () => { const redDot = c3.querySelector('.bg-red-400'); expect(redDot).toBeInTheDocument(); }); + + it('does not show device status for member without compliance obligation (e.g. auditor)', () => { + const auditorMember = { + ...baseMember, + role: 'auditor', + } as unknown as MemberWithUser; + + render( + + + + +
, + ); + + expect(screen.queryByText('Not Installed')).not.toBeInTheDocument(); + expect(screen.queryByText('Compliant')).not.toBeInTheDocument(); + expect(screen.queryByText('Non-Compliant')).not.toBeInTheDocument(); + }); + + it('still shows device status for member with compliance obligation', () => { + const employeeMember = { + ...baseMember, + role: 'employee', + } as unknown as MemberWithUser; + + render( + + + + +
, + ); + + expect(screen.getByText('Compliant')).toBeInTheDocument(); + }); }); diff --git a/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.tsx b/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.tsx index 72fef1ac1..655b5b6ae 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.tsx @@ -95,7 +95,7 @@ export function MemberRow({ isCurrentUserOwner, customRoles = [], taskCompletion, - deviceStatus = 'not-installed', + deviceStatus, }: MemberRowProps) { const { orgId } = useParams<{ orgId: string }>(); @@ -233,7 +233,7 @@ export function MemberRow({ {/* DEVICE */} - {isPlatformAdmin || isDeactivated ? ( + {isPlatformAdmin || isDeactivated || !deviceStatus ? ( @@ -321,7 +321,7 @@ export function MemberRow({ )} {!isDeactivated && - (member.fleetDmLabelId || deviceStatus !== 'not-installed') && + (member.fleetDmLabelId || (deviceStatus && deviceStatus !== 'not-installed')) && isCurrentUserOwner && ( { diff --git a/apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx b/apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx index 64d62ef87..ac9b88e7b 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx @@ -470,7 +470,7 @@ export function TeamMembersClient({ isCurrentUserOwner={isCurrentUserOwner} customRoles={customRoles} taskCompletion={taskCompletionMap[(item as MemberWithUser).id]} - deviceStatus={deviceStatusMap[(item as MemberWithUser).id] ?? 'not-installed'} + deviceStatus={deviceStatusMap[(item as MemberWithUser).id]} /> ) : ( = {}; + const complianceMemberIds = new Set(employees.map((m) => m.id)); + + // Default all compliance members to "not-installed" — device data below overrides + for (const id of complianceMemberIds) { + deviceStatusMap[id] = 'not-installed'; + } // Device-agent devices: compliant only if ALL of a member's devices pass const agentComplianceByMember = new Map(); for (const d of agentDevices) { - if (!d.memberId) continue; + if (!d.memberId || !complianceMemberIds.has(d.memberId)) continue; const prev = agentComplianceByMember.get(d.memberId); agentComplianceByMember.set(d.memberId, (prev ?? true) && d.isCompliant); } @@ -173,12 +183,13 @@ export default async function PeoplePage({ params }: { params: Promise<{ orgId: // Fleet-only devices: use the same merged policy data the chart uses // (Fleet API automated checks + DB manual overrides, already combined by getFleetHosts) for (const host of filteredFleetDevices) { - if (!host.member_id) continue; + if (!host.member_id || !complianceMemberIds.has(host.member_id)) continue; // If already set by device-agent, skip (agent takes priority) if (agentComplianceByMember.has(host.member_id)) continue; const isCompliant = host.policies.every((p) => p.response === 'pass'); - // If multiple fleet devices for same member, non-compliant if ANY device fails - if (!isCompliant || !deviceStatusMap[host.member_id]) { + // If multiple fleet devices for same member, non-compliant if ANY device fails. + // Once non-compliant, a later compliant device cannot upgrade it back. + if (deviceStatusMap[host.member_id] !== 'non-compliant') { deviceStatusMap[host.member_id] = isCompliant ? 'compliant' : 'non-compliant'; } }