feat(dashboards): add platform performance table#724
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis PR adds platform-level performance metrics end-to-end: server query and response shape, shared TypeScript contracts for performance categories and platform data, client-side mapping and aggregation into platform rows and a TOTAL row with ROAS bucketing, template rendering for a Platform performance table, and null-safe attribution revenue aggregation. ChangesPlatform Performance Metrics and Performance Marketing Tab
Attribution Section Null-Safety Improvements
Sequence Diagram(s)sequenceDiagram
participant Client as PerformanceMarketingTabComponent
participant Service as AnalyticsService
participant API as ProjectService.getSocialReach
participant Snowflake as Snowflake
Client->>Service: request social reach
Service->>API: getSocialReach(foundationSlug)
API->>Snowflake: platform-by-CHANNEL query (6 months)
Snowflake-->>API: platform rows (spend,revenue,clicks,impressions,conversions)
API->>API: compute CTR/CPC/convRate/ROAS
API->>API: bucket ROAS -> PaidProjectPerformance
API-->>Service: SocialReachResponse { platformBreakdown }
Service-->>Client: platformBreakdown delivered
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.ts (1)
24-29: ⚡ Quick winConsolidate to one UPPER_SNAKE_CASE revenue map constant.
These two static maps are identical and can drift; keep a single constant (e.g.,
REVENUE_KEY_BY_MODEL) and reference it fromgetRevenueKey.As per coding guidelines: Use UPPER_SNAKE_CASE for constants.♻️ Proposed cleanup
- private static readonly revenueKeyMap: Record<AttributionModel, 'linearRevenue' | 'firstTouchRevenue' | 'lastTouchRevenue' | 'timeDecayRevenue'> = { - linear: 'linearRevenue', - firstTouch: 'firstTouchRevenue', - lastTouch: 'lastTouchRevenue', - timeDecay: 'timeDecayRevenue', - }; + private static readonly REVENUE_KEY_BY_MODEL: Record<AttributionModel, 'linearRevenue' | 'firstTouchRevenue' | 'lastTouchRevenue' | 'timeDecayRevenue'> = { + linear: 'linearRevenue', + firstTouch: 'firstTouchRevenue', + lastTouch: 'lastTouchRevenue', + timeDecay: 'timeDecayRevenue', + }; @@ - private static readonly revenueKeyByModel: Record<AttributionModel, 'linearRevenue' | 'firstTouchRevenue' | 'lastTouchRevenue' | 'timeDecayRevenue'> = { - linear: 'linearRevenue', - firstTouch: 'firstTouchRevenue', - lastTouch: 'lastTouchRevenue', - timeDecay: 'timeDecayRevenue', - }; @@ - return AttributionSectionComponent.revenueKeyByModel[model]; + return AttributionSectionComponent.REVENUE_KEY_BY_MODEL[model];Also applies to: 47-52
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.ts` around lines 24 - 29, Replace the two identical static maps with a single UPPER_SNAKE_CASE constant and update callers to use it: create a single constant named REVENUE_KEY_BY_MODEL that maps AttributionModel -> 'linearRevenue'|'firstTouchRevenue'|'lastTouchRevenue'|'timeDecayRevenue', remove the duplicate static property revenueKeyMap (and the second identical map), and update getRevenueKey (and any other references) to read from REVENUE_KEY_BY_MODEL so both places use the same source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.ts`:
- Around line 24-29: Replace the two identical static maps with a single
UPPER_SNAKE_CASE constant and update callers to use it: create a single constant
named REVENUE_KEY_BY_MODEL that maps AttributionModel ->
'linearRevenue'|'firstTouchRevenue'|'lastTouchRevenue'|'timeDecayRevenue',
remove the duplicate static property revenueKeyMap (and the second identical
map), and update getRevenueKey (and any other references) to read from
REVENUE_KEY_BY_MODEL so both places use the same source of truth.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9525b52b-8cfb-4a90-a4ac-c6276fcab8a8
📒 Files selected for processing (16)
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.scssapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.tsapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/overview-tab/overview-tab.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/overview-tab/overview-tab.component.tsapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.scssapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.tsapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/sparkline-kpi-card/sparkline-kpi-card.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.tsapps/lfx-one/src/server/services/project.service.tspackages/shared/src/constants/marketing-impact.constants.tspackages/shared/src/interfaces/analytics-data.interface.tspackages/shared/src/interfaces/marketing-impact.interface.tspackages/shared/src/utils/marketing-impact.utils.ts
There was a problem hiding this comment.
Pull request overview
Adds new Marketing Impact dashboard functionality by introducing a Performance Marketing tab with a Platform Performance table (plus supporting shared interfaces/constants/utils) and a new Marketing attribution section used in both the Overview and Attribution tabs.
Changes:
- Extend Social Reach API response to include a platform-level paid performance breakdown and surface it in a new Performance Marketing UI tab (with totals row + horizontal scroll).
- Introduce a reusable Attribution section with model selector and channel revenue-share table, and embed it into the Overview tab.
- Add shared marketing-impact types/constants and trend formatting helpers used by KPI cards.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/utils/marketing-impact.utils.ts | Adds shared trend helper utilities for KPI trend direction/color + percent-change formatting. |
| packages/shared/src/interfaces/marketing-impact.interface.ts | Introduces new Marketing Impact view-model/types (attribution model, rows, funnel stage, performance labels). |
| packages/shared/src/interfaces/analytics-data.interface.ts | Extends SocialReachResponse with optional platformBreakdown and adds PaidPlatformBreakdown type. |
| packages/shared/src/constants/marketing-impact.constants.ts | Adds attribution model options and funnel stage filter options. |
| apps/lfx-one/src/server/services/project.service.ts | Adds Snowflake query for channel/platform breakdown and updates paid performance labels returned by the API. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.ts | Wires new Attribution + Performance Marketing tab components into the Marketing Impact page. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.html | Adds conditional rendering for Attribution and Performance Marketing tabs. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/sparkline-kpi-card/sparkline-kpi-card.component.html | Updates KPI card markup (icon wrapper + new testids). |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.ts | Implements Performance Marketing tab data fetching, KPI cards, project rows, platform rows, and totals computation. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.scss | Adds stylesheet file (currently header only). |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.html | Adds Performance Marketing tab UI including project table + platform performance table with total row. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/overview-tab/overview-tab.component.ts | Refactors overview KPI computation and embeds the Attribution section; uses shared trend helpers. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/overview-tab/overview-tab.component.html | Adds KPI empty state and renders Attribution section below overview KPI cards. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.ts | Adds Attribution section component with model selector and computed table rows. |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.scss | Adds stylesheet file (currently header only). |
| apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.html | Adds Attribution section UI with model selector, alert banner, skeleton, table, and empty state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Review Feedback AddressedResponses
Threads Resolved2 of 2 review threads addressed and resolved. |
3465454 to
f70e092
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/lfx-one/src/server/services/project.service.ts`:
- Line 2512: The function currently only attaches platformBreakdown on the happy
path, while an early short-circuit return (the pre-aggregation/empty-data path)
and the older return path still return the old shape; update all return paths in
the function that builds the response so they always include the new breakdown
arrays (platformBreakdown and the corresponding channelBreakdown/channel-level
array) even when monthlySeries or other data is missing. Locate the response
object assembly (where platformBreakdown was added and the earlier short-circuit
return occurs) and ensure both the early return and the fallback return include
empty arrays for platformBreakdown and channelBreakdown instead of omitting or
leaving them undefined.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3c07e02e-435b-4052-91f5-061fc598e8d1
📒 Files selected for processing (11)
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.tsapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.scssapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.tsapps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.tsapps/lfx-one/src/server/services/project.service.tspackages/shared/src/constants/marketing-impact.constants.tspackages/shared/src/interfaces/analytics-data.interface.tspackages/shared/src/interfaces/marketing-impact.interface.ts
✅ Files skipped from review due to trivial changes (1)
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.scss
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.html
- packages/shared/src/interfaces/analytics-data.interface.ts
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.ts
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/marketing-impact.component.ts
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.html
- packages/shared/src/constants/marketing-impact.constants.ts
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.ts
- performance-marketing-tab: remove dead @if guard, add normalizePerformance() to validate API boundary values - analytics-data.interface: narrow PaidPlatformBreakdown.performance to PaidProjectPerformance, collapse multi-line JSDoc - attribution-section: remove dead revenueKeyMap declaration LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
- Fix circular type dependency: move PaidProjectPerformance definition to analytics-data.interface.ts, re-export from marketing-impact - Eliminate duplicate Snowflake query: derive channelGroups from platformPerfResult instead of running a separate channelQuery - Fix ROAS/impressions MoM swap: assign changePercentage to ROAS card, compute impressions MoM from monthlyData via computeMomPct helper - Fix loading race condition: replace finalize with tap + catchError to prevent premature loading.set(false) on switchMap cancellation - Add projectBreakdown/platformBreakdown to early return and catch fallback paths in getSocialReach LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
Review Feedback AddressedCommit: 28b64ed Changes Made
Threads Resolved5 of 5 unresolved threads addressed in this iteration. |
- performance-marketing-tab: remove dead @if guard, add normalizePerformance() to validate API boundary values - analytics-data.interface: narrow PaidPlatformBreakdown.performance to PaidProjectPerformance, collapse multi-line JSDoc - attribution-section: remove dead revenueKeyMap declaration LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
- Fix circular type dependency: move PaidProjectPerformance definition to analytics-data.interface.ts, re-export from marketing-impact - Eliminate duplicate Snowflake query: derive channelGroups from platformPerfResult instead of running a separate channelQuery - Fix ROAS/impressions MoM swap: assign changePercentage to ROAS card, compute impressions MoM from monthlyData via computeMomPct helper - Fix loading race condition: replace finalize with tap + catchError to prevent premature loading.set(false) on switchMap cancellation - Add projectBreakdown/platformBreakdown to early return and catch fallback paths in getSocialReach LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
28b64ed to
1570106
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.html`:
- Around line 127-140: The TOTAL row leaves the PERFORMANCE cell empty; use the
values provided by platformTotals() to render the performance badge: in the
template block that uses `@if` (platformTotals(); as totals) replace the empty
<div ... role="cell"></div> with markup that displays totals.performance and
applies totals.performanceClass (or equivalent CSS class) so the badge appears
for totals just like for individual rows; ensure you reference
totals.performance and totals.performanceClass when building the badge element
inside the row for the PERFORMANCE column.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 171644cf-f35f-4c7f-aa50-b9ca12667c59
📒 Files selected for processing (6)
apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.tsapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.htmlapps/lfx-one/src/app/modules/dashboards/marketing-impact/components/performance-marketing-tab/performance-marketing-tab.component.tsapps/lfx-one/src/server/services/project.service.tspackages/shared/src/interfaces/analytics-data.interface.tspackages/shared/src/interfaces/marketing-impact.interface.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/lfx-one/src/server/services/project.service.ts
- apps/lfx-one/src/app/modules/dashboards/marketing-impact/components/attribution-section/attribution-section.component.ts
Add platform-level performance breakdown table aggregating paid campaign data by ad channel (Google Ads, LinkedIn Ads, etc.) with spend, revenue, ROAS, clicks, impressions, CTR, CPC, conversion rate, and conversions. Includes TOTAL footer row, horizontal scroll for narrow viewports, and neutral performance labels (EXCELLENT, GOOD, AVERAGE, EMERGING) to avoid premature red flags while data quality is being validated. LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
- performance-marketing-tab: remove dead @if guard, add normalizePerformance() to validate API boundary values - analytics-data.interface: narrow PaidPlatformBreakdown.performance to PaidProjectPerformance, collapse multi-line JSDoc - attribution-section: remove dead revenueKeyMap declaration LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
- Fix circular type dependency: move PaidProjectPerformance definition to analytics-data.interface.ts, re-export from marketing-impact - Eliminate duplicate Snowflake query: derive channelGroups from platformPerfResult instead of running a separate channelQuery - Fix ROAS/impressions MoM swap: assign changePercentage to ROAS card, compute impressions MoM from monthlyData via computeMomPct helper - Fix loading race condition: replace finalize with tap + catchError to prevent premature loading.set(false) on switchMap cancellation - Add projectBreakdown/platformBreakdown to early return and catch fallback paths in getSocialReach LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
2fbe027 to
4129052
Compare
- Use type-only import for PaidProjectPerformance - Add ROAS_RAW column for unrounded classification - Deduplicate normalizePerformance/getRoasPerformance calls LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
Use the same rounded ROAS value for both display and performance classification so the label always matches the visible number. Remove duplicate PaidProjectPerformance re-export. LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
Align platform breakdown and totals ROAS formatting with the KPI cards and project rows by appending the x suffix. Add blank line after import in analytics-data interface. LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
apps/lfx-one/src/server/services/project.service.ts:2265
- The CTR/CPC/CONV_RATE expressions combine
DIV0(...)withNULLIF(..., 0).DIV0already guards divide-by-zero (returns 0 when denominator is 0), butNULLIFturns 0 into NULL, which makesDIV0return NULL instead. Consider dropping theNULLIFwrappers (or droppingDIV0if you intentionally want NULL) to keep the SQL semantics clear and avoid unexpected NULLs in the result set.
ROUND(DIV0(SUM(CLICKS), NULLIF(SUM(IMPRESSIONS), 0)) * 100, 2) AS CTR,
ROUND(DIV0(SUM(SPEND), NULLIF(SUM(CLICKS), 0)), 2) AS CPC,
ROUND(DIV0(SUM(CONV), NULLIF(SUM(CLICKS), 0)) * 100, 2) AS CONV_RATE,
apps/lfx-one/src/server/services/project.service.ts:2386
platformPerfQueryis treated as optional (caught and converted torows: []), butchannelGroupsis now derived exclusively fromplatformPerfResult.rows. This means a platform query failure will silently wipe out the impressions-by-channel data too (regression vs having a dedicated channel query). Consider either makingplatformPerfQuerynon-optional so failures surface, or adding a lightweight fallback query just forchannelGroupswhen the platform breakdown query fails.
const channelGroups = platformPerfResult.rows
.map((row) => ({
channel: row.CHANNEL,
totalImpressions: row.IMPRESSIONS,
}))
.sort((a, b) => b.totalImpressions - a.totalImpressions);
|
Hey Misha — thanks for the fast turnaround on the review. I went through each item against the latest commit (8636f1b) and everything lands cleanly. 👏 👏 Nice work
✅ Resolved
|
dealako
left a comment
There was a problem hiding this comment.
Approved. See my comment ablve.
Add subtitle to platform performance table noting revenue/ROAS use linear attribution. Add backend comment documenting the intentional attribution model split between KPI headlines (first-touch) and drill-down tables (linear). LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
Unify all social reach queries on LAST_TOUCH_REVENUE from the PAID_SOCIAL_REACH_BY_PROJECT_CHANNEL_MONTH table. This eliminates the attribution model mismatch between KPI headlines (was first-touch) and drill-down tables (was linear). ROAS MoM is now computed from two completed months instead of the pre-computed ROAS_MOM_PCT column. LFXV2-1644 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Misha Rautela <mrautela@linuxfoundation.org>
| const [impressionsResult, roasKpiResult, monthlyRoasResult, monthlyImpressionsResult, projectPerfResult, platformPerfResult] = await Promise.all([ | ||
| this.snowflakeService.execute<{ TOTAL_IMPRESSIONS: number; TOTAL_SPEND: number; TOTAL_REVENUE: number }>(impressionsQuery, [foundationSlug]), | ||
| this.snowflakeService.execute<{ ROAS: number; ROAS_MOM_PCT: number }>(roasKpiQuery, [foundationSlug]), | ||
| this.snowflakeService.execute<{ CAMPAIGN_MONTH: string; ROAS: number }>(roasKpiQuery, [foundationSlug, foundationSlug]), |
| this.snowflakeService | ||
| .execute<{ | ||
| CHANNEL: string; | ||
| SPEND: number; | ||
| REVENUE: number; | ||
| ROAS: number; | ||
| CLICKS: number; | ||
| IMPRESSIONS: number; | ||
| CTR: number; | ||
| CPC: number; | ||
| CONV_RATE: number; | ||
| CONVERSIONS: number; | ||
| }>(platformPerfQuery, [foundationSlug]) | ||
| .catch((error) => { | ||
| logger.warning(undefined, 'get_social_reach', 'Optional platform breakdown query failed, degrading gracefully', { | ||
| foundation_slug: foundationSlug, | ||
| err: error, | ||
| }); | ||
| return { | ||
| rows: [] as { | ||
| CHANNEL: string; | ||
| SPEND: number; | ||
| REVENUE: number; | ||
| ROAS: number; | ||
| CLICKS: number; | ||
| IMPRESSIONS: number; | ||
| CTR: number; | ||
| CPC: number; | ||
| CONV_RATE: number; | ||
| CONVERSIONS: number; | ||
| }[], | ||
| }; | ||
| }), |
Summary
PAID_SOCIAL_REACH_BY_PROJECT_CHANNEL_MONTHby CHANNEL with spend, revenue, ROAS, clicks, impressions, CTR, CPC, conversion rate, and conversionsPaidPlatformBreakdowninterface (backend) andPlatformPerformanceRowview-model (frontend)Test plan
/foundation/marketing-impact?project=tlf→ Performance Marketing tabLFXV2-1644
🤖 Generated with Claude Code