av: clone broadcastbeat sources + AV Beat rebrand (Phase 1)

- Replaces the prior Rocket scaffold (saved on pre-bb-clone-rollback branch)
- Domain: broadcastbeat.com -> avbeat.com
- Brand: Broadcast Beat -> AV Beat
- Slug: broadcastbeat -> avbeat (package, identifiers)
- Schema fallback: bb -> av (env var name unchanged)
- Placeholder AV BEAT logo (gold->red gradient) at /assets/logos/av.svg
- All BB/RMP logo references repointed

Outstanding before public DNS swap:
  - Supabase av schema bootstrap (mirror of bb tables)
  - WordPress archive import (avbeat-com -> av.articles)
  - Coolify env vars
  - dev-avbeat.onsethost.com staging deploy + visual review
This commit is contained in:
Claude
2026-06-02 15:32:56 +00:00
parent dd93a74bdf
commit d43f78b161
137 changed files with 4728 additions and 4541 deletions

View File

@@ -47,7 +47,7 @@ interface Stats {
}
const PUBLICATIONS = [
{ id: 'broadcast-beat', name: 'Broadcast Beat', short: 'BB' },
{ id: 'broadcast-beat', name: 'AV Beat', short: 'BB' },
{ id: 'av-beat', name: 'AV Beat', short: 'AV' },
];
@@ -218,7 +218,7 @@ export default function AdOpsDashboard() {
</button>
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-orange-900/50 text-orange-300 border border-orange-700">
<span className="w-1.5 h-1.5 rounded-full bg-orange-400" />
adops@broadcastbeat.com
adops@avbeat.com
</span>
</div>
</div>

View File

@@ -191,9 +191,9 @@ const QUICK_ACTIONS = [
];
const QUICK_ACTION_TEMPLATES: Record<string, string> = {
url_story: `Write a news story for [Broadcast Beat / AV Beat] based on this URL: [URL]. Use the byline [pen name]. Target length: [400-600] words. Focus angle: [product launch / company news / industry trend].`,
url_story: `Write a news story for [AV Beat / AV Beat] based on this URL: [URL]. Use the byline [pen name]. Target length: [400-600] words. Focus angle: [product launch / company news / industry trend].`,
exhibitor_preview: `Write a pre-show exhibitor preview for [company name] at [NAB Show / InfoComm] [year]. They will be showing [product/technology]. Booth: [number if known]. Use [pen name] byline. 400-500 words.`,
press_release: `Rewrite the following press release as a real news story for [Broadcast Beat / AV Beat]. Remove marketing language, lead with the news hook, add industry context. Byline: [pen name].
press_release: `Rewrite the following press release as a real news story for [AV Beat / AV Beat]. Remove marketing language, lead with the news hook, add industry context. Byline: [pen name].
[PASTE PRESS RELEASE HERE]`,
nab_story: `Write a [story type: preview / announcement / recap] story about [topic/company] for NAB Show [year]. Include relevant SEO keywords. Byline: James Mercer. [400-800] words.`,
@@ -203,7 +203,7 @@ Story headline: [HEADLINE]
Story summary: [SUMMARY]`,
headlines: `Generate 8 headline options for this story. Include 2 SEO-optimized versions targeting [keyword], 2 social media-friendly versions, 2 trade publication style versions, and 2 alternatives. Story summary: [SUMMARY]`,
translate: `Translate the following story into [language]. Maintain the professional trade publication tone. Keep technical terms in their internationally recognized form. [PASTE STORY HERE]`,
company_bio: `Write a 150-word company bio for [company name] suitable for use as a boilerplate paragraph at the end of news stories on [Broadcast Beat / AV Beat]. The company makes [products/services]. Website: [URL].`,
company_bio: `Write a 150-word company bio for [company name] suitable for use as a boilerplate paragraph at the end of news stories on [AV Beat / AV Beat]. The company makes [products/services]. Website: [URL].`,
product_desc: `Write a 200-word product description for [product name] by [company]. Key features: [features]. Target audience: [integrators / broadcast engineers / etc]. Suitable for use in a product announcement story.`,
};
@@ -243,7 +243,7 @@ function DraftPreviewModal({ content, title, penName, siteId, onClose, onSave }:
<div className="flex items-center justify-between px-5 py-4 border-b border-[#1e2a3a]">
<div>
<h2 className="text-white font-semibold text-sm">Draft Preview</h2>
<p className="text-[#555] text-xs mt-0.5">Review before saving to {siteId === 1 ? 'Broadcast Beat' : 'AV Beat'}</p>
<p className="text-[#555] text-xs mt-0.5">Review before saving to {siteId === 1 ? 'AV Beat' : 'AV Beat'}</p>
</div>
<button onClick={onClose} className="text-[#555] hover:text-white transition-colors text-lg leading-none"></button>
</div>
@@ -256,7 +256,7 @@ function DraftPreviewModal({ content, title, penName, siteId, onClose, onSave }:
<div className="flex items-center gap-2">
<span className="text-[#555] text-xs">Site:</span>
<span className={`text-xs font-bold px-1.5 py-0.5 rounded ${siteId === 2 ? 'bg-orange-900/40 text-orange-400' : 'bg-blue-900/40 text-blue-400'}`}>
{siteId === 2 ? 'AV Beat' : 'Broadcast Beat'}
{siteId === 2 ? 'AV Beat' : 'AV Beat'}
</span>
</div>
<div className="flex items-center gap-2">
@@ -487,7 +487,7 @@ function StyleGuidesPanel({ onClose, onSelectPenName }: StyleGuidesPanelProps) {
onClick={() => setFilterSite(f)}
className={`text-xs px-2.5 py-1 rounded transition-colors ${filterSite === f ? 'bg-[#3b82f6] text-white' : 'bg-[#1e2a3a] text-[#aaa] hover:text-white'}`}
>
{f === 'ALL' ? 'All Writers' : f === 'BB' ? 'Broadcast Beat' : 'AV Beat'}
{f === 'ALL' ? 'All Writers' : f === 'BB' ? 'AV Beat' : 'AV Beat'}
</button>
))}
</div>
@@ -670,7 +670,7 @@ export default function AIConsolePage() {
const penNames = selectedSite === 1 ? BB_PEN_NAMES : AV_PEN_NAMES;
const buildSystemPrompt = () => {
const siteName = selectedSite === 1 ? 'Broadcast Beat' : 'AV Beat';
const siteName = selectedSite === 1 ? 'AV Beat' : 'AV Beat';
const siteDesc = selectedSite === 1
? 'a professional B2B trade publication for broadcast engineers, production professionals, and industry executives'
: 'a professional B2B trade publication for AV integrators, technology managers, and AV industry professionals';
@@ -1068,7 +1068,7 @@ Respond conversationally and helpfully. If asked to write a story, write the ful
onChange={e => setSelectedSite(Number(e.target.value) as 1 | 2)}
className="bg-[#1e2a3a] border border-[#2a3a50] text-white text-xs rounded px-2 py-1 focus:outline-none focus:border-[#3b82f6]"
>
<option value={1}>Broadcast Beat</option>
<option value={1}>AV Beat</option>
<option value={2}>AV Beat</option>
</select>
</div>

View File

@@ -303,12 +303,12 @@ export default function AIConsoleSettingsPage() {
onChange={e => update('default_site_id', Number(e.target.value))}
className="w-full bg-[#0d1117] border border-[#2a3a50] text-white text-sm rounded-lg px-3 py-2.5 focus:outline-none focus:border-[#3b82f6]"
>
<option value={1}>Broadcast Beat</option>
<option value={1}>AV Beat</option>
<option value={2}>AV Beat</option>
</select>
</div>
<div>
<label className="block text-[#aaa] text-xs font-medium mb-1.5">Default Pen Name (Broadcast Beat)</label>
<label className="block text-[#aaa] text-xs font-medium mb-1.5">Default Pen Name (AV Beat)</label>
<select
value={settings.default_pen_name_bb}
onChange={e => update('default_pen_name_bb', e.target.value)}

View File

@@ -256,7 +256,7 @@ export default function ShowCalendarPage() {
if (loading) {
return (
<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center">
<div className="w-8 h-8 border-2 border-[#E67E22] border-t-transparent rounded-full animate-spin" />
<div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" />
</div>
);
}
@@ -294,7 +294,7 @@ export default function ShowCalendarPage() {
<div className="flex items-center gap-3">
<button
onClick={() => { setEditingEvent({ site_id: 1, year: new Date().getFullYear(), seo_tier: 3, event_type: 'trade_show' }); setShowEventModal(true); }}
className="px-3 py-1.5 bg-[#E67E22] hover:bg-blue-500 text-white text-sm rounded-lg transition-colors"
className="px-3 py-1.5 bg-[#3b82f6] hover:bg-blue-500 text-white text-sm rounded-lg transition-colors"
>
+ Add Event
</button>
@@ -316,7 +316,7 @@ export default function ShowCalendarPage() {
onClick={() => setSiteFilter(s)}
className={`px-3 py-1 rounded text-xs font-medium transition-colors ${
siteFilter === s
? 'bg-[#E67E22] text-white'
? 'bg-[#3b82f6] text-white'
: 'bg-[#1a1a1a] text-[#888] hover:text-white border border-[#333]'
}`}
>
@@ -333,7 +333,7 @@ export default function ShowCalendarPage() {
onClick={() => setActiveTab(tab.id)}
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px ${
activeTab === tab.id
? 'border-[#E67E22] text-white'
? 'border-[#3b82f6] text-white'
: 'border-transparent text-[#666] hover:text-white'
}`}
>
@@ -347,7 +347,7 @@ export default function ShowCalendarPage() {
<div className="max-w-7xl mx-auto px-4 py-6">
{loadingData ? (
<div className="flex items-center justify-center py-20">
<div className="w-8 h-8 border-2 border-[#E67E22] border-t-transparent rounded-full animate-spin" />
<div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" />
</div>
) : (
<>
@@ -383,7 +383,7 @@ export default function ShowCalendarPage() {
<span>Engine: {formatDate(event.story_engine_start_date)} {formatDate(event.story_engine_end_date)}</span>
{event.official_site && (
<a href={`https://${event.official_site}`} target="_blank" rel="noopener noreferrer"
className="text-[#E67E22] hover:underline">{event.official_site}</a>
className="text-[#3b82f6] hover:underline">{event.official_site}</a>
)}
</div>
<div className="flex items-center gap-3 mt-2 text-xs text-[#555]">
@@ -440,7 +440,7 @@ export default function ShowCalendarPage() {
onClick={() => setQueueStatus(s)}
className={`px-3 py-1 rounded text-xs font-medium transition-colors ${
queueStatus === s
? 'bg-[#E67E22] text-white'
? 'bg-[#3b82f6] text-white'
: 'bg-[#1a1a1a] text-[#888] hover:text-white border border-[#333]'
}`}
>
@@ -779,7 +779,7 @@ export default function ShowCalendarPage() {
<button
type="submit"
disabled={actionLoading === 'save-event'}
className="flex-1 py-2 bg-[#E67E22] hover:bg-blue-500 text-white text-sm rounded-lg transition-colors disabled:opacity-50"
className="flex-1 py-2 bg-[#3b82f6] hover:bg-blue-500 text-white text-sm rounded-lg transition-colors disabled:opacity-50"
>
{actionLoading === 'save-event' ? 'Saving...' : 'Save Event'}
</button>
@@ -857,7 +857,7 @@ export default function ShowCalendarPage() {
<button
type="submit"
disabled={actionLoading === 'generate'}
className="flex-1 py-2 bg-[#E67E22] hover:bg-blue-500 text-white text-sm rounded-lg transition-colors disabled:opacity-50"
className="flex-1 py-2 bg-[#3b82f6] hover:bg-blue-500 text-white text-sm rounded-lg transition-colors disabled:opacity-50"
>
{actionLoading === 'generate' ? 'Generating...' : 'Generate Story'}
</button>