Resolved reply recipient userId from TenantUsers and enabled Beneficiary compliance exploration modal

This commit is contained in:
2026-10-26 10:02:14 +02:00
parent f56cb70b55
commit 155a379ab4
5 changed files with 355 additions and 50 deletions
@@ -1996,6 +1996,91 @@ export const getTenantUsers = async (tenantId: string | number): Promise<TenantU
};
// ==========================================
/**
* Resolves a recipient user GUID by email address across TenantUsers and Directory.
* Automatically caches any found user emails and IDs in local memory.
*/
export const resolveUserIdByEmail = async (
email: string,
hintTenantOrOrgId?: number | string
): Promise<string | undefined> => {
if (!email || typeof email !== 'string' || !email.includes('@')) {
return undefined;
}
const cleanEmail = email.trim().toLowerCase();
// 1. Fast cache check
const cachedId = getUserIdByEmail(cleanEmail);
if (cachedId && cachedId !== '00000000-0000-0000-0000-000000000000') {
return cachedId;
}
const matchInTenantUsers = (tenantUsers: TenantUser[]): string | undefined => {
if (!Array.isArray(tenantUsers)) return undefined;
let match: string | undefined = undefined;
for (const tu of tenantUsers) {
const uId = tu.userId || tu.user?.id;
const uEmail = tu.user?.email || (tu as any)?.email;
if (uId && uEmail) {
registerUserEmail(uId, uEmail);
if (String(uEmail).trim().toLowerCase() === cleanEmail) {
match = String(uId).trim();
}
}
}
return match;
};
// 2. Query tenant users with hintTenantOrOrgId
if (hintTenantOrOrgId) {
try {
const users = await getTenantUsers(hintTenantOrOrgId);
const match = matchInTenantUsers(users);
if (match) return match;
} catch {}
}
// 3. Query all tenants from getTenants and their users
try {
const tenants = await getTenants();
for (const t of tenants) {
if (hintTenantOrOrgId && String(t.id) === String(hintTenantOrOrgId)) continue;
try {
const users = await getTenantUsers(t.id);
const match = matchInTenantUsers(users);
if (match) return match;
} catch {}
}
} catch {}
// 4. Try standard fallback tenant IDs
for (const tid of [1, 2, 3]) {
if (hintTenantOrOrgId && String(tid) === String(hintTenantOrOrgId)) continue;
try {
const users = await getTenantUsers(tid);
const match = matchInTenantUsers(users);
if (match) return match;
} catch {}
}
// 5. Query user directory fallback
try {
const dirUsers = await apiClient.post<User[]>('/v1/api/users/1/200', {});
if (Array.isArray(dirUsers.data)) {
for (const u of dirUsers.data) {
if (u && u.id && u.email) {
registerUserEmail(u.id, u.email);
if (u.email.trim().toLowerCase() === cleanEmail) {
return String(u.id).trim();
}
}
}
}
} catch {}
return undefined;
};
// 10. Projects: Beneficiaries & Outcomes APIs
// ==========================================
export const getProjectBeneficiaries = async (projectId: string | number): Promise<Beneficiary[]> => {