🏠 Home 🔒 Record Sharing ⚙ Apex Triggers 🔍 SOQL 💻 LWC 🔗 Integration 🤖 Flows & Automation 🤖 Agentforce & AI 🎈 Agentforce Course — Free ☁ Data Cloud 🎓 DC Course — Free 🚀 DevOps Course — Free 💵 CPQ 🎯 100 Scenario Questions 🏆 150 Advanced Questions 📧 Marketing Cloud 🎤 Mock Interview Community 🏗️ Company Wise 👥 About Us Start Learning Free →

LWC Zero to Hero Module 15 — Capstone Project (XYZ Company Order Dashboard)

LWC Zero to Hero - Module 15: Capstone Project | SF Interview Pro
🏆 LWC Zero to Hero — Module 15 of 15 — CAPSTONE

The Capstone Project — XYZ Company
Order Management Dashboard

Every module, every pattern, every gotcha from this entire course, brought together into one complete, production-style application. This is where it all clicks into place.

Module 15 of 15 (plus Module 0 bonus) · Phase 5: Advanced and Capstone · FINAL MODULE
📋 The Capstone Brief
XYZ Company needs a real, working Order Management Dashboard for their Account record pages. Sales reps need to see all Orders related to the current Account, filter and search them, view full details in a modal without leaving the page, update an Order's status, get a clear success or error toast for every action, and trust that the dashboard performs well and stays correct as the codebase evolves. This is not a toy example. This is the kind of feature you will genuinely build on the job, and every single piece of it traces back to a specific module in this course.
A complete component architecture, planned before any code is written
A reactive display layer built on @wire, proper templates, and clean reactivity
An interactive layer with filtering, modals, toasts, and parent-child events
A robust data layer with imperative Apex, error handling, and refreshApex
Performance, testing, and security considerations applied for real
A genuine deployment of the finished feature
A complete look back at everything this course covered, end to end
Phase 1 of 7
The Brief & Project Scope
Before writing a single line of code, let us be precise about scope, exactly the discipline a real project demands. The XYZ Company Order Management Dashboard needs four user-facing capabilities: viewing a filterable, searchable list of Orders for the current Account, viewing full Order details in a modal, updating an Order's status with proper confirmation and feedback, and staying fast and reliable even with hundreds of Orders on a single Account.
📚 Setting the Foundation
This phase is planning, not coding, but it directly uses the analytical thinking from Module 12, Concept 1, asking "what does the user actually need right now" before writing anything, and Module 7, Concept 7's decision-framework habit of reasoning through trade-offs before committing to an approach.
🛠️ Why Scope Discipline Matters
A genuinely common real-world mistake is starting to code before scope is clear, then discovering halfway through that the component architecture does not actually support a requirement that surfaces late. Writing the scope down first, even briefly, is what lets Phase 2's architecture decisions be made deliberately rather than reactively.
⚠️ A Note Before We Begin
This capstone deliberately reuses patterns from nearly every module rather than introducing new syntax. If any code below feels unfamiliar, that is a signal to revisit the specific module referenced in that section's "Modules Used" callout before continuing, rather than pushing through confusion.
Phase 2 of 7
Architecture & Component Breakdown
A single, giant component handling everything would violate the clean component API design principles from Module 5, Concept 7. Instead, we break this into a small tree of focused components, each with a clear single responsibility, communicating through the sanctioned patterns from Modules 5 through 7.
c-order-dashboard (orchestrator, recordId from Account page) ├── c-order-filter-panel (search input + status filter, fires events UP) ├── c-order-list (renders rows, receives filtered data DOWN) │ └── c-order-row (one Order, fires deleteclick/statusclick events) └── c-confirm-action-modal (LightningModal, used for delete/status confirmation)
📚 Modules Used in This Phase
Module 2 (component anatomy, deciding where each component's boundary lives), Module 5 Concept 7 (designing clean component APIs, deciding what each component exposes via @api), and Module 6 Concept 1 (the two-way communication picture: data flows down via properties, actions flow up via events).
🛠️ Why This Specific Breakdown
c-order-dashboard is the only component that talks to Apex directly, exactly the kind of intentional design decision Module 5's clean API guidance encourages. c-order-filter-panel and c-order-row stay simple and reusable, knowing nothing about Apex or data fetching at all, only about reporting user intent upward through events.
⚠️ A Design Decision Worth Defending
An interviewer might reasonably ask "why not let c-order-row call the delete Apex method itself, since it knows the Order's Id." The answer connects directly to Module 5's design principles: keeping data-fetching logic centralized in the orchestrator component makes the data layer easier to reason about, test, and later modify, rather than scattering Apex calls across many small, individually simple components.
Phase 3 of 7
Building the Display Layer
We start with c-order-dashboard fetching and displaying Orders, using the exact wired pattern from Module 9 and Module 11, combined with Module 3's loading and empty-state template handling.
orderDashboard.js — the orchestrator's data layer
import { LightningElement, api, wire } from 'lwc';
import { refreshApex } from '@salesforce/apex';
import getOrdersForAccount from '@salesforce/apex/OrderController.getOrdersForAccount';

export default class OrderDashboard extends LightningElement {
  @api recordId;        // Module 5 — set automatically on an Account record page
  searchTerm = '';       // Module 4 — plain reactive property
  statusFilter = 'All';
  wiredOrdersResult;     // Module 11 — stored for refreshApex

  @wire(getOrdersForAccount, { accountId: '$recordId' })  // Module 9 — reactive $ syntax
  wiredOrders(result) {
    this.wiredOrdersResult = result;
  }

  get filteredOrders() {                       // Module 9 Concept 3 + Module 4
    const orders = this.wiredOrdersResult?.data ?? [];
    return orders
      .filter(o => this.statusFilter === 'All' || o.Status__c === this.statusFilter)
      .filter(o => o.Name.toLowerCase().includes(this.searchTerm.toLowerCase()));
  }

  get isLoading() {                            // Module 3 — loading/empty/populated pattern
    return !this.wiredOrdersResult?.data && !this.wiredOrdersResult?.error;
  }
}
orderDashboard.html — Module 3's full pattern, end to end
<template>
  <c-order-filter-panel onfilterchanged={handleFilterChanged}></c-order-filter-panel>

  <template lwc:if={isLoading}>
    <lightning-spinner alternative-text="Loading Orders"></lightning-spinner>
  </template>
  <template lwc:elseif={filteredOrders.length}>
    <c-order-list orders={filteredOrders} onorderaction={handleOrderAction}></c-order-list>
  </template>
  <template lwc:else>
    <p>No matching Orders found.</p>
  </template>
</template>
📚 Modules Used in This Phase
Module 3 (lwc:if/lwc:elseif/lwc:else loading pattern), Module 4 (reactive plain properties and getters), Module 9 (wire fundamentals and reactive $recordId), and Module 11 (storing the full wire result for later refreshApex use).
🛠️ Notice How Naturally This Reads Now
If this code feels straightforward rather than overwhelming, that is the entire point of building this course module by module. Every single piece here was taught in isolation earlier; the capstone's only real new skill is recognizing how they combine.
⚠️ A Subtle Detail Worth Noticing
filteredOrders is a getter doing real filtering work on every render, exactly the pattern Module 12, Concept 2 flagged as a potential performance concern. For a typical Account with dozens of Orders this is genuinely fine; if XYZ Company later reports performance issues with Accounts holding thousands of Orders, this getter is precisely where Module 12's setter-based optimization would be applied first.
Phase 4 of 7
Building the Interactive Layer
Now we wire up genuine user interaction: the filter panel reporting changes upward, each Order row reporting clicked actions upward, and a confirmation modal protecting the destructive delete action, using the exact patterns from Modules 5, 6, and 8.
orderFilterPanel.js — fires an event up, never touches data directly
export default class OrderFilterPanel extends LightningElement {
  handleSearchInput(event) {
    this.dispatchEvent(new CustomEvent('filterchanged', {     // Module 6
      detail: { searchTerm: event.target.value, type: 'search' }
    }));
  }
  handleStatusChange(event) {
    this.dispatchEvent(new CustomEvent('filterchanged', {
      detail: { status: event.target.value, type: 'status' }
    }));
  }
}
orderRow.js — @api in, events out, exactly Module 6's two-way picture
export default class OrderRow extends LightningElement {
  @api order;  // Module 5 — data flows DOWN

  handleDeleteClick() {
    this.dispatchEvent(new CustomEvent('orderaction', {   // Module 6 — action flows UP
      detail: { action: 'delete', orderId: this.order.Id }
    }));
  }
}
orderDashboard.js — handling the action, with Module 8's full modal flow
import ConfirmActionModal from 'c/confirmActionModal';     // Module 8
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

async handleOrderAction(event) {
  const { action, orderId } = event.detail;     // Module 1 destructuring, Module 6 detail payload

  if (action === 'delete') {
    const result = await ConfirmActionModal.open({
      size: 'small', message: 'Delete this Order? This cannot be undone.'
    });
    if (result !== 'confirmed') return;   // Module 8's exact early-return gotcha
    await this.deleteOrder(orderId);
  }
}
📚 Modules Used in This Phase
Module 5 (data flowing down via @api), Module 6 (events flowing up, the detail payload, the two-way communication picture), and Module 8 (the LightningModal API, the critical early-return-on-cancel pattern, and ShowToastEvent feedback).
🛠️ This Is the Exact Combined Pattern from Module 10
If handleOrderAction looks familiar, it should. This is precisely the modal-then-imperative-call pattern built fully in Module 10, Concept 6, just now living inside a real, larger application instead of an isolated example.
⚠️ Why c-order-row Stays Deliberately Simple
Notice c-order-row has no idea what "delete" actually does, no Apex import, no modal logic. It only reports that a delete action was requested, for a specific orderId. This is the Module 5 clean-API and Module 7 anti-pattern guidance in practice: keep individual components simple and let the orchestrator handle complexity, rather than scattering business logic across every small component.
Phase 5 of 7
Building the Data Layer
Now the part that actually changes data: deleting an Order imperatively, handling both success and failure robustly, and keeping the wired list in sync afterward, combining Module 10's full error handling with Module 11's refresh pattern.
orderDashboard.js — the complete deleteOrder method
import deleteOrder from '@salesforce/apex/OrderController.deleteOrder';

async deleteOrder(orderId) {
  this.isProcessing = true;                              // Module 10 — disable triggering UI
  try {
    await deleteOrder({ orderId });                          // Module 10 — imperative call
    await refreshApex(this.wiredOrdersResult);                 // Module 11 — close the staleness gap
    this.dispatchEvent(new ShowToastEvent({
      title: 'Order Deleted', variant: 'success'
    }));
  } catch (error) {
    this.dispatchEvent(new ShowToastEvent({                     // Module 10 — error.body.message
      title: 'Delete Failed',
      message: error.body?.message ?? 'An unexpected error occurred',
      variant: 'error', mode: 'sticky'
    }));
  } finally {
    this.isProcessing = false;                             // Module 10 — finally, always resets
  }
}
OrderController.cls — the Apex side, with proper custom error handling
public class OrderController {
  @AuraEnabled(cacheable=true)                              // Module 9 — required for @wire
  public static List<Order__c> getOrdersForAccount(Id accountId) {
    return [SELECT Id, Name, Status__c, Amount__c FROM Order__c
            WHERE Account__c = :accountId];
  }

  @AuraEnabled                                                  // Module 10 — NOT cacheable, this has side effects
  public static void deleteOrder(Id orderId) {
    try {
      delete [SELECT Id FROM Order__c WHERE Id = :orderId];
    } catch (Exception e) {
      AuraHandledException ex = new AuraHandledException(e.getMessage());  // Module 10
      ex.setMessage('Unable to delete this Order. It may be referenced elsewhere.');
      throw ex;
    }
  }
}
📚 Modules Used in This Phase
Module 9 (cacheable=true on the read method), Module 10 (the full imperative try/catch/finally pattern, AuraHandledException, the deliberate absence of cacheable on the write method), and Module 11 (refreshApex closing the exact staleness gap that Module 10's Concept 5 originally flagged).
🛠️ The Single Most Important Line in This Whole Capstone
await refreshApex(this.wiredOrdersResult) is genuinely the line that ties Modules 9, 10, and 11 together into one coherent story. Without it, the user would delete an Order and still see it sitting in the list, the exact bug Module 11 was built specifically to prevent.
⚠️ Notice What Is Deliberately Different Between the Two Apex Methods
getOrdersForAccount has cacheable=true; deleteOrder does not. This is not an oversight, it is Module 10, Concept 1's exact lesson: methods with side effects should never be marked cacheable, while methods purely reading data for wire usage must be.
Phase 6 of 7
Performance, Testing & Security
A working feature is not the same as a production-ready one. This phase applies Modules 12, 13, and 14 directly to the dashboard we just built, exactly the production-readiness layer that separates a demo from something XYZ Company can actually ship.
A real Jest test for the delete flow — Module 13's complete pattern
import { createElement } from 'lwc';
import OrderDashboard from 'c/orderDashboard';
import deleteOrder from '@salesforce/apex/OrderController.deleteOrder';

jest.mock('@salesforce/apex/OrderController.deleteOrder',
  () => ({ default: jest.fn() }), { virtual: true });

it('shows a success toast after a confirmed delete', async () => {
  deleteOrder.mockResolvedValue();                       // Module 13 — mocking success
  const element = createElement('c-order-dashboard', { is: OrderDashboard });
  document.body.appendChild(element);

  const handler = jest.fn();
  element.addEventListener('lightning__showtoast', handler);  // listening for ShowToastEvent

  // ...simulate the row's delete click and modal confirmation here...
  await Promise.resolve();                                  // Module 13 — wait for the re-render

  expect(handler).toHaveBeenCalled();                          // Module 13 — assert observable behavior
});
Applying Module 12 and Module 14 — what to check before shipping
// PERFORMANCE (Module 12): if XYZ Company Accounts can have 1000+ Orders,
// add pagination to c-order-list rather than rendering every row at once,
// and confirm Order__c records use stable Id-based keys, never array indexes.

// SECURITY (Module 14): no raw DOM manipulation reaching outside this
// component's own shadowRoot, no third-party libraries imported without
// checking LWS compatibility first.
📚 Modules Used in This Phase
Module 13 (the complete create-attach-inspect test pattern, mocking imperative Apex with jest.mock and virtual:true, asserting on observable behavior rather than implementation), Module 12 (pagination for large data volumes, stable keys), and Module 14 (LWS-aware coding habits before deployment).
🛠️ Testing the Behavior, Not the Wiring
Notice the test above checks that a toast event fired, the OBSERVABLE behavior a user experiences, rather than checking some internal implementation detail of how the toast was constructed. This is Module 13, Concept 7's exact guidance, applied to a real scenario rather than an isolated example.
⚠️ This Phase Has No New Syntax, Deliberately
Every technique in this phase was already fully taught in Modules 12 through 14. The only genuinely new skill the Capstone teaches here is recognizing WHEN to apply each one to a real feature you have just built, rather than to an isolated, disconnected example.
Phase 7 of 7
Deployment & The Complete Journey
The final step: getting this finished, tested feature out of your local project and into a real, working Salesforce org, using Module 14's complete CLI workflow.
The actual deployment of everything built in this Capstone
# Run the full test suite first — never deploy untested code (Module 13 + 14)
npm run test:unit

# Deploy every component and Apex class built across this Capstone
sf project deploy start --target-org myDevOrg

# Add c-order-dashboard to an Account record page via Lightning App Builder,
# then verify it genuinely works end to end, with real data, in the real org
🛠️ What You Have Actually Built
Five components, one Apex controller, a working test suite, and a genuinely deployed, working feature on a real Account record page. Every line traces back to a specific lesson from this course. This is not a simplified example anymore. This is the real shape of professional LWC development.
⚠️ One Last, Honest Note
A real XYZ Company project would also need proper permission checks (Module 14's security mindset extended further), bulkification considerations for the Apex SOQL and DML, and likely additional edge-case handling this Capstone deliberately kept focused rather than exhaustive. Treat this as a strong, complete foundation, and the natural next step in your real growth as a Salesforce developer.
🏆
Course Complete — LWC Zero to Hero!
From absolute programming basics in Module 0, through modern JavaScript, every core LWC building block, component communication, data integration, and finally production-readiness, all the way to this real, deployed Capstone feature. Fifteen modules, one continuous thread connecting every single one. You did not just learn syntax. You built an actual mental model for how production LWC development genuinely works.
Keep building. Keep shipping. And go crack that interview. 🚀
Practice LWC Interview Questions → Explore More Free Courses →
🎯 Your Final Challenge
Do not just read this Capstone. Build it. Every component, the Apex controller, the test suite, and a real deployment to a Developer Edition org. Then extend it: add a status-update flow alongside delete, following the exact same combined pattern. That extension, built entirely on your own using only what this course taught you, is the real proof you have genuinely mastered Lightning Web Components.
☕ Enjoyed this article?
SF Interview Pro is 100% free and maintained by a Salesforce professional. No ads, no paywalls, and no signup required. If this guide helped you prepare for an interview, earn a certification, or grow your Salesforce career, consider buying me a coffee! ☕💜
🇮🇳 UPI (India)
UPI QR Code to support sfinterviewpro
Pay by QR
GPay · PhonePe · Paytm · BHIM
🌎 International
PayPal QR Code to support sfinterviewpro
Scan or tap to pay