52 lines
1.3 KiB
TypeScript
52 lines
1.3 KiB
TypeScript
import type {
|
|
PublicAuditLookupResult,
|
|
PublicAuditOffer,
|
|
PublicAuditRenderState,
|
|
} from "./public-audit-types";
|
|
|
|
const isSafeCtaHref = (href: string) => {
|
|
try {
|
|
const parsed = new URL(href);
|
|
return parsed.protocol === "https:" || parsed.protocol === "mailto:" || parsed.protocol === "tel:";
|
|
} catch {
|
|
return href.startsWith("/");
|
|
}
|
|
};
|
|
|
|
const sanitizeOffer = (offer: PublicAuditOffer): PublicAuditOffer => {
|
|
if (!offer.ctaHref || isSafeCtaHref(offer.ctaHref)) {
|
|
return offer;
|
|
}
|
|
|
|
return {
|
|
body: offer.body,
|
|
ctaLabel: offer.ctaLabel,
|
|
};
|
|
};
|
|
|
|
export const toPublicAuditRenderState = (
|
|
result: PublicAuditLookupResult,
|
|
): PublicAuditRenderState => {
|
|
if (!result || result.publicationStatus === "deactivated") {
|
|
return { kind: "unavailable" };
|
|
}
|
|
|
|
if (result.publicationStatus !== "published") {
|
|
return { kind: "pending" };
|
|
}
|
|
|
|
return {
|
|
kind: "published",
|
|
audit: {
|
|
companyName: result.companyName,
|
|
domain: result.domain,
|
|
publishedAt: result.publishedAt,
|
|
headline: result.publicContent.headline,
|
|
intro: result.publicContent.intro,
|
|
observations: result.publicContent.observations,
|
|
finalOffer: sanitizeOffer(result.publicContent.finalOffer),
|
|
screenshots: result.screenshots,
|
|
},
|
|
};
|
|
};
|