ServiceNow Widget for Accepting or Rejecting Incident Resolution in Service Portal

Puneet Hegde1
Tera Guru

This article demonstrates how to build a custom Service Portal widget that allows end users to accept or reject the resolution of an Incident.

When an incident is moved to the Resolved state, the widget appears to the user.
They can either:

  • Accept the solution, which automatically closes the incident, or

  • Reject the solution, by providing a reason — which then reopens the incident.

This helps IT support teams gather real-time feedback on resolution quality, increases accountability, and enhances the overall ITSM experience.


HTML:

<div ng-if="data.showWidget && !data.actionTaken" class="panel panel-default p-3">
<h4 class="widget-heading text-primary">Was the solution helpful?</h4>

<div class="d-flex flex-column align-items-start">
<button class="btn btn-success mb-2 widget-button" ng-click="acceptSolution()">
Accept Solution
</button>

<button class="btn btn-danger widget-button" ng-click="showRejectModal()">
Reject Solution
</button>
</div>
</div>

<div ng-if="data.actionTaken">
<p>{{ data.message }}</p>
</div>

<div class="modal-backdrop" ng-if="showModal">
<div class="modal-content-box p-3" style="background: white; border-radius: 6px; max-width: 500px; margin: 100px auto;">
<form name="rejectForm" novalidate ng-submit="submitRejection()">
<h5 class="mb-2">Please tell us why you're rejecting the solution</h5>

<textarea ng-model="data.rejectionReason"
class="form-control"
rows="4"
required
placeholder="Enter your reason..."></textarea>


<div class="modal-buttons mt-3 d-flex justify-content-end">
<button type="button" class="btn btn-secondary mr-2" ng-click="closeModal()">Cancel</button>
<button type="submit" class="btn btn-primary widget-button" ng-disabled="rejectForm.$invalid">Submit</button>
</div>
</form>
</div>
</div>


CSS:

/* === Panel/Card === */
.panel {
border: none;
border-radius: 10px;
background-color: #ffffff;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}

/* === Heading === */
.widget-heading {
font-weight: 600;
margin-bottom: 15px;
color: var(--brand-primary, #007bff); /* Use portal theme color or fallback to Bootstrap blue */
}

/* === Base Button === */
.btn {
width: 100%;
max-width: 300px;
font-weight: 600;
padding: 10px;
font-size: 14px;
border-radius: 8px;
}

.mb-2 {
margin-bottom: 10px;
}

/* === Custom Widget Button Enhancements === */
.widget-button {
width: 100%;
max-width: 300px;
font-weight: 600;
padding: 10px 20px;
font-size: 15px;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease-in-out;
}

.widget-button:hover {
transform: translateY(-1px);
}

/* === Force Button Colors (Overrides Theme) === */
.btn-success.widget-button {
background-color: #28a745 !important;
border-color: #28a745 !important;
color: #fff !important;
}

.btn-danger.widget-button {
background-color: #dc3545 !important;
border-color: #dc3545 !important;
color: #fff !important;
}

.btn-success.widget-button:hover {
background-color: #218838 !important;
}

.btn-danger.widget-button:hover {
background-color: #c82333 !important;
}

/* === Modal Overlay === */
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}

/* === Modal Content Box === */
.modal-content-box {
background-color: #fff;
padding: 25px;
border-radius: 10px;
width: 500px;
max-width: 90%;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}

/* === Modal Buttons Layout === */
.modal-buttons {
display: flex;
justify-content: flex-end;
}

/* === Textarea === */
textarea.form-control {
width: 100%;
resize: vertical;
}

.btn-primary.widget-button {
background-color: var(--brand-primary, #007bff) !important;
border-color: var(--brand-primary, #007bff) !important;
color: #fff !important;
}

.btn-primary.widget-button:hover {
background-color: #0069d9 !important;
}


Client script:

function($scope) {
$scope.showModal = false;
$scope.rejectionReason = '';

$scope.acceptSolution = function() {
var ga = new GlideAjax('SolutionFeedbackHandler');
ga.addParam('sysparm_name', 'processFeedback');
ga.addParam('sysparm_incident_id', $scope.data.sys_id);
ga.addParam('sysparm_action', 'accept');
ga.addParam('sysparm_comment', '');

ga.getXMLAnswer(function(response) {
if (response === 'success') {
$scope.$apply(function() {
$scope.data.message = "Thank you! The incident is now closed.";
$scope.data.actionTaken = true;
});
} else {
alert("Failed: " + response);
}
});
};

$scope.showRejectModal = function() {
$scope.rejectionReason = '';
$scope.showModal = true;
};

$scope.closeModal = function() {
$scope.rejectionReason = '';
$scope.showModal = false;
};

$scope.data.rejectionReason = '';

$scope.submitRejection = function() {
var comment = ($scope.data.rejectionReason || "").trim();
console.log("DEBUG: comment =", comment);

if (comment === "") {
alert("Please enter a reason for rejection.");
return;
}

var ga = new GlideAjax('SolutionFeedbackHandler');
ga.addParam('sysparm_name', 'processFeedback');
ga.addParam('sysparm_incident_id', $scope.data.sys_id);
ga.addParam('sysparm_action', 'reject');
ga.addParam('sysparm_comment', comment);

ga.getXMLAnswer(function(response) {
if (response === 'success') {
$scope.$apply(function() {
$scope.data.message = "Thanks! The incident has been reopened.";
$scope.data.actionTaken = true;
$scope.showModal = false;
});
} else {
alert("Failed: " + response);
}
});
};
}

Server script:
(function() {
data.sys_id = $sp.getParameter("sys_id");
data.showWidget = false;

var gr = new GlideRecord('incident');
if (gr.get(data.sys_id) && gr.state == 6) { // 6 = Resolved
data.showWidget = true;
}
})();

Screenshots of the output of the widget are below

Feel free to use the widget as needed or suggest the best modification which makes functionality more dynamic for other tables.

Please click helpful if you use it 

PuneetHegde1_0-1752761204764.pngPuneetHegde1_1-1752761233625.png

 


Thank you,
Puneet Hegde


1 REPLY 1

Peter Bodelier
Giga Sage

Or you could simply use OOTB configuration with the standard ticket widget:

PeterBodelier_0-1752761727670.pngPeterBodelier_1-1752761745706.png

 


Help others to find a correct solution by marking the appropriate response as accepted solution and helpful.