Skip to content

Commit 002b36e

Browse files
authored
Merge pull request #2465 from trycompai/main
[comp] Production Deploy
2 parents d24998d + 8ac2416 commit 002b36e

File tree

4 files changed

+65
-17
lines changed

4 files changed

+65
-17
lines changed

apps/app/src/app/(app)/[orgId]/people/all/components/MemberRow.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { MultiRoleCombobox } from './MultiRoleCombobox';
3939
import { RemoveDeviceAlert } from './RemoveDeviceAlert';
4040
import { RemoveMemberAlert } from './RemoveMemberAlert';
4141
import type { CustomRoleOption } from './MultiRoleCombobox';
42-
import type { MemberWithUser } from './TeamMembers';
42+
import type { MemberWithUser, TaskCompletion } from './TeamMembers';
4343

4444
interface MemberRowProps {
4545
member: MemberWithUser;
@@ -50,7 +50,7 @@ interface MemberRowProps {
5050
canEdit: boolean;
5151
isCurrentUserOwner: boolean;
5252
customRoles?: CustomRoleOption[];
53-
taskCompletion?: { completed: number; total: number };
53+
taskCompletion?: TaskCompletion;
5454
hasDeviceAgentDevice?: boolean;
5555
}
5656

@@ -254,16 +254,28 @@ export function MemberRow({
254254
{/* TASKS */}
255255
<TableCell>
256256
{taskCompletion ? (
257-
<div className="w-[170px]">
257+
<div className="w-[220px]">
258258
<div className="h-2 w-full overflow-hidden rounded-full bg-muted">
259259
<div
260260
className="h-full bg-primary transition-all"
261261
style={{ width: `${taskProgressPercent}%` }}
262262
/>
263263
</div>
264-
<Text size="xs" variant="muted">
265-
{taskCompletion.completed}/{taskCompletion.total} complete
266-
</Text>
264+
<div className="mt-1 flex flex-wrap gap-x-2 gap-y-0.5">
265+
<Text size="xs" variant="muted">
266+
Policies {taskCompletion.policies.completed}/{taskCompletion.policies.total}
267+
</Text>
268+
{taskCompletion.training.total > 0 && (
269+
<Text size="xs" variant="muted">
270+
Training {taskCompletion.training.completed}/{taskCompletion.training.total}
271+
</Text>
272+
)}
273+
{taskCompletion.hipaa && (
274+
<Text size="xs" variant="muted">
275+
HIPAA {taskCompletion.hipaa.completed}/{taskCompletion.hipaa.total}
276+
</Text>
277+
)}
278+
</div>
267279
</div>
268280
) : (
269281
<Text size="sm" variant="muted">

apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembers.tsx

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { filterComplianceMembers } from '@/lib/compliance';
2+
import { HIPAA_TRAINING_ID } from '@/lib/data/hipaa-training-content';
23
import { trainingVideos as trainingVideosData } from '@/lib/data/training-videos';
34
import { serverApi } from '@/lib/server-api-client';
45
import type { Invitation, Member, User } from '@db';
@@ -15,6 +16,14 @@ export interface TeamMembersData {
1516
pendingInvitations: Invitation[];
1617
}
1718

19+
export interface TaskCompletion {
20+
completed: number;
21+
total: number;
22+
policies: { completed: number; total: number };
23+
training: { completed: number; total: number };
24+
hipaa?: { completed: number; total: number };
25+
}
26+
1827
export interface TeamMembersProps {
1928
canManageMembers: boolean;
2029
canInviteUsers: boolean;
@@ -51,7 +60,7 @@ export async function TeamMembers(props: TeamMembersProps) {
5160
const employeeSyncData = await getEmployeeSyncConnections(organizationId);
5261

5362
// Build task completion map for employees/contractors
54-
const taskCompletionMap: Record<string, { completed: number; total: number }> = {};
63+
const taskCompletionMap: Record<string, TaskCompletion> = {};
5564

5665
const employeeMembers = await filterComplianceMembers(members, organizationId);
5766

@@ -69,10 +78,17 @@ export async function TeamMembers(props: TeamMembersProps) {
6978
];
7079

7180
if (employeeMembers.length > 0) {
72-
const org = await db.organization.findUnique({
73-
where: { id: organizationId },
74-
select: { securityTrainingStepEnabled: true },
75-
});
81+
const [org, hipaaInstance] = await Promise.all([
82+
db.organization.findUnique({
83+
where: { id: organizationId },
84+
select: { securityTrainingStepEnabled: true },
85+
}),
86+
db.frameworkInstance.findFirst({
87+
where: { organizationId, framework: { name: 'HIPAA' } },
88+
select: { id: true },
89+
}),
90+
]);
91+
const hasHipaaFramework = !!hipaaInstance;
7692

7793
const policies = await db.policy.findMany({
7894
where: {
@@ -92,20 +108,40 @@ export async function TeamMembers(props: TeamMembersProps) {
92108

93109
const totalPolicies = policies.length;
94110
const totalTrainingVideos = org?.securityTrainingStepEnabled ? trainingVideosData.length : 0;
95-
const totalTasks = totalPolicies + totalTrainingVideos;
111+
const totalHipaaTraining = hasHipaaFramework ? 1 : 0;
112+
const totalTasks = totalPolicies + totalTrainingVideos + totalHipaaTraining;
96113

97114
for (const employee of employeeMembers) {
98115
const policiesCompleted = policies.filter((p) => p.signedBy.includes(employee.id)).length;
99116

100117
const trainingsCompleted = org?.securityTrainingStepEnabled
101118
? trainingCompletions.filter(
102-
(tc) => tc.memberId === employee.id && tc.completedAt !== null,
119+
(tc) =>
120+
tc.memberId === employee.id &&
121+
tc.completedAt !== null &&
122+
tc.videoId !== HIPAA_TRAINING_ID,
103123
).length
104124
: 0;
105125

126+
const hipaaCompleted =
127+
hasHipaaFramework &&
128+
trainingCompletions.some(
129+
(tc) =>
130+
tc.memberId === employee.id &&
131+
tc.videoId === HIPAA_TRAINING_ID &&
132+
tc.completedAt !== null,
133+
)
134+
? 1
135+
: 0;
136+
106137
taskCompletionMap[employee.id] = {
107-
completed: policiesCompleted + trainingsCompleted,
138+
completed: policiesCompleted + trainingsCompleted + hipaaCompleted,
108139
total: totalTasks,
140+
policies: { completed: policiesCompleted, total: totalPolicies },
141+
training: { completed: trainingsCompleted, total: totalTrainingVideos },
142+
...(hasHipaaFramework && {
143+
hipaa: { completed: hipaaCompleted, total: 1 },
144+
}),
109145
};
110146
}
111147
}

apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { apiClient } from '@/lib/api-client';
3939
import { buildDisplayItems, filterDisplayItems } from './filter-members';
4040
import { MemberRow } from './MemberRow';
4141
import { PendingInvitationRow } from './PendingInvitationRow';
42-
import type { MemberWithUser, TeamMembersData } from './TeamMembers';
42+
import type { MemberWithUser, TaskCompletion, TeamMembersData } from './TeamMembers';
4343

4444
import type { EmployeeSyncConnectionsData } from '../data/queries';
4545
import { useEmployeeSync } from '../hooks/useEmployeeSync';
@@ -52,7 +52,7 @@ interface TeamMembersClientProps {
5252
isAuditor: boolean;
5353
isCurrentUserOwner: boolean;
5454
employeeSyncData: EmployeeSyncConnectionsData;
55-
taskCompletionMap: Record<string, { completed: number; total: number }>;
55+
taskCompletionMap: Record<string, TaskCompletion>;
5656
memberIdsWithDeviceAgent: string[];
5757
}
5858

apps/app/src/app/(app)/[orgId]/people/dashboard/components/EmployeeCompletionChart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ export function EmployeeCompletionChart({
329329
}
330330

331331
function TaskBarChart({ stat }: { stat: EmployeeTaskStats }) {
332-
const totalCompleted = stat.policiesCompleted + stat.trainingsCompleted;
332+
const totalCompleted = stat.policiesCompleted + stat.trainingsCompleted + (stat.hipaaCompleted ? 1 : 0);
333333
const totalIncomplete = stat.totalTasks - totalCompleted;
334334
const barHeight = 12;
335335

0 commit comments

Comments
 (0)