import type { DomainSortMode, KeywordRow, PageRow, SortOrder, } from "@/client/features/domain/types"; export function toSortMode(value: string | null): DomainSortMode | undefined { if ( value === "rank" || value === "traffic" || value === "volume" || value === "score" || value === "cpc" ) { return value; } return undefined; } export function toSortOrder(value: string | null): SortOrder | undefined { if (value === "asc" || value === "desc") return value; return undefined; } export function getDefaultSortOrder(sortMode: DomainSortMode): SortOrder { return sortMode === "rank" ? "asc" : "desc"; } export function resolveSortOrder( sortMode: DomainSortMode, sortOrder: SortOrder | undefined, ): SortOrder { return sortOrder ?? getDefaultSortOrder(sortMode); } export function toSortSearchParam( sortMode: DomainSortMode, ): DomainSortMode | undefined { return sortMode === "rank" ? undefined : sortMode; } export function toSortOrderSearchParam( sortMode: DomainSortMode, sortOrder: SortOrder, ): SortOrder | undefined { return sortOrder === getDefaultSortOrder(sortMode) ? undefined : sortOrder; } export function sortableNullableNumber( value: number | null | undefined, order: SortOrder, ): number { if (value != null) return value; return order === "asc" ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; } export function toPageSortMode(sortMode: DomainSortMode): "traffic" | "volume" { if (sortMode === "volume") return "volume"; return "traffic"; } export function normalizeDomainTarget(input: string): string | null { const value = input.trim(); if (!value) return null; const withProtocol = /^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(value) ? value : `https://${value}`; try { const parsed = new URL(withProtocol); const hostname = parsed.hostname.toLowerCase(); if (!hostname || !hostname.includes(".")) return null; if (!/^[a-z\d.-]+$/.test(hostname)) return null; const path = parsed.pathname === "/" ? "" : parsed.pathname; return `${hostname}${path}`; } catch { return null; } } export function formatNumber(value: number | null | undefined) { if (value == null) return "-"; return new Intl.NumberFormat().format(value); } export function formatFloat(value: number | null | undefined) { if (value == null) return "-"; if (value > 100) return new Intl.NumberFormat().format(Math.round(value)); return value.toFixed(2); } export function formatMetric( value: number | null | undefined, hasData: boolean | undefined, ) { if (!hasData) return "Not enough data"; return formatNumber(value); } type ExportTable = { headers: string[]; rows: (string | number | null)[][] }; export function keywordsToTable(rows: KeywordRow[]): ExportTable { return { headers: ["Keyword", "Rank", "Volume", "Traffic", "CPC", "URL", "Score"], rows: rows.map((row) => [ row.keyword, row.position, row.searchVolume, row.traffic, row.cpc, row.url ?? row.relativeUrl, row.keywordDifficulty, ]), }; } export function pagesToTable(rows: PageRow[]): ExportTable { return { headers: ["Page", "Organic Traffic", "Keywords"], rows: rows.map((row) => [row.page, row.organicTraffic, row.keywords]), }; } export function resolveDomainPageHref( value: string | null | undefined, domain: string, ): string | null { if (!value) return null; return value.includes("://") ? value : `https://${domain}${value.startsWith("/") ? value : `/${value}`}`; }