600 lines
28 KiB
SQL
600 lines
28 KiB
SQL
-- ============================================================
|
|
-- Migration: Sections 15-19
|
|
-- Timestamp: 20260322240000
|
|
-- Covers: event_calendar, author_style_profiles, show_story_queue,
|
|
-- translations, ui_translations, AV Beat seed companies,
|
|
-- pen name roster update (authorized rosters only),
|
|
-- blocked sources update (remove infocommshow.org),
|
|
-- language preferences
|
|
-- ============================================================
|
|
|
|
-- ─── 1. Event Calendar ───────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.event_calendar (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
event_name TEXT NOT NULL,
|
|
event_type TEXT NOT NULL DEFAULT 'trade_show' CHECK (event_type IN (
|
|
'trade_show','conference','regional_expo','awards','virtual'
|
|
)),
|
|
site_id INTEGER NOT NULL DEFAULT 1 REFERENCES public.sites(id),
|
|
city TEXT,
|
|
venue TEXT,
|
|
country TEXT,
|
|
start_date DATE,
|
|
end_date DATE,
|
|
story_engine_start_date DATE,
|
|
story_engine_end_date DATE,
|
|
official_site TEXT,
|
|
seo_tier INTEGER NOT NULL DEFAULT 3 CHECK (seo_tier BETWEEN 1 AND 4),
|
|
status TEXT NOT NULL DEFAULT 'upcoming' CHECK (status IN (
|
|
'upcoming','active','post_show','off_season','cancelled','unconfirmed'
|
|
)),
|
|
year INTEGER NOT NULL DEFAULT EXTRACT(YEAR FROM CURRENT_DATE)::INTEGER,
|
|
phase1_volume_min INTEGER NOT NULL DEFAULT 1,
|
|
phase1_volume_max INTEGER NOT NULL DEFAULT 3,
|
|
phase2_volume_min INTEGER NOT NULL DEFAULT 3,
|
|
phase2_volume_max INTEGER NOT NULL DEFAULT 5,
|
|
phase3_volume_min INTEGER NOT NULL DEFAULT 5,
|
|
phase3_volume_max INTEGER NOT NULL DEFAULT 8,
|
|
phase4_volume_min INTEGER NOT NULL DEFAULT 1,
|
|
phase4_volume_max INTEGER NOT NULL DEFAULT 3,
|
|
phase5_volume_min INTEGER NOT NULL DEFAULT 1,
|
|
phase5_volume_max INTEGER NOT NULL DEFAULT 2,
|
|
auto_publish_enabled BOOLEAN NOT NULL DEFAULT false,
|
|
engine_paused BOOLEAN NOT NULL DEFAULT false,
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_event_calendar_site_id ON public.event_calendar(site_id);
|
|
CREATE INDEX IF NOT EXISTS idx_event_calendar_year ON public.event_calendar(year);
|
|
CREATE INDEX IF NOT EXISTS idx_event_calendar_status ON public.event_calendar(status);
|
|
CREATE INDEX IF NOT EXISTS idx_event_calendar_start_date ON public.event_calendar(start_date);
|
|
|
|
ALTER TABLE public.event_calendar ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "admin_manage_event_calendar" ON public.event_calendar;
|
|
CREATE POLICY "admin_manage_event_calendar"
|
|
ON public.event_calendar FOR ALL TO authenticated
|
|
USING (true) WITH CHECK (true);
|
|
|
|
DROP POLICY IF EXISTS "public_read_event_calendar" ON public.event_calendar;
|
|
CREATE POLICY "public_read_event_calendar"
|
|
ON public.event_calendar FOR SELECT TO public USING (true);
|
|
|
|
-- ─── 2. Seed Broadcast Beat shows (site_id = 1) ──────────────────────────────
|
|
INSERT INTO public.event_calendar (
|
|
event_name, event_type, site_id, city, venue, country,
|
|
start_date, end_date, story_engine_start_date, story_engine_end_date,
|
|
official_site, seo_tier, year,
|
|
phase1_volume_min, phase1_volume_max,
|
|
phase2_volume_min, phase2_volume_max,
|
|
phase3_volume_min, phase3_volume_max,
|
|
phase4_volume_min, phase4_volume_max,
|
|
phase5_volume_min, phase5_volume_max,
|
|
notes
|
|
) VALUES
|
|
-- NAB Show Las Vegas
|
|
(
|
|
'NAB Show Las Vegas', 'trade_show', 1,
|
|
'Las Vegas', 'Las Vegas Convention Center', 'USA',
|
|
'2026-04-25', '2026-04-30',
|
|
'2026-02-24', '2026-05-14',
|
|
'nabshow.com', 1, 2026,
|
|
3, 3, 5, 8, 8, 12, 3, 5, 1, 2,
|
|
'Primary NAB Show. James Mercer primary byline for pillar content.'
|
|
),
|
|
-- NAB Show New York
|
|
(
|
|
'NAB Show New York', 'trade_show', 1,
|
|
'New York', 'Javits Center', 'USA',
|
|
'2026-10-19', '2026-10-21',
|
|
'2026-08-20', '2026-10-28',
|
|
'nabshow.com', 2, 2026,
|
|
3, 3, 5, 8, 8, 12, 3, 5, 1, 2,
|
|
'NAB Show New York. James Mercer primary byline.'
|
|
),
|
|
-- IBC Show
|
|
(
|
|
'IBC Show', 'trade_show', 1,
|
|
'Amsterdam', 'RAI Amsterdam', 'Netherlands',
|
|
'2026-09-11', '2026-09-14',
|
|
'2026-07-13', '2026-09-28',
|
|
'ibc.org', 1, 2026,
|
|
3, 3, 5, 8, 8, 10, 3, 5, 1, 2,
|
|
'European equivalent of NAB Las Vegas. Ellen Forsythe primary byline.'
|
|
),
|
|
-- BroadcastAsia / ATxSG
|
|
(
|
|
'BroadcastAsia / ATxSG', 'trade_show', 1,
|
|
'Singapore', 'Singapore Expo', 'Singapore',
|
|
'2026-05-27', '2026-05-29',
|
|
'2026-03-28', '2026-06-05',
|
|
'broadcastasia.com', 2, 2026,
|
|
2, 2, 4, 6, 6, 8, 2, 3, 1, 1,
|
|
'Cover as both BroadcastAsia and ATxSG/Asia Tech x Singapore. Ellen Forsythe primary byline.'
|
|
),
|
|
-- CABSAT
|
|
(
|
|
'CABSAT', 'trade_show', 1,
|
|
'Dubai', 'Dubai World Trade Centre', 'UAE',
|
|
'2026-03-10', '2026-03-12',
|
|
'2026-01-09', '2026-03-19',
|
|
'cabsat.com', 2, 2026,
|
|
2, 2, 3, 5, 5, 7, 2, 3, 1, 1,
|
|
'Premier broadcast/satellite event for Middle East, Africa, South Asia.'
|
|
),
|
|
-- MPTS
|
|
(
|
|
'MPTS — Media Production & Technology Show', 'trade_show', 1,
|
|
'London', 'Olympia London', 'UK',
|
|
'2026-05-13', '2026-05-14',
|
|
'2026-03-14', '2026-05-21',
|
|
'thempts.co.uk', 3, 2026,
|
|
1, 2, 3, 4, 4, 6, 1, 2, 1, 1,
|
|
'UK broadcast and media production technology event.'
|
|
),
|
|
-- NATPE Global
|
|
(
|
|
'NATPE Global', 'conference', 1,
|
|
'TBD', 'TBD', 'TBD',
|
|
NULL, NULL, NULL, NULL,
|
|
'natpe.org', 3, 2026,
|
|
1, 2, 2, 3, 3, 5, 1, 2, 1, 1,
|
|
'Confirm dates annually. Format and location varies. Content/programming focus.'
|
|
),
|
|
-- SET Expo
|
|
(
|
|
'SET Expo', 'trade_show', 1,
|
|
'São Paulo', 'São Paulo Expo', 'Brazil',
|
|
'2026-08-18', '2026-08-20',
|
|
'2026-07-04', '2026-08-27',
|
|
'setexpo.com.br', 3, 2026,
|
|
1, 2, 2, 4, 4, 6, 1, 2, 1, 1,
|
|
'Latin America largest broadcast tech event. English stories from Portuguese sources.'
|
|
),
|
|
-- NewsTECHForum
|
|
(
|
|
'NewsTECHForum', 'conference', 1,
|
|
'New York', 'TBD', 'USA',
|
|
'2026-01-14', '2026-01-15',
|
|
'2025-12-15', '2026-01-20',
|
|
'newstechforum.net', 4, 2026,
|
|
1, 1, 2, 3, 3, 4, 1, 1, 1, 1,
|
|
'News broadcasting technology. Smaller targeted event.'
|
|
),
|
|
-- Silicon Valley Video Summit
|
|
(
|
|
'Silicon Valley Video Summit (SVVS)', 'conference', 1,
|
|
'San Francisco Bay Area', 'TBD', 'USA',
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, 4, 2026,
|
|
1, 1, 2, 2, 2, 3, 1, 1, 1, 1,
|
|
'Confirm dates annually. Video technology innovation and streaming.'
|
|
),
|
|
-- TAB Show
|
|
(
|
|
'TAB Show', 'trade_show', 1,
|
|
'Austin', 'Austin Convention Center', 'USA',
|
|
'2026-08-12', '2026-08-13',
|
|
'2026-07-13', '2026-08-18',
|
|
'tab.org', 4, 2026,
|
|
1, 1, 2, 2, 3, 3, 1, 1, 1, 1,
|
|
'Texas Association of Broadcasters. Regional US broadcast show.'
|
|
),
|
|
-- Hamburg Open
|
|
(
|
|
'Hamburg Open', 'conference', 1,
|
|
'Hamburg', 'TBD', 'Germany',
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, 4, 2026,
|
|
1, 1, 2, 2, 2, 3, 1, 1, 1, 1,
|
|
'Confirm dates annually. European broadcast/media tech. DACH market.'
|
|
),
|
|
-- AES Convention (Fall NY)
|
|
(
|
|
'AES Convention — New York', 'trade_show', 1,
|
|
'New York', 'Javits Center', 'USA',
|
|
'2026-10-28', '2026-10-31',
|
|
'2026-09-13', '2026-11-07',
|
|
'aes.org/events/conventions/', 2, 2026,
|
|
1, 2, 3, 4, 4, 6, 1, 2, 1, 1,
|
|
'Fall NY convention — primary AES event. Audio-focused pen names preferred. Thomas Reeves primary byline.'
|
|
),
|
|
-- AES Convention (Spring Europe)
|
|
(
|
|
'AES Convention — Europe', 'trade_show', 1,
|
|
'TBD', 'TBD', 'Europe',
|
|
NULL, NULL, NULL, NULL,
|
|
'aes.org/events/conventions/', 2, 2026,
|
|
1, 2, 3, 4, 4, 6, 1, 2, 1, 1,
|
|
'Spring European convention. Confirm city annually. Thomas Reeves primary byline.'
|
|
)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- ─── 3. Seed AV Beat shows (site_id = 2) ─────────────────────────────────────
|
|
INSERT INTO public.event_calendar (
|
|
event_name, event_type, site_id, city, venue, country,
|
|
start_date, end_date, story_engine_start_date, story_engine_end_date,
|
|
official_site, seo_tier, year,
|
|
phase1_volume_min, phase1_volume_max,
|
|
phase2_volume_min, phase2_volume_max,
|
|
phase3_volume_min, phase3_volume_max,
|
|
phase4_volume_min, phase4_volume_max,
|
|
phase5_volume_min, phase5_volume_max,
|
|
notes
|
|
) VALUES
|
|
-- InfoComm
|
|
(
|
|
'InfoComm', 'trade_show', 2,
|
|
'Las Vegas', 'Las Vegas Convention Center', 'USA',
|
|
'2026-06-17', '2026-06-19',
|
|
'2026-04-18', '2026-07-03',
|
|
'infocommshow.org', 1, 2026,
|
|
3, 3, 5, 8, 8, 12, 3, 5, 1, 2,
|
|
'InfoComm 2026 Las Vegas June 13-19 (exhibits June 17-19). Rex Chandler primary byline. Cover AVIXA XL Awards and FORTÉ Smart Workplace hub.'
|
|
),
|
|
-- ISE
|
|
(
|
|
'ISE — Integrated Systems Europe', 'trade_show', 2,
|
|
'Barcelona', 'Fira Barcelona Gran Vía', 'Spain',
|
|
'2026-02-03', '2026-02-06',
|
|
'2025-12-05', '2026-02-20',
|
|
'iseurope.org', 1, 2026,
|
|
3, 3, 5, 8, 8, 10, 3, 5, 1, 2,
|
|
'World largest pro AV show. 92,000+ attendees. Cover ISE Innovation Awards. Rex Chandler / Dana Flux primary bylines.'
|
|
),
|
|
-- InfoComm EDGE Dubai
|
|
(
|
|
'InfoComm EDGE Dubai', 'trade_show', 2,
|
|
'Dubai', 'Festival Arena', 'UAE',
|
|
'2026-10-27', '2026-10-28',
|
|
'2026-09-12', '2026-11-04',
|
|
'avixa.org', 2, 2026,
|
|
2, 2, 3, 4, 4, 5, 1, 2, 1, 1,
|
|
'New AVIXA event launching 2026. Experiential format. GCC/Middle East focus. Chip Crosspoint / Derek Wainwright bylines.'
|
|
),
|
|
-- InfoComm China
|
|
(
|
|
'InfoComm China — Beijing', 'trade_show', 2,
|
|
'Beijing', 'TBD', 'China',
|
|
'2026-04-15', '2026-04-17',
|
|
'2026-03-16', '2026-04-24',
|
|
'infocommchina.com', 2, 2026,
|
|
1, 2, 2, 3, 3, 5, 1, 2, 1, 1,
|
|
'Asia leading pro AV show. Chinese AV manufacturers and Asia-Pacific market.'
|
|
),
|
|
-- InfoComm Latin America
|
|
(
|
|
'InfoComm América Latina', 'trade_show', 2,
|
|
'Mexico City', 'WTC Convention Center', 'Mexico',
|
|
'2026-10-21', '2026-10-23',
|
|
'2026-09-21', '2026-10-30',
|
|
'infocommshow.org', 2, 2026,
|
|
1, 2, 2, 3, 3, 5, 1, 2, 1, 1,
|
|
'Latin America leading pro AV. 5,000+ attendees. Congreso AVIXA co-located. Blair Presenter primary byline.'
|
|
),
|
|
-- InfoComm India
|
|
(
|
|
'InfoComm India', 'trade_show', 2,
|
|
'Mumbai', 'TBD', 'India',
|
|
'2026-09-10', '2026-09-11',
|
|
'2026-08-11', '2026-09-16',
|
|
'avixa.org', 3, 2026,
|
|
1, 1, 2, 2, 2, 3, 1, 1, 1, 1,
|
|
'India leading pro AV event. Fastest-growing AV market globally per AVIXA.'
|
|
),
|
|
-- FORTÉ LIVE
|
|
(
|
|
'FORTÉ LIVE Regional Expos', 'regional_expo', 2,
|
|
'Multiple US Cities', 'Various', 'USA',
|
|
NULL, NULL, NULL, NULL,
|
|
'ourforte.com/events', 3, 2026,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
'Per-city: 1 preview + 1 recap. Monitor ourforte.com/events for dates. Dallas, Milwaukee, Atlanta, DC area confirmed. FORTÉ is Premier Sponsor of InfoComm 2026.'
|
|
),
|
|
-- AVIXA Regional Events
|
|
(
|
|
'AVIXA Regional Events', 'regional_expo', 2,
|
|
'Various', 'Various', 'Various',
|
|
NULL, NULL, NULL, NULL,
|
|
'avixa.org/events', 4, 2026,
|
|
1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
|
|
'Monitor avixa.org/events. Cover events with exhibition component. Skip pure training events.'
|
|
)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- ─── 4. Author Style Profiles ────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.author_style_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
author_username TEXT NOT NULL,
|
|
event_type TEXT NOT NULL DEFAULT 'general',
|
|
event_name TEXT,
|
|
style_guide_text TEXT,
|
|
source_article_count INTEGER NOT NULL DEFAULT 0,
|
|
last_updated TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_author_style_profiles_unique
|
|
ON public.author_style_profiles(author_username, event_type, COALESCE(event_name, ''));
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_author_style_profiles_username ON public.author_style_profiles(author_username);
|
|
CREATE INDEX IF NOT EXISTS idx_author_style_profiles_event ON public.author_style_profiles(event_type);
|
|
|
|
ALTER TABLE public.author_style_profiles ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "admin_manage_author_style_profiles" ON public.author_style_profiles;
|
|
CREATE POLICY "admin_manage_author_style_profiles"
|
|
ON public.author_style_profiles FOR ALL TO authenticated
|
|
USING (true) WITH CHECK (true);
|
|
|
|
-- ─── 5. Show Story Queue (extends auto_story_queue for show-specific stories) ─
|
|
ALTER TABLE public.auto_story_queue
|
|
ADD COLUMN IF NOT EXISTS event_id UUID REFERENCES public.event_calendar(id) ON DELETE SET NULL,
|
|
ADD COLUMN IF NOT EXISTS show_phase TEXT CHECK (show_phase IN (
|
|
'phase1_buildup','phase2_surge','phase3_show_week','phase4_post_show','phase5_off_season'
|
|
)),
|
|
ADD COLUMN IF NOT EXISTS show_story_type TEXT CHECK (show_story_type IN (
|
|
'exhibitor_preview','award_announcement','show_preview_guide','product_announcement',
|
|
'keynote_preview','travel_logistics','breaking_announcement','show_floor_report',
|
|
'award_winner','best_of_roundup','recap','product_availability','nab_cross_post'
|
|
)),
|
|
ADD COLUMN IF NOT EXISTS source_show TEXT,
|
|
ADD COLUMN IF NOT EXISTS nab_exhibitor_id UUID,
|
|
ADD COLUMN IF NOT EXISTS seo_primary_keyword TEXT,
|
|
ADD COLUMN IF NOT EXISTS seo_meta_description TEXT;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_auto_story_queue_event_id ON public.auto_story_queue(event_id);
|
|
CREATE INDEX IF NOT EXISTS idx_auto_story_queue_show_phase ON public.auto_story_queue(show_phase);
|
|
|
|
-- ─── 6. NAB Exhibitors ───────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.nab_exhibitors (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
company_id UUID REFERENCES public.tracked_companies(id) ON DELETE CASCADE,
|
|
company_name TEXT NOT NULL,
|
|
event_id UUID REFERENCES public.event_calendar(id) ON DELETE CASCADE,
|
|
booth_number TEXT,
|
|
preview_story_status TEXT NOT NULL DEFAULT 'pending' CHECK (preview_story_status IN (
|
|
'pending','generated','published','skipped'
|
|
)),
|
|
announcement_count INTEGER NOT NULL DEFAULT 0,
|
|
is_av_eligible BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(company_name, event_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_nab_exhibitors_event_id ON public.nab_exhibitors(event_id);
|
|
CREATE INDEX IF NOT EXISTS idx_nab_exhibitors_company_id ON public.nab_exhibitors(company_id);
|
|
|
|
ALTER TABLE public.nab_exhibitors ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "admin_manage_nab_exhibitors" ON public.nab_exhibitors;
|
|
CREATE POLICY "admin_manage_nab_exhibitors"
|
|
ON public.nab_exhibitors FOR ALL TO authenticated
|
|
USING (true) WITH CHECK (true);
|
|
|
|
-- ─── 7. Translations table ───────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.translations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
post_id UUID NOT NULL,
|
|
post_type TEXT NOT NULL DEFAULT 'native' CHECK (post_type IN ('native','imported')),
|
|
language_code TEXT NOT NULL CHECK (language_code IN ('en','es','pt','fr','de','zh','ja','ar','hi')),
|
|
title_translated TEXT,
|
|
content_translated TEXT,
|
|
excerpt_translated TEXT,
|
|
meta_description_translated TEXT,
|
|
slug_translated TEXT,
|
|
generated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
is_stale BOOLEAN NOT NULL DEFAULT false,
|
|
word_count INTEGER,
|
|
UNIQUE(post_id, language_code)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_translations_post_id ON public.translations(post_id);
|
|
CREATE INDEX IF NOT EXISTS idx_translations_language ON public.translations(language_code);
|
|
CREATE INDEX IF NOT EXISTS idx_translations_stale ON public.translations(is_stale) WHERE is_stale = true;
|
|
|
|
ALTER TABLE public.translations ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "public_read_translations" ON public.translations;
|
|
CREATE POLICY "public_read_translations"
|
|
ON public.translations FOR SELECT TO public USING (true);
|
|
|
|
DROP POLICY IF EXISTS "admin_manage_translations" ON public.translations;
|
|
CREATE POLICY "admin_manage_translations"
|
|
ON public.translations FOR ALL TO authenticated
|
|
USING (true) WITH CHECK (true);
|
|
|
|
-- ─── 8. UI Translations (admin-generated public labels) ─────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.ui_translations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
key TEXT NOT NULL,
|
|
language_code TEXT NOT NULL CHECK (language_code IN ('en','es','pt','fr','de','zh','ja','ar','hi')),
|
|
value TEXT NOT NULL,
|
|
context TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(key, language_code)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_ui_translations_key ON public.ui_translations(key);
|
|
CREATE INDEX IF NOT EXISTS idx_ui_translations_lang ON public.ui_translations(language_code);
|
|
|
|
ALTER TABLE public.ui_translations ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "public_read_ui_translations" ON public.ui_translations;
|
|
CREATE POLICY "public_read_ui_translations"
|
|
ON public.ui_translations FOR SELECT TO public USING (true);
|
|
|
|
DROP POLICY IF EXISTS "admin_manage_ui_translations" ON public.ui_translations;
|
|
CREATE POLICY "admin_manage_ui_translations"
|
|
ON public.ui_translations FOR ALL TO authenticated
|
|
USING (true) WITH CHECK (true);
|
|
|
|
-- ─── 9. Translation Queue ────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.translation_queue (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
post_id UUID NOT NULL,
|
|
post_type TEXT NOT NULL DEFAULT 'native',
|
|
language_code TEXT NOT NULL,
|
|
priority INTEGER NOT NULL DEFAULT 5,
|
|
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','processing','completed','failed')),
|
|
error_message TEXT,
|
|
queued_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
processed_at TIMESTAMPTZ,
|
|
UNIQUE(post_id, language_code, status)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_translation_queue_status ON public.translation_queue(status);
|
|
CREATE INDEX IF NOT EXISTS idx_translation_queue_priority ON public.translation_queue(priority DESC, queued_at ASC);
|
|
|
|
ALTER TABLE public.translation_queue ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "admin_manage_translation_queue" ON public.translation_queue;
|
|
CREATE POLICY "admin_manage_translation_queue"
|
|
ON public.translation_queue FOR ALL TO authenticated
|
|
USING (true) WITH CHECK (true);
|
|
|
|
-- ─── 10. User Language Preferences ──────────────────────────────────────────
|
|
ALTER TABLE public.user_profiles
|
|
ADD COLUMN IF NOT EXISTS lang_pref TEXT DEFAULT 'en' CHECK (lang_pref IN ('en','es','pt','fr','de','zh','ja','ar','hi'));
|
|
|
|
-- ─── 11. Update pen_names table with authorized rosters ──────────────────────
|
|
-- Remove old placeholder BB pen names and insert authorized roster
|
|
DELETE FROM public.pen_names WHERE site_id = 1;
|
|
|
|
INSERT INTO public.pen_names (name, site_id, beat, bio, is_active) VALUES
|
|
('Michael Strand', 1, 'broadcast-technology', 'Senior Technology Editor. Broadcast technology, industry news, NAB Show primary correspondent.', true),
|
|
('David Harlow', 1, 'broadcast-engineering', 'Broadcast Engineering Correspondent. Broadcast engineering, transmission, RF, satellite, signal distribution.', true),
|
|
('Karen Fielding', 1, 'post-production', 'Post Production Editor. Post production, editing, color grading, VFX, media asset management.', true),
|
|
('James Mercer', 1, 'nab-show', 'NAB Show Senior Correspondent. NAB Show Las Vegas, NAB Show New York, show floor coverage, exhibitor news. PRIMARY byline for all major NAB pillar content, day recaps, and best-of roundups.', true),
|
|
('Peter Calloway', 1, 'radio-transmission', 'Radio & Transmission Reporter. Radio broadcasting, AM/FM/DAB, transmission infrastructure, tower and antenna technology.', true),
|
|
('Sandra Voss', 1, 'production-technology', 'Production Technology Editor. Studio production, cameras, switchers, graphics systems, live production.', true),
|
|
('Brian Kowalski', 1, 'streaming-ott', 'Streaming & OTT Correspondent. Streaming technology, OTT platforms, CDN, video delivery, cloud broadcast.', true),
|
|
('Laura Pennington', 1, 'industry-news', 'Industry News Editor. Company news, acquisitions, appointments, executive moves, industry business coverage.', true),
|
|
('Thomas Reeves', 1, 'audio-technology', 'Audio Technology Correspondent. Broadcast audio, mixing consoles, audio processing, AES Convention coverage.', true),
|
|
('Christine Vale', 1, 'business-industry', 'Business & Industry Reporter. Industry analysis, market trends, opinion, business of broadcast.', true),
|
|
('Marcus Webb', 1, 'live-production', 'Live Production Correspondent. Live events, sports broadcasting, remote production, REMI and IP production workflows.', true),
|
|
('Ellen Forsythe', 1, 'international-markets', 'International Markets Editor. IBC Show, BroadcastAsia, CABSAT, international broadcast markets, European and Asian coverage.', true)
|
|
ON CONFLICT (name) DO UPDATE SET
|
|
site_id = EXCLUDED.site_id,
|
|
beat = EXCLUDED.beat,
|
|
bio = EXCLUDED.bio,
|
|
is_active = EXCLUDED.is_active;
|
|
|
|
-- Update AV Beat pen names with corrected roster (replace Mitch Boardroom with Derek Wainwright)
|
|
DELETE FROM public.pen_names WHERE site_id = 2;
|
|
|
|
INSERT INTO public.pen_names (name, site_id, beat, bio, is_active) VALUES
|
|
('Rex Chandler', 2, 'av-integration', 'AV Integration Senior Correspondent. AV systems integration, integrator news, company announcements, InfoComm coverage.', true),
|
|
('Dana Flux', 2, 'digital-signage', 'Digital Signage & Display Editor. Digital signage, LED walls, display technology, CMS platforms.', true),
|
|
('Derek Wainwright', 2, 'unified-communications', 'Unified Communications Correspondent. UC&C, video conferencing, hybrid meeting rooms, Microsoft Teams Rooms, Zoom Rooms, enterprise collaboration technology.', true),
|
|
('Sloane Rigging', 2, 'sound-audio', 'Installed AV & Audio Editor. Installed audio, sound reinforcement, houses of worship AV, DSP, loudspeakers, microphone systems.', true),
|
|
('Chip Crosspoint', 2, 'av-networking', 'AV Networking & Technology Correspondent. AV-over-IP, Dante, IPMX, control systems, IT/AV convergence, networked AV.', true),
|
|
('Blair Presenter', 2, 'education-corporate-av', 'Education & Corporate AV Editor. Education AV, classroom technology, corporate AV, government AV, ISE coverage.', true),
|
|
('Jordan Lumen', 2, 'display-visualization', 'Display & Visualization Correspondent. Projectors, large-venue displays, video walls, visualization technology, simulation.', true)
|
|
ON CONFLICT (name) DO UPDATE SET
|
|
site_id = EXCLUDED.site_id,
|
|
beat = EXCLUDED.beat,
|
|
bio = EXCLUDED.bio,
|
|
is_active = EXCLUDED.is_active;
|
|
|
|
-- ─── 12. Fix blocked_sources: remove infocommshow.org, add correct list ──────
|
|
UPDATE public.company_tracker_settings
|
|
SET blocked_sources = ARRAY[
|
|
'tvtechnology.com','broadcastingcable.com','sportsvideo.org',
|
|
'digitaltveurope.com','sportspromedia.com','nexttv.com',
|
|
'ravepubs.com','avnetwork.com','avtechnology.com',
|
|
'commercialintegrator.com','systemscontractor.com',
|
|
'avinteriors.com','avinteractive.com','avmagazine.net'
|
|
]
|
|
WHERE blocked_sources IS NOT NULL;
|
|
|
|
-- ─── 13. Seed AV Beat tracked companies (Section 19F) ────────────────────────
|
|
INSERT INTO public.tracked_companies (company_name, company_website, site_scope, auto_story_enabled, crawl_priority, mention_count)
|
|
VALUES
|
|
('Crestron', 'crestron.com', 'avbeat', true, 'high', 5),
|
|
('Extron', 'extron.com', 'avbeat', true, 'high', 5),
|
|
('AMX', 'amx.com', 'avbeat', true, 'standard', 3),
|
|
('QSC', 'qsc.com', 'avbeat', true, 'high', 5),
|
|
('Biamp', 'biamp.com', 'avbeat', true, 'standard', 3),
|
|
('Shure', 'shure.com', 'both', true, 'high', 5),
|
|
('Sennheiser', 'sennheiser.com', 'both', true, 'standard', 3),
|
|
('Audio-Technica', 'audio-technica.com', 'both', true, 'standard', 3),
|
|
('Bose Professional', 'pro.bose.com', 'avbeat', true, 'standard', 3),
|
|
('JBL Professional', 'jblpro.com', 'avbeat', true, 'standard', 3),
|
|
('Crown Audio', 'crownaudio.com', 'avbeat', true, 'standard', 3),
|
|
('BSS Audio', 'bssaudio.com', 'avbeat', true, 'standard', 3),
|
|
('Audinate', 'audinate.com', 'avbeat', true, 'standard', 3),
|
|
('Samsung Professional', 'samsung.com/business', 'avbeat', true, 'high', 5),
|
|
('LG Business Solutions', 'lg.com/business', 'avbeat', true, 'high', 5),
|
|
('Sony Professional', 'pro.sony', 'both', true, 'high', 5),
|
|
('Panasonic Business', 'business.panasonic.com', 'both', true, 'standard', 3),
|
|
('Sharp NEC', 'sharpnecdisplays.com', 'avbeat', true, 'standard', 3),
|
|
('Barco', 'barco.com', 'avbeat', true, 'high', 5),
|
|
('Christie', 'christiedigital.com', 'avbeat', true, 'standard', 3),
|
|
('Epson Professional', 'epson.com/professional', 'avbeat', true, 'standard', 3),
|
|
('BenQ', 'benq.com', 'avbeat', true, 'standard', 3),
|
|
('Optoma', 'optoma.com', 'avbeat', true, 'standard', 3),
|
|
('Planar', 'planar.com', 'avbeat', true, 'standard', 3),
|
|
('Leyard', 'leyard.com', 'avbeat', true, 'standard', 3),
|
|
('Absen', 'absen.com', 'avbeat', true, 'standard', 3),
|
|
('Daktronics', 'daktronics.com', 'avbeat', true, 'standard', 3),
|
|
('Primeview', 'primeviewglobal.com', 'avbeat', true, 'standard', 3),
|
|
('Unilumin', 'unilumin.com', 'avbeat', true, 'standard', 3),
|
|
('Peerless-AV', 'peerless-av.com', 'avbeat', true, 'standard', 3),
|
|
('Chief', 'chiefmfg.com', 'avbeat', true, 'standard', 3),
|
|
('Middle Atlantic', 'middleatlantic.com', 'avbeat', true, 'standard', 3),
|
|
('Legrand AV', 'legrandav.com', 'avbeat', true, 'standard', 3),
|
|
('Atlona', 'atlona.com', 'avbeat', true, 'standard', 3),
|
|
('Kramer', 'kramerav.com', 'avbeat', true, 'standard', 3),
|
|
('ATEN', 'aten.com', 'avbeat', true, 'standard', 3),
|
|
('Black Box', 'blackbox.com', 'avbeat', true, 'standard', 3),
|
|
('Mersive', 'mersive.com', 'avbeat', true, 'standard', 3),
|
|
('Logitech Video Collaboration', 'logitech.com/video-collaboration', 'avbeat', true, 'high', 5),
|
|
('Poly', 'poly.com', 'avbeat', true, 'standard', 3),
|
|
('Jabra', 'jabra.com', 'avbeat', true, 'standard', 3),
|
|
('DTEN', 'dten.com', 'avbeat', true, 'standard', 3),
|
|
('Neat', 'neat.no', 'avbeat', true, 'standard', 3),
|
|
('Yealink', 'yealink.com', 'avbeat', true, 'standard', 3),
|
|
('Cisco Webex', 'webex.com', 'avbeat', true, 'high', 5),
|
|
('Crestron Flex', 'crestron.com/flex', 'avbeat', true, 'standard', 3),
|
|
('AVI-SPL', 'avispl.com', 'avbeat', true, 'standard', 3),
|
|
('Diversified', 'diversifiedus.com', 'avbeat', true, 'standard', 3),
|
|
('AVI Systems', 'avisystems.com', 'avbeat', true, 'standard', 3),
|
|
('FORTÉ', 'ourforte.com', 'avbeat', true, 'high', 5)
|
|
ON CONFLICT (company_name) DO UPDATE SET
|
|
site_scope = EXCLUDED.site_scope,
|
|
auto_story_enabled = EXCLUDED.auto_story_enabled,
|
|
crawl_priority = EXCLUDED.crawl_priority;
|
|
|
|
-- ─── 14. Triggers ────────────────────────────────────────────────────────────
|
|
DROP TRIGGER IF EXISTS update_event_calendar_updated_at ON public.event_calendar;
|
|
CREATE TRIGGER update_event_calendar_updated_at
|
|
BEFORE UPDATE ON public.event_calendar
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at();
|
|
|
|
DROP TRIGGER IF EXISTS update_ui_translations_updated_at ON public.ui_translations;
|
|
CREATE TRIGGER update_ui_translations_updated_at
|
|
BEFORE UPDATE ON public.ui_translations
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at();
|
|
|
|
-- ─── 15. Seed UI translations (English base keys) ────────────────────────────
|
|
INSERT INTO public.ui_translations (key, language_code, value, context) VALUES
|
|
('nav.home', 'en', 'Home', 'Navigation'),
|
|
('nav.forum', 'en', 'Forum', 'Navigation'),
|
|
('nav.advertise', 'en', 'Advertise', 'Navigation'),
|
|
('nav.about', 'en', 'About', 'Navigation'),
|
|
('nav.subscribe', 'en', 'Subscribe', 'Navigation'),
|
|
('lang.en', 'en', 'English', 'Language switcher'),
|
|
('lang.es', 'en', 'Español', 'Language switcher'),
|
|
('lang.pt', 'en', 'Português', 'Language switcher'),
|
|
('lang.fr', 'en', 'Français', 'Language switcher'),
|
|
('lang.de', 'en', 'Deutsch', 'Language switcher'),
|
|
('lang.zh', 'en', '中文', 'Language switcher'),
|
|
('lang.ja', 'en', '日本語', 'Language switcher'),
|
|
('lang.ar', 'en', 'العربية', 'Language switcher'),
|
|
('lang.hi', 'en', 'हिंदी', 'Language switcher')
|
|
ON CONFLICT (key, language_code) DO NOTHING;
|