BB AI redesign: 4-item nav, Show coverage, About, status bar, wire ticker,
trends sidebar, Ask BB AI, neural-summary schema, 13-event seed.
Phases 1-10 (11 phases minus Phase 8 which is the pgvector marker).
DB:
bb.events — 13 industry events seeded (MPTS, ANGA COM,
BroadcastAsia, SET Expo, IBC, NAB NY,
NewTECHForum, SATIS, DPP EBS, SVVS,
Hamburg Open, CABSAT, NAB Show '27).
bb.article_events — composite PK (article_id, article_table, event_id)
links any article-table row to one or more events.
bb.banner_creatives — (no change tonight)
bb.{native_articles,
ai_rewritten_articles,
wp_imported_posts,
press_releases}.neural_summary jsonb — populated by Phase 7
analyzer (deferred).
bb.{native_articles,
ai_rewritten_articles}.featured boolean — featured carousel source.
Routes:
/api/events/upcoming — date-gated, enriched with is_live/days_until/
day_of_event/total_days.
/api/ask-bb-ai — Claude Opus 4.7 chat with prompt caching on
the system block, 30-msg/hr/IP rate limit.
Tool-use deferred — first cut is straight
LLM with archive-citation prompting.
Pages:
/show-coverage — index of all events, upcoming + past split.
/show-coverage/[slug] — hero + status (live/T-Nd/past) + schema.org
Event JSON-LD + tagged articles list.
/about, /about/{team,contact,advertise,press-kit}.
Components:
Header — 4-item nav: Show coverage (dropdown) /
Newsletter / Forum / About (dropdown).
Old NEWS/GEAR/TECHNOLOGY/ADVERTISE items
removed from nav (routes still exist).
EventsDropdown — 340px CSI panel with L-corner brackets,
pulsing dot, "BB AI" badge, live/T-Nd rows,
auto-updated stamp.
AboutDropdown — 5-item lighter treatment.
SystemStatusBar — Index online pulse, articles/sources/events
counts, build version. Above Header.
LiveWireTicker — replaces NewsTicker. [HH:MM] [SRC] prefix,
CSS marquee, doubled for seamless loop.
TrendsSidebar — BB AI detected trends, top 5 entities by
7d-vs-30d lift, vector-pass stamp.
Phase 6b: replace with pgvector.
AskBBAI — floating bottom-right button + 420px right
drawer chat. No mention of Anthropic/Claude.
Styling:
redesign-tokens.css — --color-text-info, --color-background-*,
--font-serif/mono/body, bb-pulse/bb-ring
animations + bb-marquee.
Constraints honored:
- LiveU 728x90 still between </nav> and ticker.
- Blackmagic 300x600 still pinned top of sidebar.
- /r/[slug] click tracking unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
src/app/api/events/upcoming/route.ts
Normal file
67
src/app/api/events/upcoming/route.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const revalidate = 300; // 5 min cache
|
||||
|
||||
interface Row {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
short_name: string | null;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
venue: string | null;
|
||||
city: string | null;
|
||||
country: string | null;
|
||||
url: string | null;
|
||||
status: string;
|
||||
hashtag: string | null;
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const url = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
|
||||
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
|
||||
const schema = process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb";
|
||||
if (!url || !key) return NextResponse.json({ events: [] }, { status: 200 });
|
||||
|
||||
const sb = createClient(url, key, {
|
||||
db: { schema: schema as "public" },
|
||||
auth: { persistSession: false },
|
||||
});
|
||||
|
||||
const nowIso = new Date().toISOString().slice(0, 10);
|
||||
const { data, error } = await sb
|
||||
.from("events")
|
||||
.select("id,slug,name,short_name,start_date,end_date,venue,city,country,url,status,hashtag")
|
||||
.gte("end_date", nowIso)
|
||||
.in("status", ["confirmed", "tentative"])
|
||||
.order("start_date", { ascending: true })
|
||||
.limit(6);
|
||||
|
||||
if (error) return NextResponse.json({ events: [], error: error.message }, { status: 500 });
|
||||
|
||||
const now = new Date();
|
||||
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
||||
|
||||
const events = (data || []).map((r: any) => {
|
||||
const start = new Date(r.start_date);
|
||||
const end = new Date(r.end_date);
|
||||
const isLive = today >= start && today <= end;
|
||||
const msDay = 86400 * 1000;
|
||||
const daysUntil = Math.ceil((start.getTime() - today.getTime()) / msDay);
|
||||
const totalDays = Math.max(1, Math.round((end.getTime() - start.getTime()) / msDay) + 1);
|
||||
const dayOfEvent = isLive
|
||||
? Math.round((today.getTime() - start.getTime()) / msDay) + 1
|
||||
: null;
|
||||
return {
|
||||
...r,
|
||||
is_live: isLive,
|
||||
days_until: daysUntil,
|
||||
day_of_event: dayOfEvent,
|
||||
total_days: totalDays,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({ events, generated_at: new Date().toISOString() });
|
||||
}
|
||||
Reference in New Issue
Block a user