Skip to content

Commit 2d87914

Browse files
authored
feat: add contractor role (#1735)
* feat: add contractor role * feat: update role checks to include contractor in various actions
1 parent df1f8ce commit 2d87914

File tree

15 files changed

+59
-17
lines changed

15 files changed

+59
-17
lines changed

apps/app/src/actions/organization/remove-employee.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,18 @@ export const removeEmployeeRoleOrMember = authActionClient
6767
};
6868
}
6969

70-
// 3. Check if target has 'employee' role
70+
// 3. Check if target has 'employee' or 'contractor' role
7171
const roles = targetMember.role.split(',').filter(Boolean); // Handle empty strings/commas
72-
if (!roles.includes('employee')) {
72+
if (!roles.includes('employee') && !roles.includes('contractor')) {
7373
return {
7474
success: false,
75-
error: 'Target member does not have the employee role.',
75+
error: 'Target member does not have the employee or contractor role.',
7676
};
7777
}
7878

7979
// 4. Logic: Remove role or delete member
80-
if (roles.length === 1 && roles[0] === 'employee') {
81-
// Only has employee role - delete member fully
80+
if (roles.length === 1 && (roles[0] === 'employee' || roles[0] === 'contractor')) {
81+
// Only has employee or contractor role - delete member fully
8282

8383
// Cannot remove owner (shouldn't happen if only role is employee, but safety check)
8484
if (targetMember.role === 'owner') {

apps/app/src/actions/policies/accept-requested-policy-changes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ export const acceptRequestedPolicyChangesAction = authActionClient
8888
},
8989
});
9090

91-
// Filter to get only employees
91+
// Filter to get only employees and contractors
9292
const employeeMembers = employees.filter((member) => {
9393
const roles = member.role.includes(',') ? member.role.split(',') : [member.role];
94-
return roles.includes('employee');
94+
return roles.includes('employee') || roles.includes('contractor');
9595
});
9696

9797
// Prepare the events array for the API

apps/app/src/app/(app)/[orgId]/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default async function Layout({
6363
return redirect('/auth/unauthorized');
6464
}
6565

66-
if (member.role === 'employee') {
66+
if (member.role === 'employee' || member.role === 'contractor') {
6767
return redirect('/no-access');
6868
}
6969

apps/app/src/app/(app)/[orgId]/people/[employeeId]/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ const getPoliciesTasks = async (employeeId: string) => {
113113
where: {
114114
organizationId: organizationId,
115115
status: 'published',
116+
isRequiredToSign: true,
117+
isArchived: false,
116118
},
117119
orderBy: {
118120
name: 'asc',

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ import { addEmployeeWithoutInvite } from '../actions/addEmployeeWithoutInvite';
3535
import { MultiRoleCombobox } from './MultiRoleCombobox';
3636

3737
// --- Constants for Roles ---
38-
const selectableRoles = ['admin', 'auditor', 'employee'] as const satisfies Readonly<
38+
const selectableRoles = ['admin', 'auditor', 'employee', 'contractor'] as const satisfies Readonly<
3939
[Role, ...Role[]]
4040
>;
4141
type InviteRole = (typeof selectableRoles)[number];
4242
const DEFAULT_ROLES: InviteRole[] = [];
4343

4444
// Type guard to check if a string is a valid InviteRole
4545
const isInviteRole = (role: string): role is InviteRole => {
46-
return role === 'admin' || role === 'auditor' || role === 'employee';
46+
return role === 'admin' || role === 'auditor' || role === 'employee' || role === 'contractor';
4747
};
4848

4949
// --- Schemas ---
@@ -159,7 +159,8 @@ export function InviteMembersModal({
159159
// Process each invitation sequentially
160160
for (const invite of values.manualInvites) {
161161
const hasEmployeeRoleAndNoAdmin =
162-
!invite.roles.includes('admin') && invite.roles.includes('employee');
162+
!invite.roles.includes('admin') &&
163+
(invite.roles.includes('employee') || invite.roles.includes('contractor'));
163164
try {
164165
if (hasEmployeeRoleAndNoAdmin) {
165166
await addEmployeeWithoutInvite({
@@ -320,7 +321,8 @@ export function InviteMembersModal({
320321

321322
// Attempt to invite
322323
const hasEmployeeRoleAndNoAdmin =
323-
validRoles.includes('employee') && !validRoles.includes('admin');
324+
(validRoles.includes('employee') || validRoles.includes('contractor')) &&
325+
!validRoles.includes('admin');
324326
try {
325327
if (hasEmployeeRoleAndNoAdmin) {
326328
await addEmployeeWithoutInvite({

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ export function MemberRow({ member, onRemove, onUpdateRole, canEdit }: MemberRow
168168
return 'Auditor';
169169
case 'employee':
170170
return 'Employee';
171+
case 'contractor':
172+
return 'Contractor';
171173
default:
172174
return '???';
173175
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ const selectableRoles: {
2828
labelKey: 'people.roles.employee',
2929
descriptionKey: 'people.roles.employee_description',
3030
},
31+
{
32+
value: 'contractor',
33+
labelKey: 'people.roles.contractor',
34+
descriptionKey: 'people.roles.contractor_description',
35+
},
3136
{
3237
value: 'auditor',
3338
labelKey: 'people.roles.auditor',
@@ -95,6 +100,8 @@ export function MultiRoleCombobox({
95100
return 'Auditor';
96101
case 'employee':
97102
return 'Employee';
103+
case 'contractor':
104+
return 'Contractor';
98105
default:
99106
return roleValue;
100107
}
@@ -112,6 +119,8 @@ export function MultiRoleCombobox({
112119
return 'Auditor';
113120
case 'employee':
114121
return 'Employee';
122+
case 'contractor':
123+
return 'Contractor';
115124
case 'owner':
116125
return 'Owner';
117126
default:

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export function MultiRoleComboboxContent({
4242
return 'Auditor';
4343
case 'employee':
4444
return 'Employee';
45+
case 'contractor':
46+
return 'Contractor';
4547
default:
4648
return roleValue;
4749
}
@@ -57,6 +59,8 @@ export function MultiRoleComboboxContent({
5759
return 'Read-only access for compliance checks.';
5860
case 'employee':
5961
return 'Can sign policies and complete training.';
62+
case 'contractor':
63+
return 'Can sign policies and complete training.';
6064
default:
6165
return '';
6266
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ export async function EmployeesOverview() {
4949

5050
employees = fetchedMembers.filter((member) => {
5151
const roles = member.role.includes(',') ? member.role.split(',') : [member.role];
52-
return roles.includes('employee');
52+
return roles.includes('employee') || roles.includes('contractor');
5353
});
5454

55-
// Fetch required policies
55+
// Fetch required policies that are published and not archived
5656
policies = await db.policy.findMany({
5757
where: {
5858
organizationId: organizationId,
59+
isRequiredToSign: true,
60+
status: 'published',
61+
isArchived: false,
5962
},
6063
});
6164

apps/app/src/app/(app)/[orgId]/people/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default async function Layout({ children }: { children: React.ReactNode }
2323

2424
const employees = allMembers.filter((member) => {
2525
const roles = member.role.includes(',') ? member.role.split(',') : [member.role];
26-
return roles.includes('employee');
26+
return roles.includes('employee') || roles.includes('contractor');
2727
});
2828

2929
return (

0 commit comments

Comments
 (0)