diff --git a/src/components/campaigns/campaign-hero.tsx b/src/components/campaigns/campaign-hero.tsx index 365c376..0489134 100644 --- a/src/components/campaigns/campaign-hero.tsx +++ b/src/components/campaigns/campaign-hero.tsx @@ -91,13 +91,6 @@ export const CampaignHero = ({ campaign }: CampaignHeroProps) => { View on Platform )} - diff --git a/src/pages/campaign-detail.tsx b/src/pages/campaign-detail.tsx index 1babf94..21717ff 100644 --- a/src/pages/campaign-detail.tsx +++ b/src/pages/campaign-detail.tsx @@ -1,7 +1,6 @@ import { useMemo, useState } from 'react'; import { useParams } from 'react-router'; -import { Tabs, TabList, Tab, TabPanel } from '@/components/application/tabs/tabs'; import { CampaignHero } from '@/components/campaigns/campaign-hero'; import { KpiStrip } from '@/components/campaigns/kpi-strip'; import { AdCard } from '@/components/campaigns/ad-card'; @@ -9,28 +8,52 @@ import { ConversionFunnel } from '@/components/campaigns/conversion-funnel'; import { SourceBreakdown } from '@/components/campaigns/source-breakdown'; import { BudgetBar } from '@/components/campaigns/budget-bar'; import { HealthIndicator } from '@/components/campaigns/health-indicator'; -import { Button } from '@/components/base/buttons/button'; +import { LeadTable } from '@/components/leads/lead-table'; +import { LeadActivitySlideout } from '@/components/leads/lead-activity-slideout'; import { useCampaigns } from '@/hooks/use-campaigns'; import { useLeads } from '@/hooks/use-leads'; +import { useData } from '@/providers/data-provider'; import { formatCurrency, formatDateOnly } from '@/lib/format'; - -const detailTabs = [ - { id: 'overview', label: 'Overview' }, - { id: 'leads', label: 'Leads' }, -]; +import type { Lead } from '@/types/entities'; export const CampaignDetailPage = () => { const { id } = useParams<{ id: string }>(); - const [activeTab, setActiveTab] = useState('overview'); const { campaigns, ads } = useCampaigns(); const { leads } = useLeads(); + const { leadActivities } = useData(); const campaign = campaigns.find((c) => c.id === id); const campaignAds = useMemo(() => ads.filter((ad) => ad.campaignId === id), [ads, id]); const campaignLeads = useMemo(() => leads.filter((lead) => lead.campaignId === id), [leads, id]); + const [selectedIds, setSelectedIds] = useState([]); + const [sortField, setSortField] = useState('createdAt'); + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc'); + const [activityLead, setActivityLead] = useState(null); + + const handleSort = (field: string) => { + if (field === sortField) { + setSortDirection((d) => (d === 'asc' ? 'desc' : 'asc')); + } else { + setSortField(field); + setSortDirection('desc'); + } + }; + + const sortedLeads = useMemo(() => { + const copy = [...campaignLeads]; + const dir = sortDirection === 'asc' ? 1 : -1; + copy.sort((a, b) => { + const av = (a as any)[sortField] ?? ''; + const bv = (b as any)[sortField] ?? ''; + if (av === bv) return 0; + return av > bv ? dir : -dir; + }); + return copy; + }, [campaignLeads, sortField, sortDirection]); + if (!campaign) { return (
@@ -46,126 +69,122 @@ export const CampaignDetailPage = () => { return (
- {/* Hero header */} - {/* KPI strip */} - {/* Tabs */} -
- setActiveTab(String(key))}> - - {(item) => } - - - -
- {/* Left: Ads list */} -
+ {/* Main body: leads table on the left, campaign details + funnel + source on the right */} +
+
+
+
+

+ Leads ({campaignLeads.length}) +

+
+ {campaignLeads.length === 0 ? ( +
+ No leads from this campaign yet. +
+ ) : ( + setActivityLead(lead)} + /> + )} +
+ + {campaignAds.length > 0 && ( +
+

Ads ({campaignAds.length})

- {campaignAds.map((ad) => ( - - ))} - {campaignAds.length === 0 && ( -

- No ads for this campaign. -

- )} -
- - {/* Right: Details + Funnel + Source */} -
- {/* Campaign Details card */} -
-

Campaign Details

-
-
-
Type
-
- {campaign.campaignType?.replace(/_/g, ' ') ?? '--'} -
-
-
-
Platform
-
- {campaign.platform ?? '--'} -
-
-
-
Start Date
-
- {formatDateShort(campaign.startDate)} -
-
-
-
End Date
-
- {formatDateShort(campaign.endDate)} -
-
-
-
Budget
-
- {campaign.budget - ? formatCurrency(campaign.budget.amountMicros, campaign.budget.currencyCode) - : '--'} -
-
-
-
Impressions
-
- {campaign.impressionCount?.toLocaleString('en-IN') ?? '--'} -
-
-
-
Clicks
-
- {campaign.clickCount?.toLocaleString('en-IN') ?? '--'} -
-
-
- -
- - -
-
- - {/* Conversion Funnel */} - - - {/* Source Breakdown */} - -
-
- - - -
-
-

- {campaignLeads.length} lead{campaignLeads.length !== 1 ? 's' : ''} from this campaign -

-

- View the full leads table filtered by this campaign on the All Leads page. -

-
- +
+ {campaignAds.map((ad) => ( + + ))}
+ )} +
+ +
+
+

Campaign Details

+
+
+
Type
+
+ {campaign.campaignType?.replace(/_/g, ' ') ?? '--'} +
+
+
+
Platform
+
+ {campaign.platform ?? '--'} +
+
+
+
Start Date
+
+ {formatDateShort(campaign.startDate)} +
+
+
+
End Date
+
+ {formatDateShort(campaign.endDate)} +
+
+
+
Budget
+
+ {campaign.budget + ? formatCurrency(campaign.budget.amountMicros, campaign.budget.currencyCode) + : '--'} +
+
+
+
Impressions
+
+ {campaign.impressionCount?.toLocaleString('en-IN') ?? '--'} +
+
+
+
Clicks
+
+ {campaign.clickCount?.toLocaleString('en-IN') ?? '--'} +
+
+
+ +
+ + +
- - + + + + +
+
+ + {activityLead && ( + !open && setActivityLead(null)} + lead={activityLead} + activities={leadActivities} + /> + )}
); };