sw-fe/src/components/Map/templateSpecs.js

273 lines
11 KiB
JavaScript

/**
* 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 };
}