Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Modern Development/Service Portal Widgets/My Reminders/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Service Portal Reminder Widget

A simple custom ServiceNow Service Portal widget for viewing and creating personal reminders.

## Features

- This uses **ServiceNow's OOB Reminder table**. Table name : 'reminder'
- Displays a list of reminders for the current user.
- Provides a form to create new reminders.
- Allows associating reminders with any Task record.
- Auto-refreshes the list after a new reminder is created.


## How to create

1. Navigate to **Service Portal > Widgets**.
2. Click **Create a new widget**.
3. Set the **Widget Name** (e.g. 'My Reminders') and **ID** (e.g., 'reminder-widget').
4. Copy and paste the provided HTML, CSS, Client Script, and Server Script into their respective tabs.
5. Save the widget.

## How to Use

1. Open your target portal page in the Service Portal Designer.
2. Find your widget in the "Widgets" filter on the left.
3. Drag and dropp the widget onto the page.

## Screenshots
<img width="1831" height="396" alt="image" src="https://github.com/user-attachments/assets/bdb124a8-9634-4884-8d4b-cfb79225f07e" />
<img width="1728" height="731" alt="image" src="https://github.com/user-attachments/assets/9bef4295-0321-4806-a4e5-465d80881bdc" />

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">My Reminders</h3>
</div>
<div class="panel-body">
<div ng-if="!c.data.reminders.length" class="alert alert-info text-center">
You have no active reminders.
</div>

<ul class="list-group" ng-if="c.data.reminders.length > 0">
<li class="list-group-item reminder-item" ng-repeat="reminder in c.data.reminders">
<h4>{{reminder.subject}}</h4>
<p class="text-muted">
<strong>Task:</strong> {{reminder.task_display || 'N/A'}} <br/>
<strong>Action:</strong> Send an {{reminder.using}} {{reminder.remind_me}} minutes before {{reminder.field_display}}.
</p>
<p ng-if="reminder.notes"><strong>Notes:</strong> {{reminder.notes}}</p>
</li>
</ul>

<hr/>

<div class="reminder-form-container">
<h4>Create a New Reminder</h4>
<form name="reminderForm" ng-submit="c.createReminder()">
<div class="form-group">
<label for="task">Task (Optional)</label>
<sn-record-picker field="c.taskField" table="'task'" display-field="'number'" value-field="'sys_id'" search-fields="'number,short_description'" page-size="10" placeholder="Search for a task..."></sn-record-picker>
</div>

<div class="form-group">
<label for="subject">Subject <span class="text-danger">*</span></label>
<input type="text" id="subject" class="form-control" ng-model="c.newReminder.subject" required>
</div>

<div class="form-group">
<label for="notes">Notes</label>
<textarea id="notes" class="form-control" ng-model="c.newReminder.notes" rows="3"></textarea>
</div>

<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="remind_me">Remind Me... <span class="text-danger">*</span></label>
<select id="remind_me" class="form-control" ng-model="c.newReminder.remind_me" required>
<option value="" disabled selected>-- Select Time --</option>
<option value="15">15 Minutes</option>
<option value="30">30 Minutes</option>
<option value="60">1 Hour</option>
<option value="120">2 Hours</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="field">...Before <span class="text-danger">*</span></label>
<select id="field" class="form-control" ng-model="c.newReminder.field" required>
<option value="" disabled selected>-- Select Date Field --</option>
<option value="activity_due">Activity Due</option>
<option value="due_date">Due Date</option>
<option value="follow_up">Follow Up</option>
<option value="sla_due">SLA Due</option>
</select>
</div>
</div>
</div>

<div class="form-group">
<label for="using">Using... <span class="text-danger">*</span></label>
<select id="using" class="form-control" ng-model="c.newReminder.using" required>
<option value="" disabled selected>-- Select Method --</option>
<option value="email">Send an email</option>
<option value="outlook">Outlook calendar invite</option>
</select>
</div>

<div class="form-group">
<button type="submit" class="btn btn-primary" ng-disabled="reminderForm.$invalid">
<i class="fa fa-plus-circle"></i> Create Reminder
</button>
</div>
</form>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.reminder-item {
border-left: 4px solid #337ab7;
margin-bottom: 10px;
padding: 10px 15px;
}

.reminder-item h4 {
margin-top: 0;
font-weight: bold;
color: #333;
}

.reminder-item p {
margin-bottom: 5px;
}

.reminder-form-container {
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}

.form-group {
margin-bottom: 15px;
}

.panel-heading + .panel-body {
padding-top: 20px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function($scope) {
var c = this;

// Object to hold data for the new reminder form
c.newReminder = {};

// Special object for the sn-record-picker directive
c.taskField = {
displayValue: '',
value: '',
name: 'task'
};

// Function to submit the new reminder
c.createReminder = function() {
// Check if the form is valid before submitting
if ($scope.reminderForm.$invalid) {
return;
}

// Set the task sys_id from the record picker into our submission object
c.newReminder.task = c.taskField.value;
c.data.newReminder = c.newReminder;

// Set an action for the server to identify the request
c.data.action = 'create_reminder';

// Call the server script to insert the record
c.server.update().then(function(response) {
// Clear the action and the form model after successful submission
c.data.action = undefined;
c.newReminder = {};
c.taskField.displayValue = '';
c.taskField.value = '';

// Refresh the reminder list by reloading server data
c.server.get().then(function(response) {
c.data = response.data;
});
});
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
(function() {

var currentUserId = gs.getUserID();
data.reminders = [];


if (input && input.action === 'create_reminder') {
var newReminder = new GlideRecord('reminder');
newReminder.initialize();
newReminder.setValue('user', currentUserId);
newReminder.setValue('task', input.newReminder.task);
newReminder.setValue('subject', input.newReminder.subject);
newReminder.setValue('notes', input.newReminder.notes);
newReminder.setValue('remind_me', input.newReminder.remind_me);
newReminder.setValue('field', input.newReminder.field);
newReminder.setValue('using', input.newReminder.using);
newReminder.insert();
}


var reminderGR = new GlideRecord('reminder');
reminderGR.addQuery('user', currentUserId);
reminderGR.orderByDesc('sys_created_on'); // Show newest first
reminderGR.query();

while (reminderGR.next()) {
var reminderObj = {};
reminderObj.sys_id = reminderGR.getUniqueValue();
reminderObj.subject = reminderGR.getValue('subject');
reminderObj.notes = reminderGR.getValue('notes');
reminderObj.remind_me = reminderGR.getValue('remind_me');
reminderObj.field_display = reminderGR.getDisplayValue('field'); // Get user-friendly display value
reminderObj.using = reminderGR.getValue('using');
reminderObj.task_display = reminderGR.getDisplayValue('task'); // Get task number/display value
data.reminders.push(reminderObj);
}

})();
Loading