export type FooterBadgeInput = { name?: string; embedHtml?: string | null; imageUrl?: string | null; linkUrl?: string | null; isActive?: boolean; sortOrder?: number; }; export type ParsedBadgeEmbed = { imageUrl: string | null; linkUrl: string | null; }; /** Extract first + nested/nearby from pasted badge HTML. */ export function parseBadgeEmbedHtml(html: string): ParsedBadgeEmbed { const trimmed = html.trim(); if (!trimmed) return { imageUrl: null, linkUrl: null }; const hrefMatch = trimmed.match(/]*\bhref\s*=\s*["']([^"']+)["'][^>]*>/i) ?? trimmed.match(/href\s*=\s*["']([^"']+)["']/i); const srcMatch = trimmed.match(/]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/i) ?? trimmed.match(/src\s*=\s*["']([^"']+)["']/i); return { linkUrl: hrefMatch?.[1]?.trim() || null, imageUrl: srcMatch?.[1]?.trim() || null, }; } export function normalizeBadgeFields(input: FooterBadgeInput) { const name = (input.name ?? "").trim(); const embedHtml = input.embedHtml?.trim() || null; let imageUrl = input.imageUrl?.trim() || null; let linkUrl = input.linkUrl?.trim() || null; if (embedHtml) { const parsed = parseBadgeEmbedHtml(embedHtml); if (!imageUrl && parsed.imageUrl) imageUrl = parsed.imageUrl; if (!linkUrl && parsed.linkUrl) linkUrl = parsed.linkUrl; } return { name, embedHtml, imageUrl, linkUrl, isActive: input.isActive ?? true, sortOrder: typeof input.sortOrder === "number" && Number.isFinite(input.sortOrder) ? Math.trunc(input.sortOrder) : 0, }; } export function validateBadgeFields(fields: ReturnType): string | null { if (!fields.name) return "Name is required"; if (!fields.embedHtml && !fields.imageUrl) { return "Provide embed HTML or an image URL"; } if (fields.imageUrl && !fields.linkUrl && !fields.embedHtml) { return "Link URL is required when using image URL only"; } return null; }