/** * Frontend-only template specs. * Variants: * - news * - breaking (news urgent) * - finance (news finance) * - market * - friends * - events */ export const TEMPLATE_SPECS = { news: { mini_spec: { size: { w: 240, h: 96 }, background: { type: "gradient", value: "newsBlue" }, radius: 18, layers: [ { id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf1ea", style: "text.icon", bind: "data.categoryIcon" }, { id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 }, { id: "badge", type: "chip", x: 94, y: 10, text: "NEWS", style: "chip.news" }, { id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 }, { id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }, ], }, full_spec: { size: { w: 360, h: 260 }, background: { type: "solid", value: "surface" }, radius: 22, layers: [ { id: "badge", type: "chip", x: 16, y: 12, text: "NEWS", style: "chip.news" }, { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 }, ], }, }, breaking: { mini_spec: { size: { w: 240, h: 96 }, background: { type: "gradient", value: "breakingRed" }, radius: 18, layers: [ { id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf0e7", style: "text.icon", bind: "data.categoryIcon" }, { id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 }, { id: "badge", type: "chip", x: 94, y: 10, text: "BREAKING", style: "chip.danger" }, { id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 }, { id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }, ], }, full_spec: { size: { w: 360, h: 260 }, background: { type: "gradient", value: "breakingRed" }, radius: 22, layers: [ { id: "badge", type: "chip", x: 16, y: 12, text: "BREAKING", style: "chip.danger" }, { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 }, ], }, }, finance: { mini_spec: { size: { w: 240, h: 96 }, background: { type: "gradient", value: "financeTeal" }, radius: 18, layers: [ { id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf201", style: "text.icon", bind: "data.categoryIcon" }, { id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 }, { id: "badge", type: "chip", x: 94, y: 10, text: "FINANCE", style: "chip.news" }, { id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 }, { id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }, ], }, full_spec: { size: { w: 360, h: 260 }, background: { type: "gradient", value: "financeTeal" }, radius: 22, layers: [ { id: "badge", type: "chip", x: 16, y: 12, text: "FINANCE", style: "chip.news" }, { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 }, ], }, }, market: { mini_spec: { size: { w: 240, h: 96 }, background: { type: "gradient", value: "marketGold" }, radius: 18, layers: [ { id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf3d1", style: "text.icon", bind: "data.categoryIcon" }, { id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 }, { id: "badge", type: "chip", x: 94, y: 10, text: "MARKET", style: "chip.news" }, { id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 }, { id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }, ], }, full_spec: { size: { w: 360, h: 260 }, background: { type: "gradient", value: "marketGold" }, radius: 22, layers: [ { id: "badge", type: "chip", x: 16, y: 12, text: "MARKET", style: "chip.news" }, { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 }, ], }, }, friends: { mini_spec: { size: { w: 240, h: 96 }, background: { type: "gradient", value: "friendsGreen" }, radius: 18, layers: [ { id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf500", style: "text.icon", bind: "data.categoryIcon" }, { id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 }, { id: "badge", type: "chip", x: 94, y: 10, text: "FRIENDS", style: "chip.news" }, { id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 }, { id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }, ], }, full_spec: { size: { w: 360, h: 260 }, background: { type: "gradient", value: "friendsGreen" }, radius: 22, layers: [ { id: "badge", type: "chip", x: 16, y: 12, text: "FRIENDS", style: "chip.news" }, { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 }, ], }, }, events: { mini_spec: { size: { w: 240, h: 96 }, background: { type: "gradient", value: "eventsPurple" }, radius: 18, layers: [ { id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf073", style: "text.icon", bind: "data.categoryIcon" }, { id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 }, { id: "badge", type: "chip", x: 94, y: 10, text: "EVENTS", style: "chip.news" }, { id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 }, { id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }, ], }, full_spec: { size: { w: 360, h: 260 }, background: { type: "gradient", value: "eventsPurple" }, radius: 22, layers: [ { id: "badge", type: "chip", x: 16, y: 12, text: "EVENTS", style: "chip.news" }, { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 }, ], }, }, }; function parseDateAny(raw) { if (!raw) return null; if (raw instanceof Date) return raw; const s = String(raw).trim(); if (!s) return null; // handle "YYYY-MM-DD HH:mm:ss" const fixed = s.includes(" ") && !s.includes("T") ? s.replace(" ", "T") : s; const d = new Date(fixed); return Number.isNaN(d.getTime()) ? null : d; } function isBreaking(post) { if (post?.breaking === true || post?.is_breaking === true) return true; const t = (post?.title || "").toString().toLowerCase(); if (t.includes("breaking") || t.includes("urgent") || t.includes("alerte")) return true; const d = parseDateAny(post?.created_at || post?.CreatedAt || post?.createdAt); if (!d) return false; const mins = (Date.now() - d.getTime()) / 60000; return mins >= 0 && mins <= 45; } function normCat(post) { const c = (post?.category || post?.Category || "").toString().toUpperCase(); const sub = (post?.sub_category || post?.subCategory || "").toString().toLowerCase(); if (c === "MARKET") return "market"; if (c === "FRIENDS") return "friends"; if (c === "EVENT" || c === "EVENTS") return "events"; if (c === "NEWS" && (sub.includes("finan") || sub.includes("finance"))) return "finance"; if (c === "NEWS" && isBreaking(post)) return "breaking"; return "news"; } export function getTemplateKeyForPost(post) { return normCat(post); } export function getTemplateSpecForPost(post, variant /* "mini"|"full" */) { const key = getTemplateKeyForPost(post); const t = TEMPLATE_SPECS[key] || TEMPLATE_SPECS.news; return variant === "full" ? t.full_spec : t.mini_spec; } function getCategoryIcon(post) { const category = (post?.category || "").toUpperCase(); const subCategory = (post?.sub_category || "").toLowerCase(); // Mapping des icônes par catégorie et sous-catégorie const iconMap = { NEWS: { default: "\uf1ea", // fa-newspaper world: "\uf57d", // fa-globe-americas politics: "\uf0a3", // fa-landmark-flag tech: "\uf2db", // fa-microchip finance: "\uf201", // fa-chart-line local: "\uf3c5", // fa-map-marker-alt }, FRIENDS: { default: "\uf500", // fa-user-group nearby: "\uf3c5", // fa-location-crosshairs chats: "\uf086", // fa-comments groups: "\uf0c0", // fa-users stories: "\uf02d", // fa-book-open favs: "\uf004", // fa-heart }, EVENT: { default: "\uf073", // fa-calendar today: "\uf783", // fa-calendar-day weekend: "\uf784", // fa-calendar-week music: "\uf001", // fa-music sports: "\uf1e3", // fa-football local: "\uf3c5", // fa-map-marker-alt }, EVENTS: { default: "\uf073", // fa-calendar today: "\uf783", // fa-calendar-day weekend: "\uf784", // fa-calendar-week music: "\uf001", // fa-music sports: "\uf1e3", // fa-football local: "\uf3c5", // fa-map-marker-alt }, MARKET: { default: "\uf3d1", // fa-store actu: "\uf1ea", // fa-newspaper tech: "\uf2db", // fa-microchip finan: "\uf555", // fa-money-bill-trend-up sport: "\uf434", // fa-basketball deals: "\uf02b", // fa-tags }, }; const categoryIcons = iconMap[category] || iconMap.NEWS; return categoryIcons[subCategory] || categoryIcons.default; } export function adaptPostToTemplateData(post) { const headline = post?.title || "Untitled"; const author = post?.author ? `by ${post.author}` : ""; const sub = post?.sub_category ? String(post.sub_category) : ""; const source = author || sub || ""; const summary = post?.snippet || post?.body || ""; const url = post?.url || ""; const image = post?.image_large || post?.image_small || post?.image || post?.media_url || ""; const categoryIcon = getCategoryIcon(post); return { headline, source, summary, url, image, categoryIcon }; }