/** * Pure survey eligibility helpers (client-safe: type-only payload-types import). * The list/detail pages use them server-side to mirror the enforced gates in * formBuilderConfig.ts, so users never see surveys they cannot submit. */ import type { Form } from "@/payload-types"; export interface SurveyAudienceContext { userId: number; roleIds: number[]; qualificationNames: string[]; } export function isSurveyOpen( form: Pick, now: number, ): boolean { if (form.activeFrom && new Date(form.activeFrom).getTime() > now) return false; if (form.activeUntil && new Date(form.activeUntil).getTime() <= now) return false; return true; } function relationId(value: unknown): number | undefined { if (typeof value === "number") return value; if (typeof value === "object" && value !== null && "id" in value) { return (value as { id: number }).id; } return undefined; } function relationName(value: unknown): string | null { if (typeof value === "object" && value !== null && "name" in value) { return (value as { name: string }).name; } if (typeof value === "string") return value; return null; } export function surveyAudienceMatches( form: Pick, ctx: SurveyAudienceContext, ): boolean { const targeting = form.targeting; const audience = targeting?.audience ?? "all"; if (audience === "all") return true; if (audience === "users") { const ids = (targeting?.users ?? []).map(relationId).filter((v): v is number => v !== undefined); return ids.includes(ctx.userId); } if (audience === "roles") { const ids = (targeting?.roles ?? []).map(relationId).filter((v): v is number => v !== undefined); return ids.some((id) => ctx.roleIds.includes(id)); } const names = (targeting?.qualifications ?? []) .map(relationName) .filter((n): n is string => n !== null) .map((n) => n.toLowerCase()); if (names.length === 0) return false; return ctx.qualificationNames.some((qn) => names.includes(qn.toLowerCase())); }