improvement: a flow added for owner users to make it possible for them to participate in treatments or tasks.

This commit is contained in:
2026-07-12 15:11:50 +03:30
parent 4208d15efb
commit 39935688ad
20 changed files with 1122 additions and 154 deletions

View 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);
}

View File

@@ -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(