import axios, { AxiosError } from 'axios'; import { apiClient } from './httpClient'; import { toolsService } from './toolsService'; import { getCurrentUserSub } from '../auth/AuthProvider'; import { Dashboard, Organisation, CreateOrganisationRequest, UpdateOrganisationRequest, Portfolio, CreatePortfolioRequest, UpdatePortfolioRequest, Programme, CreateProgrammeRequest, UpdateProgrammeRequest, Instrument, Project, CreateProjectRequest, UpdateProjectRequest, UpdateProjectStatusRequest, Activity, CreateActivityRequest, UpdateActivityRequest, Milestone, CreateMilestoneRequest, UpdateMilestoneRequest, Award, CreateAwardRequest, Contract, CreateContractRequest, UpdateContractRequest, ExecuteContractRequest, Budget, CreateBudgetRequest, Disbursement, CreateDisbursementRequest, UpdateDisbursementRequest, Indicator, CreateIndicatorRequest, Evidence, UpdateEvidenceRequest, Risk, CreateRiskRequest, UpdateRiskRequest, User, UserFilterRequest, Invite, InviteRequest, InviteUserRequest, AuditLog, ProblemDetails, ActivityStatus, } from '../types'; import { parseFriendlyError, FriendlyError } from '../utils/apiErrorParser'; import { registerUserEmail, registerUserEmails } from '../lib/formatters'; // Helper to extract ProblemDetails from Axios error or unknown error export function parseApiError(error: unknown): ProblemDetails { if (axios.isAxiosError(error)) { const axiosErr = error as AxiosError; if (axiosErr.response?.data && typeof axiosErr.response.data === 'object') { return axiosErr.response.data; } } const friendly = parseFriendlyError(error); return { title: friendly.title, detail: friendly.message, status: friendly.statusCode || 500, }; } export { parseFriendlyError }; export type { FriendlyError }; // ========================================== // 1. Executive Dashboard APIs // ========================================== export const getDashboard = async (): Promise => { const res = await apiClient.get('/v1/api/governance/dashboard'); return res.data; }; // ========================================== // 2. Governance & Structure APIs // ========================================== /** * Normalizes an Organisation object from backend responses. * Robustly maps status across variations (status, Status, activityStatus, approvalStatus, statusId, isActive). */ export const normalizeOrganisation = (raw: any): Organisation => { if (!raw) return raw; const status = raw.status ?? raw.Status ?? raw.activityStatus ?? raw.ActivityStatus ?? raw.approvalStatus ?? raw.ApprovalStatus ?? raw.statusId ?? raw.StatusId ?? raw.state ?? raw.State ?? (raw.isActive !== undefined ? (raw.isActive ? 1 : 2) : undefined) ?? (raw.IsActive !== undefined ? (raw.IsActive ? 1 : 2) : undefined) ?? ActivityStatus.Active; return { ...raw, id: raw.id ?? raw.Id ?? raw.organisationId ?? raw.OrganisationId, name: raw.name ?? raw.Name ?? null, registrationNo: raw.registrationNo ?? raw.RegistrationNo ?? raw.regNo ?? raw.RegNo ?? null, email: raw.email ?? raw.Email ?? null, type: raw.type ?? raw.Type ?? raw.organisationType ?? raw.OrganisationType ?? 5, status, createdAt: raw.createdAt ?? raw.CreatedAt ?? new Date().toISOString(), createdBy: raw.createdBy ?? raw.CreatedBy ?? '', }; }; export const getOrganisations = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/governance/organisations', { params: { page, pageSize }, }); const list = res.data || []; return list.map(normalizeOrganisation); }; export const getOrganisation = async (organisationId: string | number): Promise => { const hashedOrgId = await toolsService.resolveOrganisationId(organisationId); const res = await apiClient.get(`/v1/api/governance/organisations/${hashedOrgId}`); return normalizeOrganisation(res.data); }; export const createOrganisation = async (payload: CreateOrganisationRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/governance/organisations', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const updateOrganisation = async (payload: UpdateOrganisationRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.put('/v1/api/governance/organisations', { ...payload, updatedBy: userSub || undefined, }); return res.data; }; export const getPortfolios = async (organisationId: string | number): Promise => { const hashedOrgId = await toolsService.resolveOrganisationId(organisationId); const res = await apiClient.get(`/v1/api/governance/organisations/${hashedOrgId}/portfolio`); return res.data || []; }; export const getPortfolio = async (portfolioId: string | number): Promise => { const hashedPortId = await toolsService.resolvePortfolioId(portfolioId); const res = await apiClient.get(`/v1/api/governance/portfolios/${hashedPortId}`); return Array.isArray(res.data) ? res.data[0] : res.data; }; export const getAllPortfolios = async (): Promise => { try { const orgs = await getOrganisations(1, 10); if (orgs.length > 0) { const results = await Promise.allSettled( orgs.map((org) => getPortfolios(org.id)) ); const combined = results .filter((r): r is PromiseFulfilledResult => r.status === 'fulfilled') .flatMap((r) => r.value); if (combined.length > 0) return combined; } const res = await apiClient.get('/v1/api/governance/organisations/portfolio'); return res.data || []; } catch { return []; } }; export const getPortfolioProgrammes = async (portfolioId: string | number): Promise => { const hashedPortId = await toolsService.resolvePortfolioId(portfolioId); const res = await apiClient.get(`/v1/api/governance/portfolios/${hashedPortId}/programmes`); return res.data || []; }; export const createPortfolio = async (payload: CreatePortfolioRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/governance/organisations/portfolio', { ...payload, ownerId: payload.ownerId || userSub || undefined, createdBy: userSub || undefined, }); return res.data; }; export const updatePortfolio = async (payload: UpdatePortfolioRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.put('/v1/api/governance/organisations/portfolio', { ...payload, ownerId: payload.ownerId || userSub || undefined, updatedBy: userSub || undefined, }); return res.data; }; export const getProgrammes = async (portfolioId: string | number): Promise => { const hashedPortId = await toolsService.resolvePortfolioId(portfolioId); const res = await apiClient.get(`/v1/api/governance/programmes/${hashedPortId}`); return res.data || []; }; export const getInstruments = async (programmeId: string | number): Promise => { const hashedProgId = await toolsService.resolveProgrammeId(programmeId); const res = await apiClient.get(`/v1/api/governance/instruments/${hashedProgId}`); return res.data || []; }; // ========================================== // 3. Project & Milestone Management APIs // ========================================== export const getAllProgrammes = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get(`/v1/api/projects/programme/${page}/${pageSize}`); return res.data || []; }; export const getProgramme = async (programmeId: string | number): Promise => { const hashedProgId = await toolsService.resolveProgrammeId(programmeId); const res = await apiClient.get(`/v1/api/projects/programme/${hashedProgId}`); return res.data; }; export const getProgrammeProjects = async (programmeId: string | number): Promise => { const hashedProgId = await toolsService.resolveProgrammeId(programmeId); const res = await apiClient.get(`/v1/api/projects/${hashedProgId}/programme`); return res.data || []; }; export const createProgramme = async (payload: CreateProgrammeRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/projects/programme', { ...payload, ownerId: payload.ownerId || userSub || undefined, createdBy: userSub || undefined, }); return res.data; }; export const updateProgramme = async (payload: UpdateProgrammeRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.put('/v1/api/projects/programme', { ...payload, ownerId: payload.ownerId || userSub || undefined, updatedBy: userSub || undefined, }); return res.data; }; export const getProjects = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/projects', { params: { page, pageSize }, }); return res.data || []; }; export const getProject = async (projectId: string | number): Promise => { const hashedProjectId = await toolsService.resolveProjectId(projectId); const res = await apiClient.get(`/v1/api/projects/${hashedProjectId}`); return Array.isArray(res.data) ? res.data[0] : res.data; }; export const createProject = async (payload: CreateProjectRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/projects', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const updateProject = async (payload: UpdateProjectRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.put('/v1/api/projects', { ...payload, updatedBy: userSub || undefined, }); return res.data; }; export const updateProjectStatus = async (payload: UpdateProjectStatusRequest): Promise => { const userSub = getCurrentUserSub(); await apiClient.put('/v1/api/projects/status', { ...payload, updatedBy: userSub || undefined, }); }; export const getActivities = async (projectId: string | number): Promise => { const hashedProjectId = await toolsService.resolveProjectId(projectId); const res = await apiClient.get(`/v1/api/projects/${hashedProjectId}/activities`); return res.data || []; }; export const getActivity = async (activityId: string | number): Promise => { const hashedActivityId = await toolsService.encodeId(activityId); const res = await apiClient.get(`/v1/api/projects/activities/${hashedActivityId}`); return res.data; }; export const createActivity = async (payload: CreateActivityRequest): Promise => { const userSub = getCurrentUserSub(); const body: any = { ...payload, milestoneId: payload.milestoneId !== undefined && payload.milestoneId !== null && payload.milestoneId !== '' ? Number(payload.milestoneId) : null, createdBy: userSub || undefined, }; try { const res = await apiClient.post('/v1/api/projects/activities', body); return res.data; } catch (err: any) { if (err?.response?.status === 405) { const res = await apiClient.put('/v1/api/projects/activities', body); return res.data; } throw err; } }; export const updateActivity = async (payload: UpdateActivityRequest): Promise => { const userSub = getCurrentUserSub(); const body: any = { ...payload, milestoneId: payload.milestoneId !== undefined && payload.milestoneId !== null && payload.milestoneId !== '' ? Number(payload.milestoneId) : null, updatedBy: userSub || undefined, }; try { const res = await apiClient.put('/v1/api/projects/activities', body); return res.data; } catch (err: any) { if (err?.response?.status === 404 || err?.response?.status === 405) { const res = await apiClient.put('/v1/api/projects/activity', body); return res.data; } throw err; } }; export const getMilestones = async (projectId: string | number): Promise => { const hashedProjectId = await toolsService.resolveProjectId(projectId); const res = await apiClient.get(`/v1/api/projects/${hashedProjectId}/milestones`); return res.data || []; }; export const getAllMilestones = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get(`/v1/api/projects/milestones/${page}/${pageSize}`); return res.data || []; }; export const getMilestone = async (milestoneId: string | number): Promise => { const hashedMilestoneId = await toolsService.encodeId(milestoneId); const res = await apiClient.get(`/v1/api/projects/milestones/${hashedMilestoneId}`); return res.data; }; export const createMilestone = async (payload: CreateMilestoneRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/evidence/milestones', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const updateMilestone = async (payload: UpdateMilestoneRequest): Promise => { const userSub = getCurrentUserSub(); await apiClient.put('/v1/api/projects/milestone', { ...payload, updatedBy: userSub || undefined, }); }; export const getProjectIndicators = async (projectId: string | number): Promise => { const hashedProjectId = await toolsService.resolveProjectId(projectId); const res = await apiClient.get(`/v1/api/projects/indicators/project/${hashedProjectId}`); return res.data || []; }; export const getProjectRisks = async (projectId: string | number): Promise => { const hashedProjectId = await toolsService.resolveProjectId(projectId); const res = await apiClient.get(`/v1/api/projects/${hashedProjectId}/risks`); const list = res.data || []; return list.map(normalizeRisk); }; // ========================================== // 4. Grant & Financial Management APIs // ========================================== export const getAwards = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/grants/awards', { params: { page, pageSize }, }); return res.data || []; }; export const getAward = async (awardId: string | number): Promise => { const hashedAwardId = await toolsService.resolveAwardId(awardId); const res = await apiClient.get(`/v1/api/grants/awards/${hashedAwardId}`); return res.data; }; export const createAward = async (payload: CreateAwardRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/grants/awards', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const getContracts = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/grants/contracts', { params: { page, pageSize }, }); return res.data || []; }; export const getContract = async (contractId: string | number): Promise => { const hashedContractId = await toolsService.resolveContractId(contractId); const res = await apiClient.get(`/v1/api/grants/contracts/${hashedContractId}`); return res.data; }; export const createContract = async (payload: CreateContractRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/grants/contracts', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const updateContract = async (payload: UpdateContractRequest): Promise => { await apiClient.put('/v1/api/grants/contracts', payload); }; export const uploadContractFile = async ( contractId: string | number, file: File | FormData ): Promise => { const hashedContractId = await toolsService.resolveContractId(contractId); const formData = file instanceof FormData ? file : new FormData(); if (file instanceof File) { formData.append('file', file); } await apiClient.post(`/v1/api/grants/contracts/${hashedContractId}`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); }; export const getContractSecureLink = async (contractId: string | number): Promise => { const hashedContractId = await toolsService.resolveContractId(contractId); const res = await apiClient.get(`/v1/api/grants/contracts/${hashedContractId}/securelink`); return res.data; }; export const executeContract = async (payload: ExecuteContractRequest): Promise => { const userSub = getCurrentUserSub(); await apiClient.post('/v1/api/grants/contracts/execute', { ...payload, executedBy: userSub || undefined, }); }; export const getBudgets = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/grants/budgets', { params: { page, pageSize }, }); return res.data || []; }; export const getBudget = async (budgetId: string | number): Promise => { const hashedBudgetId = await toolsService.encodeId(budgetId); const res = await apiClient.get(`/v1/api/grants/budgets/${hashedBudgetId}`); return res.data; }; export const createBudget = async (payload: CreateBudgetRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/grants/budgets', { ...payload, createdBy: userSub || undefined, }); return res.data; }; // ========================================== // 5. Execution (Disbursements & Risks) APIs // ========================================== export const getDisbursements = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/execution/disbursements', { params: { page, pageSize }, }); return res.data || []; }; export const getDisbursement = async (disbursementId: string | number): Promise => { const hashedDisbId = await toolsService.encodeId(disbursementId); const res = await apiClient.get(`/v1/api/execution/disbursements/${hashedDisbId}`); return res.data; }; export const createDisbursement = async (payload: CreateDisbursementRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/execution/disbursements', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const updateDisbursement = async (payload: UpdateDisbursementRequest): Promise => { const userSub = getCurrentUserSub(); await apiClient.put('/v1/api/execution/disbursements', { ...payload, updatedBy: userSub || undefined, }); }; export const approveDisbursement = async (disbursementId: string | number): Promise => { const hashedDisbId = await toolsService.encodeId(disbursementId); const userSub = getCurrentUserSub(); await apiClient.post(`/v1/api/execution/disbursements/${hashedDisbId}/approve`, { approvedBy: userSub || undefined, }); }; /** * Normalizes a Risk record from backend responses. * Robustly parses Likelihood (1-5) and Impact (1-5), supporting strings, numbers, and case variations. */ export const normalizeRisk = (raw: any): Risk => { if (!raw) return raw; const rawLik = raw.likelihood ?? raw.Likelihood ?? raw.riskLikelihood ?? raw.RiskLikelihood; const rawImp = raw.impact ?? raw.Impact ?? raw.riskImpact ?? raw.RiskImpact; const parseScore = (val: any, defaultVal: number): number => { if (typeof val === 'number' && !isNaN(val)) { return Math.min(5, Math.max(1, Math.round(val))); } if (typeof val === 'string') { const parsed = parseInt(val, 10); if (!isNaN(parsed) && parsed >= 1 && parsed <= 5) { return parsed; } const lower = val.toLowerCase().trim(); if (lower.includes('rare') || lower.includes('insignificant') || lower.includes('trivial') || lower === '1') return 1; if (lower.includes('unlikely') || lower.includes('minor') || lower === '2') return 2; if (lower.includes('possible') || lower.includes('moderate') || lower.includes('medium') || lower === '3') return 3; if (lower.includes('likely') || lower.includes('major') || lower.includes('high') || lower === '4') return 4; if (lower.includes('almost') || lower.includes('certain') || lower.includes('severe') || lower.includes('critical') || lower.includes('catastrophic') || lower === '5') return 5; } return defaultVal; }; const likelihood = parseScore(rawLik, 3); const impact = parseScore(rawImp, 2); return { ...raw, id: raw.id ?? raw.Id ?? raw.riskId ?? raw.RiskId, projectId: raw.projectId ?? raw.ProjectId, name: raw.name ?? raw.Name ?? null, description: raw.description ?? raw.Description ?? null, likelihood, impact, createdBy: raw.createdBy ?? raw.CreatedBy ?? '', createdAt: raw.createdAt ?? raw.CreatedAt ?? new Date().toISOString(), owner: raw.owner ?? raw.Owner ?? raw.riskOwner ?? raw.RiskOwner ?? raw.createdBy ?? raw.CreatedBy ?? '', mitigationAction: raw.mitigationAction ?? raw.MitigationAction ?? raw.mitigation ?? raw.Mitigation ?? '', status: raw.status ?? raw.Status, }; }; export const getAllRisks = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get('/v1/api/execution/risks', { params: { page, pageSize }, }); const list = res.data || []; return list.map(normalizeRisk); }; export const getRisk = async (riskId: string | number): Promise => { const hashedRiskId = await toolsService.encodeId(riskId); const res = await apiClient.get(`/v1/api/execution/risks/${hashedRiskId}`); return normalizeRisk(res.data); }; export const createRisk = async (payload: CreateRiskRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/execution/risks', { ...payload, likelihood: Number(payload.likelihood), impact: Number(payload.impact), createdBy: userSub || undefined, }); return normalizeRisk(res.data); }; export const updateRisk = async (payload: UpdateRiskRequest): Promise => { const userSub = getCurrentUserSub(); await apiClient.put('/v1/api/execution/risks', { ...payload, likelihood: Number(payload.likelihood), impact: Number(payload.impact), updatedBy: userSub || undefined, }); }; // ========================================== // 6. Evidence & M&E APIs // ========================================== export const getAllIndicators = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get(`/v1/api/evidence/indicators/${page}/${pageSize}`); return res.data || []; }; export const getIndicator = async (indicatorId: string | number): Promise => { const hashedIndId = await toolsService.encodeId(indicatorId); const res = await apiClient.get(`/v1/api/evidence/indicators/${hashedIndId}`); return res.data; }; export const createIndicator = async (payload: CreateIndicatorRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/evidence/indicators', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const recordIndicatorActual = async (indicatorId: string | number, actualAmount: number): Promise => { const hashedIndId = await toolsService.encodeId(indicatorId); const userSub = getCurrentUserSub(); await apiClient.post(`/v1/api/evidence/indicators/${hashedIndId}/actuals/${actualAmount}`, { recordedBy: userSub || undefined, }); }; export const getEvidenceFiles = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get(`/v1/api/evidence/files/${page}/${pageSize}`); return res.data || []; }; export const getAllEvidence = async (page = 1, pageSize = 100): Promise => { return getEvidenceFiles(page, pageSize); }; export const getEvidence = async (evidenceId: string | number): Promise => { const hashedEvId = await toolsService.encodeId(evidenceId); const res = await apiClient.get(`/v1/api/evidence/${hashedEvId}`); return res.data; }; export const getEvidenceSecureLink = async (evidenceId: string | number): Promise => { const hashedEvId = await toolsService.encodeId(evidenceId); const res = await apiClient.get(`/v1/api/evidence/${hashedEvId}/securelink`); return res.data; }; export const getMilestoneEvidence = async (milestoneId: string | number): Promise => { const hashedMId = await toolsService.encodeId(milestoneId); const res = await apiClient.get(`/v1/api/evidence/milestone/${hashedMId}`); return res.data || []; }; export const getProjectEvidence = async (projectId: string | number): Promise => { const hashedProjectId = await toolsService.resolveProjectId(projectId); const res = await apiClient.get(`/v1/api/evidence/${hashedProjectId}`); return res.data || []; }; export const uploadEvidenceFile = async (formData: FormData): Promise => { const userSub = getCurrentUserSub(); if (userSub && !formData.get('userId')) { formData.append('userId', userSub); } const res = await apiClient.put('/v1/api/evidence/file', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); return res.data; }; export const updateEvidence = async (payload: UpdateEvidenceRequest): Promise => { const userSub = getCurrentUserSub(); await apiClient.put('/v1/api/evidence', { ...payload, updatedBy: userSub || undefined, }); }; // ========================================== // 7. Users & Background Integration APIs // ========================================== export const getUsers = async ( page = 1, pageSize = 100, filter: UserFilterRequest = {} ): Promise => { const res = await apiClient.post(`/v1/api/users/${page}/${pageSize}`, filter); const list = res.data || []; registerUserEmails(list); return list; }; export const getUser = async (userId: string): Promise => { const res = await apiClient.get(`/v1/api/users/${userId}`); if (res.data?.id && res.data?.email) { registerUserEmail(res.data.id, res.data.email); } return res.data; }; export const getCurrentUser = async (): Promise => { const userSub = getCurrentUserSub(); if (userSub) { try { const u = await getUser(userSub); if (u?.id && u?.email) { registerUserEmail(u.id, u.email); } return u; } catch { // Fallback } } return { id: userSub || 'user-1', email: null, status: ActivityStatus.Active, lastLoginAt: new Date().toISOString(), }; }; export const updateUserStatus = async (userId: string, status: ActivityStatus | number): Promise => { await apiClient.get(`/v1/api/users/${userId}/status/${status}`); }; export { syncUserAccount, ensureUserSynced, isUserSyncedInBrowser, saveUserSyncInBrowser, clearUserSyncInBrowser, getUserSyncRecord, isActivationPending, setActivationPending, getActivationPendingReason, } from './userSyncService'; /** * Synchronizes the authenticated user with the backend to enable the user in the system. * By default, sync is saved in the browser to prevent hotspotting the sync endpoint. */ export const syncUser = async ( userData?: { id?: string; sub?: string; email?: string | null; name?: string | null; role?: string | null; tenant?: string | null; }, force = false ): Promise => { const { ensureUserSynced } = await import('./userSyncService'); return ensureUserSynced(userData?.sub || userData?.id, undefined, userData, force); }; export const syncIntegration = async (force = true): Promise => { const { ensureUserSynced } = await import('./userSyncService'); await ensureUserSynced(undefined, undefined, undefined, force); }; // ========================================== // 8. Communications & Invites APIs // ========================================== export const sendInvite = async (payload: InviteRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/communications/invite', { ...payload, createdBy: userSub || undefined, }); return res.data; }; export const getInvites = async (page = 1, pageSize = 100): Promise => { const res = await apiClient.get(`/v1/api/communications/invites/${page}/${pageSize}`); return res.data || []; }; export const getApplicantInvite = async (applicantUserId: string): Promise => { const res = await apiClient.get(`/v1/api/communications/invites/applicant/${applicantUserId}`); return res.data; }; export const getApplicantInvites = async (applicantUserId: string): Promise => { const res = await apiClient.get(`/v1/api/communications/invites/${applicantUserId}`); return res.data || []; }; // Legacy compatibility helpers export const inviteUser = async (payload: InviteUserRequest): Promise => { const userSub = getCurrentUserSub(); const res = await apiClient.post('/v1/api/governance/users/invite', { ...payload, invitedBy: userSub || undefined, }); return res.data; }; export const revokeUser = async (userId: string): Promise => { await apiClient.delete(`/v1/api/governance/users/${userId}`); }; export const getAuditLogs = async (): Promise => { try { const res = await apiClient.get('/v1/api/governance/audit'); return res.data || []; } catch { return []; } };