Ad Spend Roi Dashboard
blockmarketingMulti-channel paid acquisition ad spend, blended ROAS, and CAC performance dashboard: 4 primary KPI cards (total spend, revenue with blended ROAS, CAC, conversions), channel performance comparison table with live toggle switches, and top 3 performing campaign breakdown.
Installation
$pnpm dlx uipkge-ng@latest add ad-spend-roi-dashboard$npx uipkge-ng@latest add ad-spend-roi-dashboard$yarn dlx uipkge-ng@latest add ad-spend-roi-dashboard$bunx uipkge-ng@latest add ad-spend-roi-dashboardManifest:
https://uipkge.dev/r/angular/ad-spend-roi-dashboard.jsonInstalls to:src/app/components/blocks/ad-spend-roi-dashboard/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
AdChannelPerformanceinterface AdChannelPerformance {
id: string
name: string
campaignCount: number
spend: string
impressions: string
clicks: string
ctr: string
cpc: string
cpa: string
roas: string
roasStatus: ChannelRoasStatus
conversions: number
revenue: string
active: boolean
icon: LucideIconData
iconClass: string
}TopCampaigninterface TopCampaign {
id: string
rank: number
name: string
channel: string
targetNiche: string
spend: string
revenue: string
cpa: string
roas: string
}Files installed (2)
src/app/components/blocks/ad-spend-roi-dashboard/ad-spend-roi-dashboard.component.ts27.6 kB
// Multi-channel paid acquisition ad spend, blended ROAS, and CAC performance // dashboard: KPI cards, channel comparison table with live toggles, and top // campaigns. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core' import { Award, Calendar, Download, LucideAngularModule, Megaphone, Plus, Search, Share2, Target, TrendingDown, TrendingUp, Users, Wallet, Zap, type LucideIconData, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiProgressComponent } from '@/ui/progress/progress.component' import { UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, } from '@/ui/select/select.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiSwitchComponent } from '@/ui/switch/switch.component' import { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' export type ChannelRoasStatus = 'high' | 'medium' | 'low' export interface AdChannelPerformance { id: string name: string campaignCount: number spend: string impressions: string clicks: string ctr: string cpc: string cpa: string roas: string roasStatus: ChannelRoasStatus conversions: number revenue: string active: boolean icon: LucideIconData iconClass: string } export interface TopCampaign { id: string rank: number name: string channel: string targetNiche: string spend: string revenue: string cpa: string roas: string } const initialChannels: AdChannelPerformance[] = [ { id: 'google-search', name: 'Google Search Ads', campaignCount: 6, spend: '$11,200.00', impressions: '248,500', clicks: '14,910', ctr: '6.00%', cpc: '$0.75', cpa: '$42.10', roas: '4.2x', roasStatus: 'high', conversions: 266, revenue: '$47,040.00', active: true, icon: Search, iconClass: 'bg-info/10 text-info border-info/20', }, { id: 'meta-ads', name: 'Meta / Instagram Ads', campaignCount: 8, spend: '$7,650.00', impressions: '420,000', clicks: '8,400', ctr: '2.00%', cpc: '$0.91', cpa: '$49.35', roas: '3.1x', roasStatus: 'high', conversions: 155, revenue: '$23,715.00', active: true, icon: Share2, iconClass: 'bg-chart-1/10 text-chart-1 border-chart-1/20', }, { id: 'linkedin-ads', name: 'LinkedIn Sponsored', campaignCount: 3, spend: '$3,800.00', impressions: '54,000', clicks: '1,080', ctr: '2.00%', cpc: '$3.52', cpa: '$64.40', roas: '2.4x', roasStatus: 'medium', conversions: 59, revenue: '$9,120.00', active: true, icon: Megaphone, iconClass: 'bg-info/10 text-info border-info/20', }, { id: 'twitter-ads', name: 'Twitter / X Ads', campaignCount: 2, spend: '$2,200.00', impressions: '110,000', clicks: '1,650', ctr: '1.50%', cpc: '$1.33', cpa: '$68.75', roas: '1.8x', roasStatus: 'low', conversions: 32, revenue: '$3,960.00', active: false, icon: Zap, iconClass: 'bg-zinc-500/10 text-zinc-600 text-muted-foreground border-zinc-500/20', }, ] const topCampaigns: TopCampaign[] = [ { id: 'camp-1', rank: 1, name: 'Q3 Retargeting · High-Intent Abandoned Cart', channel: 'Google Search Ads', targetNiche: 'SaaS Mid-Market Decision Makers', spend: '$4,120.00', revenue: '$22,248.00', cpa: '$34.33', roas: '5.4x', }, { id: 'camp-2', rank: 2, name: 'Competitor Keyword Conquesting - Core US', channel: 'Google Search Ads', targetNiche: 'Direct B2B Search Intent', spend: '$6,800.00', revenue: '$31,280.00', cpa: '$38.20', roas: '4.6x', }, { id: 'camp-3', rank: 3, name: 'Founder Video Testimonial - Lookalike 1%', channel: 'Meta / Instagram Ads', targetNiche: 'Seed & Series A Tech Founders', spend: '$3,450.00', revenue: '$13,110.00', cpa: '$46.00', roas: '3.8x', }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-ad-spend-roi-dashboard, [ui-ad-spend-roi-dashboard]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, UiProgressComponent, UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, UiSeparatorComponent, UiSwitchComponent, UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <div data-slot="ad-spend-roi-dashboard" [class]="rootClass"> <!-- Header --> <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between"> <div class="space-y-1"> <h1 class="text-foreground text-2xl font-bold tracking-tight sm:text-3xl">Paid Acquisition & Ad Spend ROI</h1> <p class="text-muted-foreground text-sm"> Multi-channel paid acquisition ad spend, blended ROAS, and CAC performance dashboard. </p> </div> <div class="flex flex-wrap items-center gap-2.5"> <ui-select [value]="selectedPeriod()" (valueChange)="selectedPeriod.set($event)"> <button ui-select-trigger class="w-56 text-xs" aria-label="Select date range"> <lucide-icon [img]="Calendar" class="text-muted-foreground mr-1.5 size-3.5" /> <ui-select-value placeholder="Select period" /> </button> <ui-select-content> <ui-select-item value="this-month">This Month · Aug 2026</ui-select-item> <ui-select-item value="last-month">Last Month · Jul 2026</ui-select-item> <ui-select-item value="last-30">Last 30 Days</ui-select-item> <ui-select-item value="last-90">Last 90 Days (Q3)</ui-select-item> <ui-select-item value="ytd">Year to Date · 2026</ui-select-item> </ui-select-content> </ui-select> <button ui-button aria-label="Download attachment" variant="outline" size="sm" class="gap-1.5 text-xs"> <lucide-icon [img]="Download" class="size-3.5" /> <span>Export Campaign CSV</span> </button> <button ui-button size="sm" class="gap-1.5 text-xs"> <lucide-icon [img]="Plus" class="size-3.5" /> <span>Connect Ad Account</span> </button> </div> </div> <!-- 4 Primary Performance KPI Cards --> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="border-border bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Wallet" class="size-4" /> </div> <h3 ui-card-title class="text-muted-foreground text-sm font-medium">Total Ad Spend</h3> </div> <span ui-badge wrap variant="outline" class="text-xs font-normal tabular-nums">82.8% pacing</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="flex items-baseline gap-1.5"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">$24,850.00</span> </div> <p class="text-muted-foreground mt-0.5 text-xs tabular-nums">Monthly budget $30,000.00</p> </div> <div class="space-y-1.5"> <ui-progress [value]="82.8" class="[&_[data-slot=progress-indicator]]:bg-primary h-1.5" /> <div class="text-muted-foreground flex items-center justify-between text-xs tabular-nums"> <span>$5,150.00 headroom</span> <span>10 days left</span> </div> </div> </div> </div> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="border-success/20 bg-success/10 text-success flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="TrendingUp" class="size-4" /> </div> <h3 ui-card-title class="text-muted-foreground text-sm font-medium">Attributed Revenue</h3> </div> <span ui-badge wrap variant="outline" class="border-success/20 bg-success/10 text-success text-xs font-bold tabular-nums" > 3.39x Blended ROAS </span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="flex items-baseline gap-1.5"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">$84,200.00</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Direct ad touch attribution</p> </div> <div class="border-border/60 border-t pt-2 text-xs"> <div class="flex items-center justify-between"> <span class="text-muted-foreground">Period growth:</span> <span class="text-success font-semibold tabular-nums">+$18,450.00 (+28.1%)</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Target ROAS: 2.80x (exceeded)</p> </div> </div> </div> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="border-border bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Target" class="size-4" /> </div> <h3 ui-card-title class="text-muted-foreground text-sm font-medium">Customer Acquisition Cost</h3> </div> <span ui-badge wrap variant="outline" class="border-success/20 bg-success/10 text-success gap-1 text-xs font-medium tabular-nums" > <lucide-icon [img]="TrendingDown" class="size-3" /> <span>-$6.20 vs last month</span> </span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="flex items-baseline gap-1.5"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">$48.50</span> <span class="text-muted-foreground text-xs">/ customer</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Blended across all 4 channels</p> </div> <div class="border-border/60 border-t pt-2 text-xs"> <div class="flex items-center justify-between"> <span class="text-muted-foreground">Target CAC ceiling:</span> <span class="text-foreground font-semibold tabular-nums">$55.00 max</span> </div> <p class="text-success mt-0.5 text-xs font-medium">11.8% below budget cap</p> </div> </div> </div> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="border-border bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Users" class="size-4" /> </div> <h3 ui-card-title class="text-muted-foreground text-sm font-medium">Total Conversions</h3> </div> <span ui-badge wrap variant="outline" class="text-xs font-normal tabular-nums">+64 vs Jul</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="flex items-baseline gap-1.5"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">512</span> <span class="text-foreground text-sm font-semibold">Paid Customers</span> </div> <p class="text-muted-foreground mt-0.5 text-xs tabular-nums">Avg order value $164.45</p> </div> <div class="border-border/60 border-t pt-2 text-xs"> <div class="flex items-center justify-between"> <span class="text-muted-foreground">Blended Conversion Rate:</span> <span class="text-foreground font-semibold tabular-nums">14.2%</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Organic lift multiplier: 1.18x</p> </div> </div> </div> </div> <!-- Channel Performance Comparison Table --> <div ui-card class="shadow-xs"> <div ui-card-header> <div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between"> <div> <h3 ui-card-title>Channel Performance Comparison</h3> <p ui-card-description> Unit economics, click-through rates, acquisition volumes, and return on ad spend across channels. </p> </div> <span ui-badge wrap variant="outline" class="w-fit text-xs font-normal tabular-nums"> 4 channels active </span> </div> </div> <div ui-card-content class="p-0 sm:p-6 sm:pt-0"> <div class="overflow-x-auto"> <table ui-table> <thead ui-table-header> <tr ui-table-row> <th ui-table-head class="min-w-[200px]">Channel & Campaigns</th> <th ui-table-head class="text-right">Spend</th> <th ui-table-head class="text-right">Impressions & Clicks (CTR)</th> <th ui-table-head class="text-right">CPC & CPA</th> <th ui-table-head class="text-right">ROAS</th> <th ui-table-head class="text-right">Conversions & Rev</th> <th ui-table-head class="text-right">Status</th> </tr> </thead> <tbody ui-table-body> @for (channel of channels(); track channel.id) { <tr ui-table-row class="hover:bg-muted/50"> <td ui-table-cell> <div class="flex items-center gap-3"> <div [class]="channelIconClass(channel)" aria-hidden="true"> <lucide-icon [img]="channel.icon" class="size-4" /> </div> <div> <div class="text-foreground text-sm font-semibold">{{ channel.name }}</div> <div class="text-muted-foreground text-xs tabular-nums"> {{ channel.campaignCount }} active campaigns </div> </div> </div> </td> <td ui-table-cell class="text-right"> <div class="text-foreground text-sm font-semibold tabular-nums">{{ channel.spend }}</div> <div class="text-muted-foreground text-xs tabular-nums">{{ spendShare(channel) }}% of total</div> </td> <td ui-table-cell class="text-right"> <div class="text-foreground text-xs font-medium tabular-nums">{{ channel.impressions }} imp</div> <div class="text-muted-foreground text-xs tabular-nums"> {{ channel.clicks }} clicks ({{ channel.ctr }}) </div> </td> <td ui-table-cell class="text-right"> <div class="text-foreground text-xs font-semibold tabular-nums">{{ channel.cpc }} CPC</div> <div class="text-muted-foreground text-xs tabular-nums">{{ channel.cpa }} CPA</div> </td> <td ui-table-cell class="text-right"> <span ui-badge variant="outline" [class]="roasBadgeClass(channel)"> {{ channel.roas }} ROAS </span> </td> <td ui-table-cell class="text-right"> <div class="text-foreground text-xs font-bold tabular-nums">{{ channel.revenue }}</div> <div class="text-muted-foreground text-xs tabular-nums"> {{ channel.conversions }} conversions </div> </td> <td ui-table-cell class="text-right"> <div class="flex items-center justify-end gap-2"> <span class="text-muted-foreground text-xs font-medium"> {{ channel.active ? 'Active' : 'Paused' }} </span> <button ui-switch [checked]="channel.active" [attr.aria-label]="'Toggle status for ' + channel.name" (checkedChange)="handleToggleChannel(channel.id, $event)" ></button> </div> </td> </tr> } </tbody> </table> </div> </div> </div> <!-- Bottom Section: Top Performing Campaigns & Budget Allocation --> <div class="grid grid-cols-1 gap-6 lg:grid-cols-2"> <div ui-card class="shadow-xs"> <div ui-card-header> <div class="flex items-center justify-between"> <h3 ui-card-title>Top Performing Campaigns</h3> <span ui-badge wrap variant="outline" class="gap-1 text-xs"> <lucide-icon [img]="Award" class="text-warning size-3" /> <span>Highest ROAS</span> </span> </div> <p ui-card-description>Ad creatives and targeting sets driving highest attribution revenue and ROAS.</p> </div> <div ui-card-content class="space-y-4"> @for (campaign of topCampaigns; track campaign.id) { <div class="border-border bg-muted/30 hover:bg-muted/60 flex flex-col gap-3 rounded-lg border p-4 transition-colors" > <div class="flex items-start justify-between gap-2"> <div class="flex items-start gap-2.5"> <span ui-badge wrap [variant]="campaign.rank === 1 ? 'default' : 'secondary'" class="flex size-6 shrink-0 items-center justify-center rounded-full p-0 text-xs font-bold tabular-nums" > #{{ campaign.rank }} </span> <div class="space-y-0.5"> <h4 class="text-foreground text-sm font-semibold">{{ campaign.name }}</h4> <div class="flex flex-wrap items-center gap-1.5"> <span ui-badge wrap variant="outline" class="text-xs font-normal">{{ campaign.channel }}</span> <span class="text-muted-foreground text-xs">{{ campaign.targetNiche }}</span> </div> </div> </div> <span ui-badge wrap variant="outline" class="border-success/20 bg-success/10 text-success shrink-0 text-xs font-bold tabular-nums" > {{ campaign.roas }} ROAS </span> </div> <div class="border-border/60 grid grid-cols-1 gap-2 border-t pt-2 text-xs sm:grid-cols-3"> <div> <span class="text-muted-foreground">Spend:</span> <p class="text-foreground font-semibold tabular-nums">{{ campaign.spend }}</p> </div> <div> <span class="text-muted-foreground">Attributed Rev:</span> <p class="text-success font-semibold tabular-nums">{{ campaign.revenue }}</p> </div> <div class="sm:text-right"> <span class="text-muted-foreground">Unit CPA:</span> <p class="text-foreground font-semibold tabular-nums">{{ campaign.cpa }}</p> </div> </div> </div> } </div> </div> <div ui-card class="shadow-xs"> <div ui-card-header> <div class="flex items-center justify-between"> <h3 ui-card-title>Budget Allocation & Guardrails</h3> <span ui-badge wrap variant="outline" class="text-xs font-normal">Automated Pacing</span> </div> <p ui-card-description> Live capital distribution by ad channel with automated spend-shift recommendations. </p> </div> <div ui-card-content class="space-y-4"> <div class="border-border bg-muted/40 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 rounded-lg border p-4" > <div> <p class="text-muted-foreground text-xs font-medium tracking-wider uppercase">Total Deployed Spend</p> <p class="text-foreground text-3xl font-bold tracking-tight tabular-nums">$24,850.00</p> </div> <div class="text-right"> <span class="text-muted-foreground text-xs"> Budget Cap: <strong class="text-foreground font-semibold tabular-nums">$30,000.00</strong> </span> <p class="text-success text-xs font-semibold tabular-nums">$5,150.00 (17.2%) remaining</p> </div> </div> <div class="space-y-3 text-xs"> <div class="space-y-1"> <div class="flex items-center justify-between"> <span class="text-foreground font-medium">Google Search Ads (45.1%)</span> <span class="text-foreground font-semibold tabular-nums">$11,200.00 / 4.2x ROAS</span> </div> <ui-progress [value]="45.1" class="[&_[data-slot=progress-indicator]]:bg-info h-1.5" /> </div> <div class="space-y-1"> <div class="flex items-center justify-between"> <span class="text-foreground font-medium">Meta / Instagram Ads (30.8%)</span> <span class="text-foreground font-semibold tabular-nums">$7,650.00 / 3.1x ROAS</span> </div> <ui-progress [value]="30.8" class="[&_[data-slot=progress-indicator]]:bg-chart-1 h-1.5" /> </div> <div class="space-y-1"> <div class="flex items-center justify-between"> <span class="text-foreground font-medium">LinkedIn Sponsored (15.3%)</span> <span class="text-foreground font-semibold tabular-nums">$3,800.00 / 2.4x ROAS</span> </div> <ui-progress [value]="15.3" class="[&_[data-slot=progress-indicator]]:bg-info h-1.5" /> </div> <div class="space-y-1"> <div class="flex items-center justify-between"> <span class="text-foreground font-medium">Twitter / X Ads (8.8%)</span> <span class="text-muted-foreground font-semibold tabular-nums">$2,200.00 / 1.8x ROAS</span> </div> <ui-progress [value]="8.8" class="h-1.5 [&_[data-slot=progress-indicator]]:bg-zinc-500" /> </div> </div> <ui-separator /> <div class="border-border/80 bg-muted/20 flex items-start gap-3 rounded-lg border p-3"> <lucide-icon [img]="TrendingUp" class="text-primary mt-0.5 size-4 shrink-0" /> <div class="space-y-1"> <h5 class="text-foreground text-xs font-semibold">Growth Optimization Insight</h5> <p class="text-muted-foreground text-xs leading-relaxed"> Google Search & Meta campaigns are outperforming blended ROAS target (+0.59x). Pausing Twitter / X Ads saves $73.30/day to reallocate into the highest-converting Q3 Retargeting set. </p> </div> </div> </div> </div> </div> </div> `, }) export class UiAdSpendRoiDashboardComponent { protected readonly Award = Award protected readonly Calendar = Calendar protected readonly Download = Download protected readonly Plus = Plus protected readonly Target = Target protected readonly TrendingDown = TrendingDown protected readonly TrendingUp = TrendingUp protected readonly Users = Users protected readonly Wallet = Wallet protected readonly topCampaigns = topCampaigns @Input() set initialPeriod(v: string | undefined) { this.selectedPeriod.set(v ?? 'this-month') } @Input('class') className?: string readonly selectedPeriod = signal('this-month') readonly channels = signal<AdChannelPerformance[]>(initialChannels.map((c) => ({ ...c }))) get rootClass(): string { return cn('mx-auto w-full max-w-6xl space-y-6', this.className) } channelIconClass(channel: AdChannelPerformance): string { return cn('flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs', channel.iconClass) } spendShare(channel: AdChannelPerformance): string { const spendNum = parseFloat(channel.spend.replace(/[$,]/g, '')) return ((spendNum / 24850) * 100).toFixed(1) } roasBadgeClass(channel: AdChannelPerformance): string { return cn( 'text-xs font-bold tabular-nums', channel.roasStatus === 'high' && 'border-success/20 bg-success/10 text-success', channel.roasStatus === 'medium' && 'border-info/20 bg-info/10 text-info', channel.roasStatus === 'low' && 'border-warning/20 bg-warning/10 text-warning', ) } handleToggleChannel(id: string, active: boolean): void { this.channels.update((prev) => prev.map((ch) => (ch.id === id ? { ...ch, active } : ch))) } }src/app/components/blocks/ad-spend-roi-dashboard/index.ts0.2 kB
export { UiAdSpendRoiDashboardComponent, type AdChannelPerformance, type ChannelRoasStatus, type TopCampaign, } from './ad-spend-roi-dashboard.component'
Raw manifest:https://uipkge.dev/r/angular/ad-spend-roi-dashboard.json