- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-17-2025 03:38 AM
Hi everyone,
I'm building a custom Service Portal widget that allows users to either accept or reject a proposed solution on an incident record when it's in the Resolved state.
The "Accept Solution" button works perfectly and updates the incident state to Closed.
However, when clicking the "Reject Solution" button, a modal pops up with a textarea for the user to enter a rejection reason. The form has a submit button which should call submitRejection().
Here’s the issue:
Even after entering text into the textarea, the script always thinks the comment is empty. It keeps showing the alert:
"Please enter a reason for rejection."
Please find the blow client script in widget is where i'm having problem:
function($scope) {
$scope.showModal = false;
$scope.rejectionReason = '';// Accept Solution (unchanged)
$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', ''); // Empty for acceptga.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);
}
});
};// Open Reject Modal
$scope.showRejectModal = function() {
$scope.rejectionReason = '';
$scope.showModal = true;
};// Close Reject Modal
$scope.closeModal = function() {
$scope.rejectionReason = '';
$scope.showModal = false;
};// Force model sync on change
$scope.updateRejectionReason = function() {
if (!$scope.$$phase) {
$scope.$apply();
}
};// Submit Rejection
$scope.submitRejection = function() {
var comment = ($scope.rejectionReason || '').trim();console.log("DEBUG: rejectionReason = [" + 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);
}
});
};
}
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-17-2025 07:09 AM
Hi everyone,
i was able to figure out 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.
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:
Server script:
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
Thank you,
Puneet Hegde
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-17-2025 04:07 AM - edited ‎07-17-2025 04:09 AM
Hi @Puneet Hegde1 ,
In submit rejection function replace the line 'var comment = ($scope.rejectionReason || '').trim();' with
var comment =($scope.rejectiontReason || "").trim();
Try it.
Did you check after logging the value of $scope.rejectionReason or comment is it printing the values as entered in pop up. It should log the value entered in pop up.
One more thing, replace if(!comment) with if(comment == "") =>if comment is blank then it will give pop up with "Please enter the reason for rejection".
Thanks,
Bhimashankar
------------------------------------------------------------------------------------------------------------------------------------
If my response points you in the right directions, please consider marking it as 'Helpful'. Thanks!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-17-2025 05:44 AM
Dear Bhimashankar,
Yes, I have tried logging the value, but that also returns blank, as any input provided is not at all considered
Thank you,
Puneet
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-17-2025 07:11 AM
Dear Bhimashankar,
I was able to identify the issue. The main problem was with the scope, where I forgot to define the scope in the client script. Once I defined the scope, it worked as expected, and I also polished some CSS to make the widget look good.
Thank you,
Puneet
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-17-2025 07:09 AM
Hi everyone,
i was able to figure out 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.
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:
Server script:
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
Thank you,
Puneet Hegde