EscalationEscalationsEscalation__cEscalation NameText Field 1: Escalation_Reason__c
- Type: Picklist
- Values: Critical Priority, High Complexity, Customer VIP, Technical Issue, Management Request
- Required: Yes
Field 2: Escalated_By__c
- Type: Lookup (User)
- Required: Yes
Field 3: Case__c
- Type: Master-Detail (Case)
- Required: Yes
Field 4: Escalation_Date__c
- Type: Date/Time
- Required: Yes
- Default: NOW()
Field 5: Escalation_Notes__c
- Type: Long Text Area
- Length: 32,768Resolution FeedbackResolution FeedbackResolution_Feedback__cFeedback NameAuto NumberFB-{00000} Field 1: Case__c
- Type: Lookup (Case)
- Required: Yes
Field 2: Rating__c
- Type: Number (1,0)
- Min: 1, Max: 5
- Required: Yes
Field 3: Feedback_Comments__c
- Type: Long Text Area
- Length: 32,768
Field 4: Feedback_Date__c
- Type: Date/Time
- Default: NOW()
Field 5: Is_Submitted__c
- Type: Checkbox
- Default: False
Field 6: Contact__c
- Type: Lookup (Contact)
- Required: Yes Field 1: Escalation_Status__c
- Type: Picklist
- Values: Not Escalated, Escalated, Resolved
- Default: Not Escalated
- Required: Yes
Field 2: Severity_Level__c
- Type: Picklist
- Values: Low, Medium, High, Critical
- Required: Yes
Field 3: Final_Rating__c
- Type: Number (2,1)
- Min: 1.0, Max: 5.0CaseA record is created or updated{!$Record.Severity_Level__c} Equals Critical{!$Record.Escalation_Status__c} Not Equal EscalatedActions and Related RecordsElement 1: Get Records - Check Existing Escalation
Get_Existing_EscalationEscalation__cCase__c Equals {!$Record.Id}varExistingEscalationElement 2: Decision - Escalation Exists
Decision_Escalation_ExistsNo_Existing_Escalation
{!Get_Existing_Escalation} Is Null TrueElement 3: Create Records - New Escalation
Create_Escalation_RecordEscalation__cCase__c: {!$Record.Id}Escalated_By__c: {!$User.Id}Escalation_Reason__c: Critical PriorityEscalation_Date__c: {!$Flow.CurrentDateTime}Escalation_Notes__c: Auto-escalated due to Critical severity levelElement 4: Update Records - Update Case Status
Update_Case_Escalation_Status{!$Record}Escalation_Status__c: EscalatedElement 5: Send Email - Alert Management
Send_Escalation_AlertCritical Case Escalated - {!$Record.CaseNumber}Create_Resolution_FeedbackVariable 1: varCaseId
- Type: Text
- Available for input: Yes
Variable 2: varContactId
- Type: Text
- Available for input: YesElement 1: Create Records - Feedback Record
Create_Feedback_RecordResolution_Feedback__cCase__c: {!varCaseId}Contact__c: {!varContactId}Is_Submitted__c: FalseFeedback_Date__c: {!$Flow.CurrentDateTime}Element 2: Assignment - Store Record ID
Store_Feedback_IDvarFeedbackId (create new){!Create_Feedback_Record}public class CaseFeedbackInvoker {
@future
public static void createFeedbackRecords(Set<Id> caseIds) {
try {
List<Case> closedCases = [
SELECT Id, ContactId, CaseNumber
FROM Case
WHERE Id IN :caseIds
AND ContactId != null
AND Status = 'Closed'
];
List<Resolution_Feedback__c> feedbackRecords = new List<Resolution_Feedback__c>();
for (Case c : closedCases) {
// Check if feedback already exists
List<Resolution_Feedback__c> existingFeedback = [
SELECT Id FROM Resolution_Feedback__c
WHERE Case__c = :c.Id LIMIT 1
];
if (existingFeedback.isEmpty()) {
Resolution_Feedback__c feedback = new Resolution_Feedback__c(
Case__c = c.Id,
Contact__c = c.ContactId,
Is_Submitted__c = false,
Feedback_Date__c = System.now()
);
feedbackRecords.add(feedback);
}
}
if (!feedbackRecords.isEmpty()) {
insert feedbackRecords;
System.debug('Created ' + feedbackRecords.size() + ' feedback records');
}
} catch (Exception e) {
System.debug('Error in createFeedbackRecords: ' + e.getMessage());
// Log error or send notification
}
}
// Alternative Queueable implementation
public class FeedbackQueueable implements Queueable {
private Set<Id> caseIds;
public FeedbackQueueable(Set<Id> caseIds) {
this.caseIds = caseIds;
}
public void execute(QueueableContext context) {
createFeedbackRecordsSync(this.caseIds);
}
}
public static void createFeedbackRecordsSync(Set<Id> caseIds) {
// Same logic as future method but synchronous
// Use for queueable execution
}
}trigger CaseTrigger on Case (after update) {
if (Trigger.isAfter && Trigger.isUpdate) {
Set<Id> closedCaseIds = new Set<Id>();
for (Case newCase : Trigger.new) {
Case oldCase = Trigger.oldMap.get(newCase.Id);
// Check if status changed to Closed
if (newCase.Status == 'Closed' &&
oldCase.Status != 'Closed' &&
newCase.ContactId != null) {
closedCaseIds.add(newCase.Id);
}
}
if (!closedCaseIds.isEmpty()) {
// Use future method for async processing
CaseFeedbackInvoker.createFeedbackRecords(closedCaseIds);
}
}
}public with sharing class CaseFeedbackController {
@AuraEnabled(cacheable=true)
public static List<Resolution_Feedback__c> getFeedbackRecords(Id contactId) {
try {
return [
SELECT Id, Case__c, Case__r.CaseNumber, Case__r.Subject,
Rating__c, Feedback_Comments__c, Is_Submitted__c,
Feedback_Date__c
FROM Resolution_Feedback__c
WHERE Contact__c = :contactId
AND Is_Submitted__c = false
ORDER BY Feedback_Date__c DESC
];
} catch (Exception e) {
throw new AuraHandledException('Error fetching feedback records: ' + e.getMessage());
}
}
@AuraEnabled
public static String submitFeedback(Id feedbackId, Decimal rating, String comments) {
try {
Resolution_Feedback__c feedback = new Resolution_Feedback__c(
Id = feedbackId,
Rating__c = rating,
Feedback_Comments__c = comments,
Is_Submitted__c = true
);
update feedback;
// Update Case Final Rating
updateCaseFinalRating(feedbackId);
return 'Success';
} catch (Exception e) {
throw new AuraHandledException('Error submitting feedback: ' + e.getMessage());
}
}
private static void updateCaseFinalRating(Id feedbackId) {
Resolution_Feedback__c feedback = [
SELECT Case__c, Rating__c
FROM Resolution_Feedback__c
WHERE Id = :feedbackId
];
Case caseToUpdate = new Case(
Id = feedback.Case__c,
Final_Rating__c = feedback.Rating__c
);
update caseToUpdate;
}
}@isTest
public class CaseFeedbackInvokerTest {
@testSetup
static void setupTestData() {
// Create test account
Account testAccount = new Account(Name = 'Test Account');
insert testAccount;
// Create test contact
Contact testContact = new Contact(
FirstName = 'Test',
LastName = 'Contact',
Email = 'test@example.com',
AccountId = testAccount.Id
);
insert testContact;
// Create test cases
List<Case> testCases = new List<Case>();
for (Integer i = 0; i < 5; i++) {
testCases.add(new Case(
Subject = 'Test Case ' + i,
Status = 'New',
ContactId = testContact.Id,
Severity_Level__c = 'Medium',
Escalation_Status__c = 'Not Escalated'
));
}
insert testCases;
}
@isTest
static void testCreateFeedbackRecords() {
List<Case> testCases = [SELECT Id FROM Case];
Set<Id> caseIds = new Set<Id>();
// Close the cases
for (Case c : testCases) {
c.Status = 'Closed';
caseIds.add(c.Id);
}
update testCases;
Test.startTest();
CaseFeedbackInvoker.createFeedbackRecords(caseIds);
Test.stopTest();
// Verify feedback records were created
List<Resolution_Feedback__c> feedbackRecords = [
SELECT Id, Case__c FROM Resolution_Feedback__c
];
System.assertEquals(5, feedbackRecords.size(), 'Should create 5 feedback records');
}
@isTest
static void testQueueableImplementation() {
List<Case> testCases = [SELECT Id FROM Case LIMIT 1];
Set<Id> caseIds = new Set<Id>{testCases[0].Id};
// Close the case
testCases[0].Status = 'Closed';
update testCases[0];
Test.startTest();
System.enqueueJob(new CaseFeedbackInvoker.FeedbackQueueable(caseIds));
Test.stopTest();
// Verify feedback record was created
List<Resolution_Feedback__c> feedbackRecords = [
SELECT Id FROM Resolution_Feedback__c
];
System.assertEquals(1, feedbackRecords.size(), 'Should create 1 feedback record');
}
}@isTest
public class CaseTriggerTest {
@testSetup
static void setupTestData() {
Account testAccount = new Account(Name = 'Test Account');
insert testAccount;
Contact testContact = new Contact(
FirstName = 'Test',
LastName = 'Contact',
Email = 'test@example.com',
AccountId = testAccount.Id
);
insert testContact;
Case testCase = new Case(
Subject = 'Test Case for Trigger',
Status = 'New',
ContactId = testContact.Id,
Severity_Level__c = 'Critical',
Escalation_Status__c = 'Not Escalated'
);
insert testCase;
}
@isTest
static void testCaseStatusChangeToClose() {
Case testCase = [SELECT Id, Status FROM Case LIMIT 1];
Test.startTest();
testCase.Status = 'Closed';
update testCase;
Test.stopTest();
// Verify async processing was triggered
// Note: In actual test, feedback creation happens in @future method
System.assertEquals('Closed', testCase.Status);
}
@isTest
static void testEscalationFlow() {
Case testCase = [SELECT Id, Escalation_Status__c FROM Case LIMIT 1];
Test.startTest();
// The flow should automatically escalate this case
// since it has Critical severity
Test.stopTest();
// Refresh the case
testCase = [SELECT Id, Escalation_Status__c FROM Case WHERE Id = :testCase.Id];
System.assertEquals('Escalated', testCase.Escalation_Status__c);
}
}import { LightningElement, track, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
import getFeedbackRecords from '@salesforce/apex/CaseFeedbackController.getFeedbackRecords';
import submitFeedback from '@salesforce/apex/CaseFeedbackController.submitFeedback';
import Id from '@salesforce/user/Id';
export default class CaseFeedbackPanel extends LightningElement {
userId = Id;
@track feedbackRecords = [];
@track selectedFeedback = null;
@track isSubmitting = false;
@track rating = 0;
@track comments = '';
wiredFeedbackResult;
@wire(getFeedbackRecords, { contactId: '$userId' })
wiredFeedback(result) {
this.wiredFeedbackResult = result;
if (result.data) {
this.feedbackRecords = result.data;
} else if (result.error) {
this.showToast('Error', result.error.body.message, 'error');
}
}
handleFeedbackSelect(event) {
const feedbackId = event.target.dataset.id;
this.selectedFeedback = this.feedbackRecords.find(
feedback => feedback.Id === feedbackId
);
this.rating = 0;
this.comments = '';
}
handleRatingChange(event) {
this.rating = parseInt(event.target.dataset.rating);
}
handleCommentsChange(event) {
this.comments = event.target.value;
}
async handleSubmit() {
if (this.rating === 0) {
this.showToast('Error', 'Please select a rating', 'error');
return;
}
this.isSubmitting = true;
try {
await submitFeedback({
feedbackId: this.selectedFeedback.Id,
rating: this.rating,
comments: this.comments
});
this.showToast('Success', 'Feedback submitted successfully!', 'success');
this.selectedFeedback = null;
this.rating = 0;
this.comments = '';
// Refresh the data
await refreshApex(this.wiredFeedbackResult);
} catch (error) {
this.showToast('Error', error.body.message, 'error');
} finally {
this.isSubmitting = false;
}
}
handleCancel() {
this.selectedFeedback = null;
this.rating = 0;
this.comments = '';
}
showToast(title, message, variant) {
const evt = new ShowToastEvent({
title: title,
message: message,
variant: variant
});
this.dispatchEvent(evt);
}
get hasRecords() {
return this.feedbackRecords && this.feedbackRecords.length > 0;
}
get showForm() {
return this.selectedFeedback !== null;
}
get stars() {
return [1, 2, 3, 4, 5].map(num => ({
number: num,
filled: num <= this.rating
}));
}
}<template>
<lightning-card title="Case Feedback" icon-name="standard:feedback">
<div class="slds-m-around_medium">
<template if:false={hasRecords}>
<div class="slds-text-align_center slds-p-vertical_large">
<lightning-icon icon-name="utility:info" size="large" variant="inverse"></lightning-icon>
<p class="slds-text-heading_small slds-m-top_small">
No pending feedback requests
</p>
</div>
</template>
<template if:true={hasRecords}>
<template if:false={showForm}>
<div class="slds-grid slds-wrap">
<template for:each={feedbackRecords} for:item="feedback">
<div key={feedback.Id} class="slds-col slds-size_1-of-1 slds-m-bottom_small">
<div class="slds-box slds-theme_default">
<div class="slds-grid slds-grid_align-spread">
<div class="slds-col">
<h3 class="slds-text-heading_small">
Case: {feedback.Case__r.CaseNumber}
</h3>
<p class="slds-text-body_regular">
{feedback.Case__r.Subject}
</p>
</div>
<div class="slds-col slds-no-flex">
<lightning-button
variant="brand"
label="Provide Feedback"
data-id={feedback.Id}
onclick={handleFeedbackSelect}>
</lightning-button>
</div>
</div>
</div>
</div>
</template>
</div>
</template>
<template if:true={showForm}>
<div class="slds-box slds-theme_default">
<h3 class="slds-text-heading_medium slds-m-bottom_medium">
Feedback for Case: {selectedFeedback.Case__r.CaseNumber}
</h3>
<div class="slds-form-element slds-m-bottom_medium">
<label class="slds-form-element__label">Rating</label>
<div class="slds-form-element__control">
<div class="rating-container">
<template for:each={stars} for:item="star">
<lightning-icon
key={star.number}
icon-name="utility:favorite"
size="large"
variant={star.filled}
class="rating-star"
data-rating={star.number}
onclick={handleRatingChange}>
</lightning-icon>
</template>
</div>
</div>
</div>
<div class="slds-form-element slds-m-bottom_medium">
<label class="slds-form-element__label">Comments</label>
<div class="slds-form-element__control">
<lightning-textarea
value={comments}
onchange={handleCommentsChange}
placeholder="Share your feedback about the case resolution...">
</lightning-textarea>
</div>
</div>
<div class="slds-grid slds-grid_align-end">
<lightning-button
variant="neutral"
label="Cancel"
onclick={handleCancel}
class="slds-m-right_small">
</lightning-button>
<lightning-button
variant="brand"
label="Submit Feedback"
onclick={handleSubmit}
disabled={isSubmitting}>
</lightning-button>
</div>
</div>
</template>
</template>
</div>
</lightning-card>
</template>.rating-container {
display: flex;
gap: 4px;
}
.rating-star {
cursor: pointer;
transition: all 0.2s ease;
}
.rating-star:hover {
transform: scale(1.1);
}
.slds-box {
border: 1px solid #d8dde6;
border-radius: 4px;
}<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
<target>lightning__RecordPage</target>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>@isTest
public class TestDataFactory {
public static Account createTestAccount() {
Account testAccount = new Account(
Name = 'Test Account',
Type = 'Customer'
);
insert testAccount;
return testAccount;
}
public static Contact createTestContact(Id accountId) {
Contact testContact = new Contact(
FirstName = 'Test',
LastName = 'Contact',
Email = 'test.contact@example.com',
AccountId = accountId
);
insert testContact;
return testContact;
}
public static List<Case> createTestCases(Id contactId, Integer numCases) {
List<Case> testCases = new List<Case>();
for (Integer i = 0; i < numCases; i++) {
testCases.add(new Case(
Subject = 'Test Case ' + i,
Description = 'Test case description ' + i,
Status = 'New',
Priority = 'Medium',
ContactId = contactId,
Severity_Level__c = 'Medium',
Escalation_Status__c = 'Not Escalated'
));
}
insert testCases;
return testCases;
}
public static List<Resolution_Feedback__c> createTestFeedback(List<Case> cases) {
List<Resolution_Feedback__c> feedbackRecords = new List<Resolution_Feedback__c>();
for (Case c : cases) {
feedbackRecords.add(new Resolution_Feedback__c(
Case__c = c.Id,
Contact__c = c.ContactId,
Is_Submitted__c = false
));
}
insert feedbackRecords;
return feedbackRecords;
}
}@isTest
public class CaseSystemIntegrationTest {
@testSetup
static void setupTestData() {
Account testAccount = TestDataFactory.createTestAccount();
Contact testContact = TestDataFactory.createTestContact(testAccount.Id);
List<Case> testCases = TestDataFactory.createTestCases(testContact.Id, 3);
// Set one case to Critical for escalation testing
testCases[0].Severity_Level__c = 'Critical';
update testCases[0];
}
@isTest
static void testCompleteWorkflow() {
// Get test data
List<Case> testCases = [SELECT Id, Status, Severity_Level__c, Escalation_Status__c FROM Case];
Case criticalCase = testCases[0];
Test.startTest();
// Verify escalation happened
System.assertEquals('Critical', criticalCase.Severity_Level__c);
System.assertEquals('Escalated', criticalCase.Escalation_Status__c);
// Close the case to trigger feedback creation
criticalCase.Status = 'Closed';
update criticalCase;
Test.stopTest();
// Verify escalation record was created
List<Escalation__c> escalations = [SELECT Id FROM Escalation__c WHERE Case__c = :criticalCase.Id];
System.assertEquals(1, escalations.size(), 'Escalation record should be created');
// Verify feedback record will be created (async)
// Note: Actual verification would need to be done in separate test method
}
@isTest
static void testBulkOperations() {
List<Case> allCases = [SELECT Id FROM Case];
Test.startTest();
// Bulk close cases
for (Case c : allCases) {
c.Status = 'Closed';
}
update allCases;
Test.stopTest();
// Verify bulk processing doesn't hit limits
System.assertEquals(3, allCases.size());
}
}□ Case Escalation Flow
□ Critical cases auto-escalate
□ Escalation records created correctly
□ Email notifications sent
□ Duplicate escalations prevented
□ Feedback Collection
□ Feedback records created on case closure
□ Only for cases with contacts
□ No duplicates created
□ Async processing works correctly
□ LWC Functionality
□ Feedback panel loads correctly
□ Star rating works
□ Comments can be entered
□ Feedback submits successfully
□ UI updates after submission
□ Error Handling
□ Graceful handling of missing data
□ User-friendly error messages
□ Logging for debugging
□ Performance
□ Bulk operations don't timeout
□ Async processing efficient
□ No SOQL/DML limit issues□ Data Access
□ Users can only see their own feedback
□ Proper sharing rules applied