Project: Sales Pipeline Dashboard
By the end of this lesson, you'll be able to:
- Aggregate Opportunity data by stage using an Apex aggregate query
- Render grouped totals in a responsive SLDS grid layout
- Recognize why this kind of aggregation genuinely requires Apex, not LDS
Prerequisites: "Project: Case Management Workspace"
What We're Building
A salesPipelineDashboard component showing each Opportunity stage as a column with its total value and count — Module 7 Lesson 1's "aggregation across many records" signal, now actually built.
The Apex Layer
public inherited sharing class PipelineSelector {
public static List<AggregateResult> stageTotals() {
return [
SELECT StageName stage, COUNT(Id) opportunityCount, SUM(Amount) totalAmount
FROM Opportunity
WHERE IsClosed = false
WITH SECURITY_ENFORCED
GROUP BY StageName
];
}
}
public inherited sharing class PipelineService {
public static List<StageSummary> getStageSummaries() {
List<StageSummary> summaries = new List<StageSummary>();
for (AggregateResult ar : PipelineSelector.stageTotals()) {
summaries.add(new StageSummary(
(String) ar.get('stage'),
(Integer) ar.get('opportunityCount'),
(Decimal) ar.get('totalAmount')
));
}
return summaries;
}
}
public with sharing class PipelineController {
@AuraEnabled(cacheable=true)
public static List<StageSummary> getStageSummaries() {
return PipelineService.getStageSummaries();
}
}
GROUP BY StageName with SUM/COUNT is precisely the aggregation LDS's simple record-read model can't express — the Selector (Module 16) isolates this specific aggregate query construction.
The Component
<div class="slds-grid slds-wrap slds-gutters">
<template for:each={stages} for:item="stage">
<div key={stage.stageName} class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
<div class="slds-box">
<h3>{stage.stageName}</h3>
<p>{stage.formattedTotal} — {stage.opportunityCount} deals</p>
</div>
</div>
</template>
</div>
The responsive grid (Module 9) stacks to one column on mobile and three on desktop; formattedTotal would apply the Intl.NumberFormat currency pattern from Module 11's internationalization lesson.
Exercise
Explain, as a comment, why GROUP BY StageName with SUM(Amount) cannot be expressed through Lightning Data Service.
Show hint
Recall Module 6/7's discussion of LDS's limits.
Exercise
Challenge: why is getStageSummaries marked cacheable=true rather than called imperatively?
Show hint
Recall Module 7's decision table.
Project: Sales Pipeline Dashboard Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
A pipeline dashboard grouping Opportunities by stage with aggregate totals — exactly the kind of cross-record aggregation Module 7 identified as a genuine signal that Apex, not LDS, is required.