homepage: auto-promote advertiser stories + premium slots in Industry News
Three-tier sort on the Industry News feed: 1. Premium — articles pinned via adv.featured_story_slots (paid slots). Sorted by position ascending. Highlighted with an amber left-border gradient + ★ Featured ribbon. 2. Advertiser — articles whose title contains the company name of a current active banner advertiser on this property, AND were published in the last 48 hours. Highlighted with a blue left-border gradient + ◆ Advertiser ribbon. Multiple matches ordered by publish date. 3. General — everything else, newest first. After 48h, advertiser stories naturally drop back into the general tier. Premium slots fall off when their ends_at passes. Detection is title-only with case-insensitive word-boundary match; the longest matching company name wins so "Sony Pictures" beats "Sony" when both advertise. Backed by the new adv.active_advertisers_by_ property view. createAdminClient() now accepts an optional schema arg so we can hit adv from BB without juggling clients. API revalidate dropped 300 → 60s so the feed reflects campaign flips within a minute.
This commit is contained in:
@@ -23,6 +23,9 @@ interface ArticleItem {
|
||||
alt: string;
|
||||
source: "live" | "imported";
|
||||
externalUrl?: string;
|
||||
promoted_kind?: "premium" | "client" | null;
|
||||
promoted_advertiser?: string | null;
|
||||
promoted_until?: string | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -179,8 +182,19 @@ export default function ArticleFeed() {
|
||||
return matchCat && matchSource && matchSearch && matchAuthor && matchDate;
|
||||
});
|
||||
|
||||
// Sort filtered articles based on sortOrder
|
||||
// Sort filtered articles based on sortOrder. For every order we honor
|
||||
// the promoted-tier hierarchy first (premium > advertiser > general) so
|
||||
// paid placements and advertiser news always lead. Within each tier the
|
||||
// user's chosen sort applies.
|
||||
function promotedTier(a: ArticleItem): number {
|
||||
if (a.promoted_kind === "premium") return 0;
|
||||
if (a.promoted_kind === "client") return 1;
|
||||
return 2;
|
||||
}
|
||||
const sortedFiltered = [...filtered].sort((a, b) => {
|
||||
const ta = promotedTier(a);
|
||||
const tb = promotedTier(b);
|
||||
if (ta !== tb) return ta - tb;
|
||||
if (sortOrder === "recent") {
|
||||
const da = parseArticleDate(a.date);
|
||||
const db = parseArticleDate(b.date);
|
||||
@@ -566,9 +580,15 @@ export default function ArticleFeed() {
|
||||
? mobileInfeedAds[Math.floor((i + 1) / MOBILE_AD_EVERY_N - 1) % mobileInfeedAds.length]
|
||||
: null;
|
||||
|
||||
const promoted = article?.promoted_kind;
|
||||
const promotedClass = promoted === 'premium'
|
||||
? 'border-l-4 border-amber-400 bg-gradient-to-r from-amber-500/10 to-transparent pl-3 -ml-2 mr-0'
|
||||
: promoted === 'client'
|
||||
? 'border-l-4 border-[#3b82f6] bg-gradient-to-r from-[#3b82f6]/8 to-transparent pl-3 -ml-2 mr-0'
|
||||
: '';
|
||||
return (
|
||||
<Fragment key={`feed-${i}`}>
|
||||
<article className="article-card flex gap-3 md:gap-4 py-4 md:py-5 group cursor-pointer rounded-sm px-2 -mx-2">
|
||||
<article className={`article-card flex gap-3 md:gap-4 py-4 md:py-5 group cursor-pointer rounded-sm px-2 -mx-2 ${promotedClass}`}>
|
||||
<Link
|
||||
href={articleHref}
|
||||
{...linkProps}
|
||||
@@ -587,6 +607,16 @@ export default function ArticleFeed() {
|
||||
</Link>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5 md:gap-2 mb-1 md:mb-1.5 flex-wrap">
|
||||
{promoted === 'premium' && (
|
||||
<span className="font-body text-[9px] font-bold text-amber-300 bg-amber-500/20 border border-amber-500/40 px-1.5 py-0.5 rounded uppercase tracking-widest">
|
||||
★ Featured
|
||||
</span>
|
||||
)}
|
||||
{promoted === 'client' && (
|
||||
<span className="font-body text-[9px] font-bold text-[#7aa7d4] bg-[#3b82f6]/15 border border-[#3b82f6]/40 px-1.5 py-0.5 rounded uppercase tracking-widest" title={article?.promoted_advertiser ? `Advertiser coverage: ${article.promoted_advertiser}` : 'Advertiser coverage'}>
|
||||
◆ Advertiser
|
||||
</span>
|
||||
)}
|
||||
{article?.category && article.category.toLowerCase() !== "legacy" && (
|
||||
<span className="font-body text-[10px] font-bold text-[#3b82f6] uppercase tracking-wide">
|
||||
{article.category}
|
||||
|
||||
Reference in New Issue
Block a user