Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

135 lines
4.3 KiB
TypeScript

function requireNonEmpty(value: string, field: string): string {
if (!value) throw new Error(`${field} is required`);
return value;
}
function withOptionalTimestamp(field: string, value?: string): Record<string, unknown> {
return value ? { [field]: value } : {};
}
export type SurveyType = 'csat' | 'nps' | 'custom';
export type NotificationChannel = 'email' | 'sms' | 'in_app' | 'push';
export function buildSurveySentPayload(params: {
surveyId: string;
surveyType: SurveyType;
recipientId: string;
ticketId?: string;
sentAt?: string;
channel: NotificationChannel;
templateId?: string;
}): Record<string, unknown> {
requireNonEmpty(params.surveyId, 'surveyId');
requireNonEmpty(params.surveyType, 'surveyType');
requireNonEmpty(params.recipientId, 'recipientId');
requireNonEmpty(params.channel, 'channel');
return {
surveyId: params.surveyId,
surveyType: params.surveyType,
recipientId: params.recipientId,
...(params.ticketId ? { ticketId: params.ticketId } : {}),
...withOptionalTimestamp('sentAt', params.sentAt),
channel: params.channel,
...(params.templateId ? { templateId: params.templateId } : {}),
};
}
export function buildSurveyResponseReceivedPayload(params: {
surveyId: string;
responseId: string;
recipientId: string;
ticketId?: string;
respondedAt?: string;
score: number;
comment?: string;
}): Record<string, unknown> {
requireNonEmpty(params.surveyId, 'surveyId');
requireNonEmpty(params.responseId, 'responseId');
requireNonEmpty(params.recipientId, 'recipientId');
if (typeof params.score !== 'number' || Number.isNaN(params.score)) throw new Error('score is required');
return {
surveyId: params.surveyId,
responseId: params.responseId,
recipientId: params.recipientId,
...(params.ticketId ? { ticketId: params.ticketId } : {}),
...withOptionalTimestamp('respondedAt', params.respondedAt),
score: params.score,
...(params.comment ? { comment: params.comment } : {}),
};
}
export function buildSurveyReminderSentPayload(params: {
surveyId: string;
recipientId: string;
ticketId?: string;
sentAt?: string;
channel: NotificationChannel;
reminderNumber: number;
}): Record<string, unknown> {
requireNonEmpty(params.surveyId, 'surveyId');
requireNonEmpty(params.recipientId, 'recipientId');
requireNonEmpty(params.channel, 'channel');
if (!Number.isInteger(params.reminderNumber) || params.reminderNumber <= 0) {
throw new Error('reminderNumber must be a positive integer');
}
return {
surveyId: params.surveyId,
recipientId: params.recipientId,
...(params.ticketId ? { ticketId: params.ticketId } : {}),
...withOptionalTimestamp('sentAt', params.sentAt),
channel: params.channel,
reminderNumber: params.reminderNumber,
};
}
export function buildSurveyExpiredPayload(params: {
surveyId: string;
recipientId: string;
ticketId?: string;
expiredAt?: string;
}): Record<string, unknown> {
requireNonEmpty(params.surveyId, 'surveyId');
requireNonEmpty(params.recipientId, 'recipientId');
return {
surveyId: params.surveyId,
recipientId: params.recipientId,
...(params.ticketId ? { ticketId: params.ticketId } : {}),
...withOptionalTimestamp('expiredAt', params.expiredAt),
};
}
export type CsatWindow = 'daily' | 'weekly' | 'monthly';
export type CsatScopeType = 'agent' | 'team' | 'org';
export function buildCsatAlertTriggeredPayload(params: {
window: CsatWindow;
score: number;
threshold: number;
triggeredAt?: string;
scopeType: CsatScopeType;
scopeId?: string;
baseline?: number;
delta?: number;
}): Record<string, unknown> {
requireNonEmpty(params.window, 'window');
if (typeof params.score !== 'number' || Number.isNaN(params.score)) throw new Error('score is required');
if (typeof params.threshold !== 'number' || Number.isNaN(params.threshold)) throw new Error('threshold is required');
requireNonEmpty(params.scopeType, 'scopeType');
return {
window: params.window,
score: params.score,
...(typeof params.baseline === 'number' ? { baseline: params.baseline } : {}),
...(typeof params.delta === 'number' ? { delta: params.delta } : {}),
threshold: params.threshold,
...withOptionalTimestamp('triggeredAt', params.triggeredAt),
scopeType: params.scopeType,
...(params.scopeId ? { scopeId: params.scopeId } : {}),
};
}