improvement: a flow added for owner users to make it possible for them to participate in treatments or tasks.
This commit is contained in:
96
backend/src/common/membership-permissions.ts
Normal file
96
backend/src/common/membership-permissions.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
ownerPermissionsForOrgType,
|
||||
type OrganizationTypeName,
|
||||
} from './organization-type';
|
||||
import { normalizeTabPermissions } from './permissions';
|
||||
|
||||
export const CLINIC_PARTICIPATION_PERMISSIONS = [
|
||||
'TAB_TREATMENT_READ',
|
||||
'TAB_TREATMENT_EDIT',
|
||||
] as const;
|
||||
|
||||
export const LAB_PARTICIPATION_PERMISSIONS = [
|
||||
'TAB_TASKS_READ',
|
||||
'TAB_TASKS_EDIT',
|
||||
] as const;
|
||||
|
||||
const CLINIC_PARTICIPATION_SET = new Set<string>(CLINIC_PARTICIPATION_PERMISSIONS);
|
||||
const LAB_PARTICIPATION_SET = new Set<string>(LAB_PARTICIPATION_PERMISSIONS);
|
||||
|
||||
export type MembershipWithPermissions = {
|
||||
isOwner: boolean;
|
||||
organization: {
|
||||
plan?: { name: string; maxUsers?: number; price?: number } | null;
|
||||
planId?: string | null;
|
||||
type?: { name: string };
|
||||
};
|
||||
permissions?: Array<{ permission: { name: string } }>;
|
||||
};
|
||||
|
||||
export function getOrgTypeFromMembership(
|
||||
membership: MembershipWithPermissions,
|
||||
): OrganizationTypeName {
|
||||
return membership.organization.type?.name === 'LAB' ? 'LAB' : 'CLINIC';
|
||||
}
|
||||
|
||||
export function hasActivePlan(membership: MembershipWithPermissions): boolean {
|
||||
if (membership.organization.plan != null) {
|
||||
return true;
|
||||
}
|
||||
return Boolean(membership.organization.planId);
|
||||
}
|
||||
|
||||
export function getStoredPermissionNames(
|
||||
membership: MembershipWithPermissions,
|
||||
): string[] {
|
||||
return membership.permissions?.map((p) => p.permission.name) ?? [];
|
||||
}
|
||||
|
||||
export function getEffectivePermissionNames(
|
||||
membership: MembershipWithPermissions,
|
||||
): string[] {
|
||||
const stored = getStoredPermissionNames(membership);
|
||||
|
||||
if (!membership.isOwner) {
|
||||
return normalizeTabPermissions(stored);
|
||||
}
|
||||
|
||||
const orgType = getOrgTypeFromMembership(membership);
|
||||
const base = ownerPermissionsForOrgType(orgType, hasActivePlan(membership));
|
||||
return normalizeTabPermissions([...base, ...stored]);
|
||||
}
|
||||
|
||||
export function hasEffectivePermission(
|
||||
membership: MembershipWithPermissions,
|
||||
permission: string,
|
||||
): boolean {
|
||||
return getEffectivePermissionNames(membership).includes(permission);
|
||||
}
|
||||
|
||||
export function participationPermissionsForOrgType(
|
||||
orgType: OrganizationTypeName,
|
||||
): readonly string[] {
|
||||
return orgType === 'LAB'
|
||||
? LAB_PARTICIPATION_PERMISSIONS
|
||||
: CLINIC_PARTICIPATION_PERMISSIONS;
|
||||
}
|
||||
|
||||
export function participatesInTreatments(
|
||||
membership: MembershipWithPermissions,
|
||||
): boolean {
|
||||
if (getOrgTypeFromMembership(membership) !== 'CLINIC') {
|
||||
return false;
|
||||
}
|
||||
return getStoredPermissionNames(membership).includes('TAB_TREATMENT_EDIT');
|
||||
}
|
||||
|
||||
export function participatesInTasks(membership: MembershipWithPermissions): boolean {
|
||||
if (getOrgTypeFromMembership(membership) !== 'LAB') {
|
||||
return false;
|
||||
}
|
||||
return getStoredPermissionNames(membership).includes('TAB_TASKS_EDIT');
|
||||
}
|
||||
|
||||
export function isParticipationPermission(name: string): boolean {
|
||||
return CLINIC_PARTICIPATION_SET.has(name) || LAB_PARTICIPATION_SET.has(name);
|
||||
}
|
||||
@@ -49,18 +49,27 @@ export function filterPermissionsForOrgType(
|
||||
return normalizeTabPermissions(names.filter((n) => allowed.has(n)));
|
||||
}
|
||||
|
||||
/** Owner opt-in permissions — granted via MembershipPermission when owner chooses to participate. */
|
||||
const OWNER_OPT_IN_CLINIC = new Set<string>(['TAB_TREATMENT_READ', 'TAB_TREATMENT_EDIT']);
|
||||
const OWNER_OPT_IN_LAB = new Set<string>(['TAB_TASKS_READ', 'TAB_TASKS_EDIT']);
|
||||
|
||||
function ownerBasePermissions(orgType: OrganizationTypeName): readonly string[] {
|
||||
const all = orgType === 'LAB' ? LAB_TAB_PERMISSIONS : CLINIC_TAB_PERMISSIONS;
|
||||
const optIn = orgType === 'LAB' ? OWNER_OPT_IN_LAB : OWNER_OPT_IN_CLINIC;
|
||||
return all.filter((p) => !optIn.has(p));
|
||||
}
|
||||
|
||||
export function ownerPermissionsForOrgType(
|
||||
orgType: OrganizationTypeName,
|
||||
hasActivePlan: boolean,
|
||||
): string[] {
|
||||
const base = ownerBasePermissions(orgType);
|
||||
|
||||
if (hasActivePlan) {
|
||||
return orgType === 'LAB' ? [...LAB_TAB_PERMISSIONS] : [...CLINIC_TAB_PERMISSIONS];
|
||||
return [...base];
|
||||
}
|
||||
|
||||
const readOnly = (perms: readonly string[]) =>
|
||||
normalizeTabPermissions(perms.filter((p) => p.endsWith('_READ')));
|
||||
|
||||
return orgType === 'LAB' ? readOnly(LAB_TAB_PERMISSIONS) : readOnly(CLINIC_TAB_PERMISSIONS);
|
||||
return normalizeTabPermissions(base.filter((p) => p.endsWith('_READ')));
|
||||
}
|
||||
|
||||
export async function getOrganizationTypeName(
|
||||
|
||||
Reference in New Issue
Block a user