feat: Added frontend for The Catalyst tab (#10)

Added frontend for "The Catalyst" tab.

Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local>
Reviewed-on: #10
This commit was merged in pull request #10.
This commit is contained in:
2026-03-27 22:35:25 +05:30
parent 023ba48da2
commit 5478f2815e
56 changed files with 80532 additions and 187100 deletions

View File

@@ -0,0 +1,214 @@
import { create } from 'zustand';
import type {
Campaign,
MarketingAsset,
AdInsight,
LiveOptimizationEvent,
CatalystSettings,
LiveEventType,
} from '@/types';
// ── Mock Data ─────────────────────────────────────────────────────────────────
const mockCampaigns: Campaign[] = [
{
id: 'c1',
name: '3BHK Prestige Launch — Dubai Marina',
objective: 'OUTCOME_LEADS',
status: 'ACTIVE',
dailyBudget: 50000, // AED 500
lifetimeSpend: 2340000,
impressions: 487200,
clicks: 9744,
ctr: 2.0,
cpa: 240,
roi: 18.5,
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 7),
updatedAt: new Date(),
},
{
id: 'c2',
name: 'Penthouse Whale Retarget — Instagram',
objective: 'OUTCOME_SALES',
status: 'ACTIVE',
dailyBudget: 100000, // AED 1000
lifetimeSpend: 5800000,
impressions: 92400,
clicks: 2772,
ctr: 3.0,
cpa: 2094,
roi: 42.1,
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 14),
updatedAt: new Date(),
},
{
id: 'c3',
name: '1BHK Investment — Lookalike Audience',
objective: 'OUTCOME_TRAFFIC',
status: 'PAUSED',
dailyBudget: 25000, // AED 250
lifetimeSpend: 980000,
impressions: 213000,
clicks: 4260,
ctr: 2.0,
cpa: 230,
roi: 8.2,
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 3),
updatedAt: new Date(),
},
];
const mockAssets: MarketingAsset[] = [
{
id: 'a1',
name: 'Penthouse Cinematic — Sea View',
type: 'video',
status: 'ready',
localUrl: '/assets/renders/penthouse_wan22_001.mp4',
metaAssetId: 'meta_vid_83920',
language: 'en',
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2),
},
{
id: 'a2',
name: 'Arabic Poster — 3BHK (Qwen-2512)',
type: 'image',
status: 'uploaded',
localUrl: '/assets/renders/3bhk_qwen_ar.png',
metaAssetId: 'meta_img_74811',
language: 'ar',
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 5),
},
{
id: 'a3',
name: 'Amenity Deck Reel — Wan 2.2 14B',
type: 'video',
status: 'rendering',
renderMessage: 'Wan 2.2 is compositing the infinity pool reflection...',
language: 'en',
createdAt: new Date(),
},
{
id: 'a4',
name: 'English Poster — Penthouse Launch',
type: 'image',
status: 'queued',
renderMessage: 'Qwen-Image 2512 queued for cinematic poster render...',
language: 'en',
createdAt: new Date(),
},
];
const mockInsights: AdInsight[] = Array.from({ length: 14 }, (_, i) => {
const d = new Date(Date.now() - 1000 * 60 * 60 * 24 * (13 - i));
return {
adSetId: `as_${i}`,
adSetName: i % 2 === 0 ? '3BHK — Dubai Marina' : 'Penthouse Retarget',
spend: 800 + Math.floor(Math.random() * 400),
impressions: 18000 + Math.floor(Math.random() * 10000),
clicks: 360 + Math.floor(Math.random() * 200),
ctr: 1.8 + Math.random() * 1.5,
cpa: 190 + Math.floor(Math.random() * 120),
roi: 14 + Math.random() * 20,
date: d.toISOString().split('T')[0],
};
});
function makeLiveEvent(
type: LiveEventType,
message: string,
campaignName?: string,
value?: string
): LiveOptimizationEvent {
return {
id: `ev_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
type,
message,
campaignName,
timestamp: new Date(),
value,
};
}
const mockLiveEvents: LiveOptimizationEvent[] = [
makeLiveEvent('pause', 'Paused Ad Set B due to CPA exceeding AED 500 threshold.', '3BHK Prestige Launch', 'CPA: AED 512'),
makeLiveEvent('shift', 'Shifted AED 200/day budget from Ad Set B to Ad Set A (lower CPA).', '3BHK Prestige Launch', '+AED 200'),
makeLiveEvent('rotate', 'Rotated in Penthouse Cinematic (sea view) as new creative for A/B test.', 'Penthouse Whale Retarget'),
makeLiveEvent('optimize', 'Expanded Lookalike Audience to 2% similarity — 48k new reach.', '1BHK Investment', '+48k reach'),
makeLiveEvent('create', 'Created new Ad Set targeting High-Net-Worth Lookalike from 23 new CRM Closed/Won leads.', 'Penthouse Whale Retarget'),
];
// ── Store Types ────────────────────────────────────────────────────────────────
interface MarketingState {
campaigns: Campaign[];
activeAssets: MarketingAsset[];
adInsights: AdInsight[];
liveEvents: LiveOptimizationEvent[];
settings: CatalystSettings;
activeTab: 'studio' | 'command' | 'intelligence' | 'war-room';
// Actions
addCampaign: (campaign: Campaign) => void;
updateCampaign: (id: string, updates: Partial<Campaign>) => void;
addAsset: (asset: MarketingAsset) => void;
updateAsset: (id: string, updates: Partial<MarketingAsset>) => void;
updateInsights: (insights: AdInsight[]) => void;
pushLiveEvent: (event: LiveOptimizationEvent) => void;
setMetaSettings: (settings: Partial<CatalystSettings>) => void;
setActiveTab: (tab: MarketingState['activeTab']) => void;
}
// ── Store ─────────────────────────────────────────────────────────────────────
export const useMarketingStore = create<MarketingState>()((set) => ({
campaigns: mockCampaigns,
activeAssets: mockAssets,
adInsights: mockInsights,
liveEvents: mockLiveEvents,
activeTab: 'studio',
settings: {
metaAccessToken: '',
metaAdAccountId: '',
metaBusinessId: '',
metaAppId: '',
whatsappPhoneNumberId: '',
isConnected: false,
},
addCampaign: (campaign) =>
set((state) => ({ campaigns: [...state.campaigns, campaign] })),
updateCampaign: (id, updates) =>
set((state) => ({
campaigns: state.campaigns.map((c) =>
c.id === id ? { ...c, ...updates, updatedAt: new Date() } : c
),
})),
addAsset: (asset) =>
set((state) => ({ activeAssets: [...state.activeAssets, asset] })),
updateAsset: (id, updates) =>
set((state) => ({
activeAssets: state.activeAssets.map((a) =>
a.id === id ? { ...a, ...updates } : a
),
})),
updateInsights: (insights) => set({ adInsights: insights }),
pushLiveEvent: (event) =>
set((state) => ({
// Keep a rolling window of the latest 30 events
liveEvents: [event, ...state.liveEvents].slice(0, 30),
})),
setMetaSettings: (updates) =>
set((state) => ({
settings: { ...state.settings, ...updates },
})),
setActiveTab: (tab) => set({ activeTab: tab }),
}));