import { t, locale } from '@/i18n' import { formatAbsoluteDateFromIso } from '@/lib/relativeDate' import type { ResolvedSeo } from '@/lib/seoTypes' const MONTH_NAMES = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ] const EMPTY_VALUE = '–' export interface ApiPreviewRow { key: string value: string chip: string | null } function pad(value: number) { return value.toString().padStart(2, '0') } export function formatApiDate(iso: string | null) { if (!iso) { return EMPTY_VALUE } const date = new Date(iso) const clock = `${pad(date.getHours())}:${pad(date.getMinutes())}` return locale.value === 'fr' ? `${formatAbsoluteDateFromIso(iso)}, ${clock}` : `${date.getDate()} ${MONTH_NAMES[date.getMonth()]} ${date.getFullYear()}, ${clock}` } const FALLBACK_CHIPS: Record string>> = { title: { title: () => t('article title + template') }, description: { body: () => t('first sentence of the body'), excerpt: () => t('excerpt'), site: () => t('site default description'), }, image: { cover: () => t('cover image'), site: () => t('site social image'), none: () => t('No image'), }, canonical: { site: () => t('site address + slug') }, } function fallbackChip(field: string, source: string) { return FALLBACK_CHIPS[field]?.[source]?.() ?? null } function authorLine(author: ResolvedSeo['authors'][number]) { const links = author.same_as.length return [ author.name, author.url ? author.url.replace(/^https?:\/\//, '') : '', links === 0 ? '' : links === 1 ? t('1 social link') : t('{0} social links', [links]), ] .filter(Boolean) .join(' · ') } function jsonLdSummary(jsonLd: Record) { const fields = Object.keys(jsonLd).filter((key) => !key.startsWith('@')) return [String(jsonLd['@type'] ?? ''), fields.join(', ')].filter(Boolean).join(' · ') } export function apiPreviewRows(resolved: ResolvedSeo): ApiPreviewRow[] { const { fallbacks } = resolved return [ { key: 'title', value: resolved.title, chip: fallbackChip('title', fallbacks.title) }, { key: 'description', value: resolved.description, chip: fallbackChip('description', fallbacks.description), }, { key: 'canonical_url', value: resolved.canonical_url, chip: fallbackChip('canonical', fallbacks.canonical), }, { key: 'robots', value: resolved.robots, chip: null }, { key: 'og.type', value: resolved.og.type, chip: null }, { key: 'og.image', value: resolved.og.image, chip: fallbackChip('image', fallbacks.image) }, { key: 'og.image_alt', value: resolved.og.image_alt, chip: null }, { key: 'twitter.card', value: resolved.twitter.card, chip: null }, { key: 'authors', value: resolved.authors.map(authorLine).join('; ') || EMPTY_VALUE, chip: null, }, { key: 'published_at', value: formatApiDate(resolved.published_at), chip: null }, { key: 'modified_at', value: formatApiDate(resolved.modified_at), chip: null }, { key: 'json_ld', value: jsonLdSummary(resolved.json_ld), chip: null }, ] }