mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
fix: campaign detail — cards above table layout (stacked, not side-by-side)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Campaign Details, Conversion Funnel, Source Breakdown now render as 3-column horizontal cards above the leads table. Table gets full width. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,108 +73,77 @@ export const CampaignDetailPage = () => {
|
|||||||
|
|
||||||
<KpiStrip campaign={campaign} />
|
<KpiStrip campaign={campaign} />
|
||||||
|
|
||||||
{/* Main body: leads table on the left, campaign details + funnel + source on the right */}
|
{/* Campaign details + funnel + source — horizontal cards above table */}
|
||||||
<div className="px-7 pt-5 pb-7">
|
<div className="px-7 pt-5">
|
||||||
<div className="grid grid-cols-1 gap-5 xl:grid-cols-[1fr_340px]">
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-3 mb-6">
|
||||||
<div className="space-y-6">
|
<div className="rounded-xl border border-secondary bg-primary p-4">
|
||||||
<div>
|
<h4 className="mb-3 text-sm font-bold text-primary">Campaign Details</h4>
|
||||||
<div className="mb-3 flex items-center justify-between">
|
<dl className="space-y-1.5 text-xs">
|
||||||
<h3 className="text-md font-bold text-primary">
|
{[
|
||||||
Leads ({campaignLeads.length})
|
['Type', campaign.campaignType?.replace(/_/g, ' ') ?? '--'],
|
||||||
</h3>
|
['Platform', campaign.platform ?? '--'],
|
||||||
</div>
|
['Start', formatDateShort(campaign.startDate)],
|
||||||
{campaignLeads.length === 0 ? (
|
['End', formatDateShort(campaign.endDate)],
|
||||||
<div className="rounded-xl border border-secondary bg-primary p-8 text-center text-sm text-tertiary">
|
['Budget', campaign.budget ? formatCurrency(campaign.budget.amountMicros, campaign.budget.currencyCode) : '--'],
|
||||||
No leads from this campaign yet.
|
['Impressions', campaign.impressionCount?.toLocaleString('en-IN') ?? '--'],
|
||||||
|
['Clicks', campaign.clickCount?.toLocaleString('en-IN') ?? '--'],
|
||||||
|
].map(([label, value]) => (
|
||||||
|
<div key={label} className="flex justify-between">
|
||||||
|
<dt className="text-quaternary">{label}</dt>
|
||||||
|
<dd className="font-medium text-secondary">{value}</dd>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
))}
|
||||||
<LeadTable
|
</dl>
|
||||||
leads={sortedLeads}
|
<div className="mt-3 space-y-2">
|
||||||
selectedIds={selectedIds}
|
<BudgetBar spent={campaign.amountSpent} budget={campaign.budget} />
|
||||||
onSelectionChange={setSelectedIds}
|
<HealthIndicator campaign={campaign} leads={campaignLeads} />
|
||||||
sortField={sortField}
|
|
||||||
sortDirection={sortDirection}
|
|
||||||
onSort={handleSort}
|
|
||||||
onViewActivity={(lead) => setActivityLead(lead)}
|
|
||||||
visibleColumns={new Set(['phone', 'name', 'source', 'status', 'lastContactedAt', 'createdAt'])}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{campaignAds.length > 0 && (
|
<ConversionFunnel campaign={campaign} leads={campaignLeads} />
|
||||||
<div>
|
<SourceBreakdown leads={campaignLeads} />
|
||||||
<h3 className="mb-3 text-md font-bold text-primary">
|
</div>
|
||||||
Ads ({campaignAds.length})
|
</div>
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
{/* Leads table — full width */}
|
||||||
{campaignAds.map((ad) => (
|
<div className="px-7 pb-7">
|
||||||
<AdCard key={ad.id} ad={ad} />
|
<div className="space-y-6">
|
||||||
))}
|
<div>
|
||||||
</div>
|
<div className="mb-3 flex items-center justify-between">
|
||||||
|
<h3 className="text-md font-bold text-primary">
|
||||||
|
Leads ({campaignLeads.length})
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
{campaignLeads.length === 0 ? (
|
||||||
|
<div className="rounded-xl border border-secondary bg-primary p-8 text-center text-sm text-tertiary">
|
||||||
|
No leads from this campaign yet.
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<LeadTable
|
||||||
|
leads={sortedLeads}
|
||||||
|
selectedIds={selectedIds}
|
||||||
|
onSelectionChange={setSelectedIds}
|
||||||
|
sortField={sortField}
|
||||||
|
sortDirection={sortDirection}
|
||||||
|
onSort={handleSort}
|
||||||
|
onViewActivity={(lead) => setActivityLead(lead)}
|
||||||
|
visibleColumns={new Set(['phone', 'name', 'source', 'status', 'lastContactedAt', 'createdAt'])}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
{campaignAds.length > 0 && (
|
||||||
<div className="rounded-xl border border-secondary bg-primary p-4">
|
<div>
|
||||||
<h4 className="mb-3 text-sm font-bold text-primary">Campaign Details</h4>
|
<h3 className="mb-3 text-md font-bold text-primary">
|
||||||
<dl className="space-y-2 text-xs">
|
Ads ({campaignAds.length})
|
||||||
<div className="flex justify-between">
|
</h3>
|
||||||
<dt className="text-quaternary">Type</dt>
|
<div className="space-y-3">
|
||||||
<dd className="font-medium text-secondary">
|
{campaignAds.map((ad) => (
|
||||||
{campaign.campaignType?.replace(/_/g, ' ') ?? '--'}
|
<AdCard key={ad.id} ad={ad} />
|
||||||
</dd>
|
))}
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-quaternary">Platform</dt>
|
|
||||||
<dd className="font-medium text-secondary">
|
|
||||||
{campaign.platform ?? '--'}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-quaternary">Start Date</dt>
|
|
||||||
<dd className="font-medium text-secondary">
|
|
||||||
{formatDateShort(campaign.startDate)}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-quaternary">End Date</dt>
|
|
||||||
<dd className="font-medium text-secondary">
|
|
||||||
{formatDateShort(campaign.endDate)}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-quaternary">Budget</dt>
|
|
||||||
<dd className="font-medium text-secondary">
|
|
||||||
{campaign.budget
|
|
||||||
? formatCurrency(campaign.budget.amountMicros, campaign.budget.currencyCode)
|
|
||||||
: '--'}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-quaternary">Impressions</dt>
|
|
||||||
<dd className="font-medium text-secondary">
|
|
||||||
{campaign.impressionCount?.toLocaleString('en-IN') ?? '--'}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-quaternary">Clicks</dt>
|
|
||||||
<dd className="font-medium text-secondary">
|
|
||||||
{campaign.clickCount?.toLocaleString('en-IN') ?? '--'}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<div className="mt-4 space-y-3">
|
|
||||||
<BudgetBar spent={campaign.amountSpent} budget={campaign.budget} />
|
|
||||||
<HealthIndicator campaign={campaign} leads={campaignLeads} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<ConversionFunnel campaign={campaign} leads={campaignLeads} />
|
|
||||||
|
|
||||||
<SourceBreakdown leads={campaignLeads} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user