Skip to content

Commit 69f205e

Browse files
authored
Merge branch 'main' into enhance-incident-description-problem
2 parents a676645 + f609e38 commit 69f205e

File tree

6 files changed

+212
-0
lines changed

6 files changed

+212
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* Main container styling with padding and rounded corners */
2+
.stepper-container {
3+
padding: 16px;
4+
border-radius: 8px;
5+
}
6+
7+
/* Flex container for horizontal stepper layout */
8+
.stepper {
9+
display: flex;
10+
justify-content: space-between;
11+
align-items: center;
12+
}
13+
14+
/* Individual step container with flex properties */
15+
.stepper-step {
16+
display: flex;
17+
align-items: center;
18+
flex: 1;
19+
position: relative;
20+
}
21+
22+
/* Circular step indicator base styling */
23+
.stepper-circle {
24+
width: 40px;
25+
height: 40px;
26+
background: #e0e0e0;
27+
border-radius: 50%;
28+
display: flex;
29+
align-items: center;
30+
justify-content: center;
31+
font-weight: bold;
32+
font-size: 20px;
33+
color: #fff;
34+
}
35+
36+
/* Green background for completed steps */
37+
.stepper-circle.completed {
38+
background: #43A047;
39+
}
40+
41+
/* Blue background with border for active step */
42+
.stepper-circle.active {
43+
background: #4285f4;
44+
border: 3px solid #1976d2;
45+
}
46+
47+
/* Step label text styling */
48+
.stepper-label {
49+
margin-left: 12px;
50+
font-size: 20px;
51+
color: #333;
52+
opacity: 0.7;
53+
font-weight: normal;
54+
}
55+
56+
/* Enhanced styling for completed and active step labels */
57+
.stepper-label.completed,
58+
.stepper-label.active {
59+
color: #222;
60+
font-weight: bold;
61+
opacity: 1;
62+
}
63+
64+
/* Connecting line between steps */
65+
.stepper-line {
66+
height: 2px;
67+
background: #e0e0e0;
68+
flex: 1;
69+
margin: 0 16px;
70+
}
71+
72+
/* Hide line after the last step */
73+
.stepper-step:last-child .stepper-line {
74+
display: none;
75+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!-- Main container for the stepper component -->
2+
<div class="stepper-container">
3+
<!-- Stepper wrapper with flex layout -->
4+
<div class="stepper">
5+
<!-- Loop through each step in the data.steps array -->
6+
<div ng-repeat="step in data.steps track by $index" class="stepper-step">
7+
<!-- Circle element showing step number or checkmark -->
8+
<div class="stepper-circle" ng-class="{
9+
'completed': $index < data.currentStep,
10+
'active': $index === data.currentStep
11+
}">
12+
<!-- Show checkmark icon for completed steps -->
13+
<i ng-if="$index < data.currentStep" class="fa fa-check"></i>
14+
<!-- Show step number for current and future steps -->
15+
<span ng-if="$index >= data.currentStep">{{$index + 1}}</span>
16+
</div>
17+
<!-- Label text for each step -->
18+
<span class="stepper-label" ng-class="{
19+
'completed': $index < data.currentStep,
20+
'active': $index === data.currentStep
21+
}">
22+
{{step}}
23+
</span>
24+
<!-- Connecting line between steps (hidden for last step) -->
25+
<div class="stepper-line" ng-if="!$last"></div>
26+
</div>
27+
</div>
28+
</div>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Stepper Widget
2+
3+
This custom widget provides a visually appealing **stepper** (multi-step progress indicator) for ServiceNow Service Portal, allowing you to display progress through steps such as campaign creation or onboarding.
4+
5+
## Features
6+
7+
- Shows steps with dynamic titles and highlights the current and completed steps.
8+
- Steps and current step are passed in as widget options.
9+
- Completed steps show a green icon.
10+
- Handles widget options for showing steps and the current step.
11+
12+
<img width="1314" height="257" alt="image" src="https://github.com/user-attachments/assets/abc005ea-3dc2-49c7-9108-5008dcf620f4" />
13+
14+
15+
## Widget Options
16+
17+
| Option | Type | Description | Example |
18+
|---------------|--------|-----------------------------------------------|------------------------------------------|
19+
| steps | String | Stringified array of step names (JSON array) | `["Step 1", "Step 2", "Step 3"]` |
20+
| currentStep | Number | The current active step (0-based index) | `1` |
21+
22+
## Usage
23+
24+
1. Add the widget to your Service Portal page.
25+
2. In the widget options, set:
26+
- **steps** as a JSON string array (e.g., `["Step 1", "Step 2", "Step 3"]`)
27+
- **currentStep** as the index of the current step (e.g., `1`)
28+
<img width="1119" height="358" alt="image" src="https://github.com/user-attachments/assets/a51d48e1-1881-4b8c-9b67-06e0a0165c4c" />
29+
30+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(function() {
2+
// Parse and set the steps array from widget options
3+
// If options.steps exists, parse the JSON string; otherwise, use empty array
4+
data.steps = options.steps ? JSON.parse(options.steps) : [];
5+
6+
// Parse and set the current step index from widget options
7+
// If options.current_step exists, convert to integer; otherwise, default to 0
8+
data.currentStep = options.current_step ? parseInt(options.current_step) : 0;
9+
})();
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
var impersonatorUserID = 'zane.sulikowski'; //Replace it with the user ID of user for whom we need to check impersonation details
2+
3+
var isUserPresent = new GlideRecord('sys_user');
4+
if (isUserPresent.get('user_name', impersonatorUserID)) {
5+
6+
var queryEvents = new GlideRecord('sysevent');
7+
queryEvents.addEncodedQuery("name=impersonation.start^ORname=impersonation.end^parm1=" + impersonatorUserID + "^sys_created_onONToday@javascript:gs.beginningOfToday()@javascript:gs.endOfToday()");
8+
queryEvents.orderBy('sys_created_on');
9+
queryEvents.query();
10+
11+
//This object will hold all events grouped by impersonated user which is in parm2
12+
var userEvents = {};
13+
14+
while (queryEvents.next()) {
15+
var impersonatedId = queryEvents.getValue('parm2');
16+
if (!userEvents[impersonatedId])
17+
userEvents[impersonatedId] = [];
18+
userEvents[impersonatedId].push({
19+
name: queryEvents.getValue('name'),
20+
time: queryEvents.getValue('sys_created_on')
21+
});
22+
}
23+
24+
} else {
25+
gs.info('Invalid User');
26+
}
27+
28+
29+
function getUserName(sysId) {
30+
var getUser = new GlideRecord('sys_user');
31+
if (getUser.get(sysId)) {
32+
return getUser.getDisplayValue('name');
33+
}
34+
return sysId;
35+
}
36+
37+
38+
for (var userId in userEvents) {
39+
var events = userEvents[userId];
40+
var totalSeconds = 0;
41+
var startTime = null;
42+
43+
events.forEach(function(evt) {
44+
if (evt.name === 'impersonation.start') {
45+
startTime = new GlideDateTime(evt.time);
46+
} else if (evt.name === 'impersonation.end' && startTime) {
47+
var endTime = new GlideDateTime(evt.time);
48+
totalSeconds += (endTime.getNumericValue() - startTime.getNumericValue()) / 1000;
49+
startTime = null;
50+
}
51+
});
52+
53+
54+
var hours = Math.floor(totalSeconds / 3600);
55+
var minutes = Math.floor((totalSeconds % 3600) / 60);
56+
var seconds = Math.floor(totalSeconds % 60);
57+
58+
gs.info(impersonatorUserID + " impersonated User: " + getUserName(userId) +
59+
" - Total Duration of impersonation is : " + hours + "hrs " + minutes + "min " + seconds + "sec (" + totalSeconds + "sec)");
60+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
This script helps to get the impersonator and impersonated user details and duration of impersonation.
2+
3+
4+
Details of Events:
5+
6+
(impersonation.start) which shows that the impersonation has started,
7+
(impersonation.end) shows that the impersonation has ended.
8+
9+
Parm1 contains the userid of user who started the impersonation.
10+
Parm2 contains the userid of user whom we have impersonated.

0 commit comments

Comments
 (0)