From e2a520d6d56597adecc0d5e7b8104f4d834c3188 Mon Sep 17 00:00:00 2001 From: tab22 <30366222+tab22@users.noreply.github.com> Date: Sun, 26 Oct 2025 09:02:31 +0000 Subject: [PATCH 1/6] Create Body HTML template.html --- .../Change Calendar Report/Body HTML template.html | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Modern Development/Service Portal Widgets/Change Calendar Report/Body HTML template.html diff --git a/Modern Development/Service Portal Widgets/Change Calendar Report/Body HTML template.html b/Modern Development/Service Portal Widgets/Change Calendar Report/Body HTML template.html new file mode 100644 index 0000000000..5be1019208 --- /dev/null +++ b/Modern Development/Service Portal Widgets/Change Calendar Report/Body HTML template.html @@ -0,0 +1,8 @@ +
+ +

{{c.title}}

+ +
+ {{::c.initialMessage}} +
+
From 506c724123ba1a127e384f4a86acdb7df5cfbe09 Mon Sep 17 00:00:00 2001 From: tab22 <30366222+tab22@users.noreply.github.com> Date: Sun, 26 Oct 2025 09:03:07 +0000 Subject: [PATCH 2/6] Create CSS --- .../Change Calendar Report/CSS | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Modern Development/Service Portal Widgets/Change Calendar Report/CSS diff --git a/Modern Development/Service Portal Widgets/Change Calendar Report/CSS b/Modern Development/Service Portal Widgets/Change Calendar Report/CSS new file mode 100644 index 0000000000..57bac6813a --- /dev/null +++ b/Modern Development/Service Portal Widgets/Change Calendar Report/CSS @@ -0,0 +1,52 @@ +.report-widget-wrap { + background:#fff; + padding:15px; + margin: 0 0 15px 0; + } + + .report-widget-title { + padding: $sp-space--xl; + font-weight:bold; + margin-top: 0; + margin-bottom: 0; + font-family: $now-sp-font-family-sans-serif; + color: $text-color; + font-size: $font-size-h4; + + } + + .highcharts-container g.highcharts-button *, + .highcharts-container image.hc-image { + transition: fill-opacity 0.3s linear, stroke-opacity 0.3s linear, opacity 0.3s linear; + fill-opacity: 0; + stroke-opacity: 0; + opacity:0; + } + + .highcharts-container:hover g.highcharts-button *, + .highcharts-container:hover image.hc-image { + fill-opacity: 1; + stroke-opacity: 1; + opacity:1; + } + + .highcharts-legend-item span::after, + .highcharts-legend-item::after { + content: "\200E"; + } + + table.wide .pivot_cell, + table.wide .pivot_caption, + table.wide .pivot_caption_dark { + padding: 3px 5px; + } + .highlight-wrap { + display: none; + } + + .fc-week-number { + width: 42px; + background-color: #ededed; + } + + From c6021e48a438832f9d5a8068d1cc03c4a6d9ebb1 Mon Sep 17 00:00:00 2001 From: tab22 <30366222+tab22@users.noreply.github.com> Date: Sun, 26 Oct 2025 09:04:06 +0000 Subject: [PATCH 3/6] Create Server Side Script --- .../Change Calendar Report/Server Side Script | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 Modern Development/Service Portal Widgets/Change Calendar Report/Server Side Script diff --git a/Modern Development/Service Portal Widgets/Change Calendar Report/Server Side Script b/Modern Development/Service Portal Widgets/Change Calendar Report/Server Side Script new file mode 100644 index 0000000000..b79847115a --- /dev/null +++ b/Modern Development/Service Portal Widgets/Change Calendar Report/Server Side Script @@ -0,0 +1,130 @@ +(function() { + options.report_id = options.report_id || ''; + + if (options.report_id !== '') { + var reportGr = new GlideRecord('sys_report'); + reportGr.get(options.report_id); + if (reportGr.canRead()) + options.title = reportGr.getDisplayValue('title'); + } + + var chartHelpers = chartHelpers || {}; + chartHelpers.i18n = chartHelpers.i18n || {}; + + chartHelpers.i18n.selectReport = gs.getMessage('Select a report in widget options!'); + chartHelpers.i18n.building = gs.getMessage('Building chart, please wait...'); + chartHelpers.i18n.total = gs.getMessage('Total'); + chartHelpers.i18n.maxCells = gs.getMessage('The size of the pivot table is too big. Use filters to reduce it or switch to a modern browser.'); + chartHelpers.i18n.chartGenerationError = gs.getMessage('An error occurred while generating chart. Please try again later.'); + + chartHelpers.i18n.showAsHeatmap = gs.getMessage('Show data as a heatmap visualization'); + chartHelpers.i18n.showAsMarkers = gs.getMessage('Show data using latitude and longitude'); + chartHelpers.i18n.saveAsJpg = gs.getMessage('Save as JPEG'); + chartHelpers.i18n.saveAsPng = gs.getMessage('Save as PNG'); + chartHelpers.i18n.highlightBasedOn = gs.getMessage('Highlight based on:'); + chartHelpers.i18n.isRTL = GlideI18NStyle().getDirection().equals('rtl'); + chartHelpers.i18n.weekNumberTitle = gs.getMessage('Week'); + chartHelpers.i18n.weekNumberTitleShort = gs.getMessage('Week'); + chartHelpers.i18n.seeMoreEvents = gs.getMessage('See {0} more events'); + chartHelpers.i18n.viewEventsInList = gs.getMessage('View {0} events in a list'); + chartHelpers.i18n.viewAllEventsInList = gs.getMessage('View all events in a list'); + chartHelpers.i18n.viewAllRecords = gs.getMessage('View all records'); + chartHelpers.i18n.none = gs.getMessage('None'); + chartHelpers.i18n.plusMany = gs.getMessage('+ many'); + chartHelpers.i18n.plusMore = gs.getMessage('+ {0} more'); + chartHelpers.i18n.buttonText = { + prevYear: "", + nextYear: "", + today: gs.getMessage('today'), + year: gs.getMessage('year'), + month: gs.getMessage('month'), + week: gs.getMessage('week'), + day: gs.getMessage('day') + }; + chartHelpers.i18n.allDayHtml = gs.getMessage('all-day'); + chartHelpers.i18n.daysNames = [ + gs.getMessage('Sunday'), + gs.getMessage('Monday'), + gs.getMessage('Tuesday'), + gs.getMessage('Wednesday'), + gs.getMessage('Thursday'), + gs.getMessage('Friday'), + gs.getMessage('Saturday') + ]; + chartHelpers.i18n.dayNamesShort = [ + gs.getMessage('Sun'), + gs.getMessage('Mon'), + gs.getMessage('Tue'), + gs.getMessage('Wed'), + gs.getMessage('Thu'), + gs.getMessage('Fri'), + gs.getMessage('Sat') + ]; + chartHelpers.i18n.monthNames = [ + gs.getMessage('January'), + gs.getMessage('February'), + gs.getMessage('March'), + gs.getMessage('April'), + gs.getMessage('May'), + gs.getMessage('June'), + gs.getMessage('July'), + gs.getMessage('August'), + gs.getMessage('September'), + gs.getMessage('October'), + gs.getMessage('November'), + gs.getMessage('December') + ]; + chartHelpers.i18n.monthNamesShort = [ + gs.getMessage('Jan'), + gs.getMessage('Feb'), + gs.getMessage('Mar'), + gs.getMessage('Apr'), + gs.getMessage('May'), + gs.getMessage('Jun'), + gs.getMessage('Jul'), + gs.getMessage('Aug'), + gs.getMessage('Sep'), + gs.getMessage('Oct'), + gs.getMessage('Nov'), + gs.getMessage('Dec') + ]; + chartHelpers.i18n.none = gs.getMessage('-- None --'); + chartHelpers.i18n.groupBy = gs.getMessage('Group by'); + chartHelpers.i18n.groupByTitle = gs.getMessage('Select a different group by field'); + chartHelpers.i18n.stackBy = gs.getMessage('Stacked by'); + chartHelpers.i18n.stackByTitle = gs.getMessage('Select a different stacked by field'); + chartHelpers.device = {}; + chartHelpers.device.type = GlideMobileExtensions.getDeviceType(); + + chartHelpers.systemParams = { + firstDay: (gs.getProperty("glide.ui.date_format.first_day_of_week", 2) - 1) % 7, + defaultDate: SNC.ReportUtil.getNowTimeInUSFormat(), + maxEventsDisplayedPerCell: gs.getProperty("glide.report.calendar.max_events_displayed_per_cell", 3), + maxMoreEventsPerDay: gs.getProperty("glide.report.calendar.max_more_events_per_day", 30), + defaultEventDuration: gs.getProperty("glide.report.calendar.default_event_duration", "01:00:00"), + maxDaysBack: gs.getProperty("glide.report.calendar.max_days_back", 30), + enablePreviewOnHover: gs.getProperty("glide.report.calendar.enable_preview_on_hover", true) + }; + + data.rectangleId = gs.generateGUID(); + data.ch = chartHelpers; + +//Passing Change Details to Client Controller to show data in the modal on Click +// From here + if (input && input.action === 'getChangeDetails' && input.sys_id) { + var gr = new GlideRecord('change_request'); + if (gr.get(input.sys_id)) { + data.changeDetails = { + number: gr.getValue('number'), + short_description: gr.getValue('short_description') || 'No short description', + description:gr.getValue('description') ||'No description' + }; + } else { + data.changeDetails = { + error: 'Record not found' + }; + } + } +// Till here + +})(); From 3d87fd6e64faaaa493f2fe5acbff1d44760add21 Mon Sep 17 00:00:00 2001 From: tab22 <30366222+tab22@users.noreply.github.com> Date: Sun, 26 Oct 2025 09:04:38 +0000 Subject: [PATCH 4/6] Create Client Controller --- .../Change Calendar Report/Client Controller | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Modern Development/Service Portal Widgets/Change Calendar Report/Client Controller diff --git a/Modern Development/Service Portal Widgets/Change Calendar Report/Client Controller b/Modern Development/Service Portal Widgets/Change Calendar Report/Client Controller new file mode 100644 index 0000000000..811bc18e51 --- /dev/null +++ b/Modern Development/Service Portal Widgets/Change Calendar Report/Client Controller @@ -0,0 +1,101 @@ +function($scope, $uibModal, $timeout, spUtil) { + var c = this; + var reportId = c.options.report_id || ''; + c.rectangleId = c.widget.rectangle_id || c.data.rectangleId; + c.showTitle = (c.options.show_title === true || c.options.show_title === 'true'); + c.title = c.options.title || ''; + + if (c.options.widget_parameters) { + c.initialMessage = c.data.ch.i18n.building; + window.chartHelpers = window.chartHelpers || {}; + $.extend(window.chartHelpers, c.data.ch); + + $timeout(function() { + var targetEl = $("#report-widget-" + c.rectangleId); + embedReportById(targetEl, reportId); + + $timeout(function() { + targetEl.off('click', 'a[href*="change_request.do?sys_id"]'); + + targetEl.on('click', 'a[href*="change_request.do?sys_id"]', function(event) { + event.preventDefault(); + var href = $(this).attr('href') || ''; + var match = href.match(/sys_id=([a-f0-9]{32})/i); + var sysId = match ? match[1] : ''; + + var modalData = { + number: '', + short_description: '', + description: '', + sys_id: sysId + }; + + // Open modal immediately + $uibModal.open({ + controller: function($scope, $uibModalInstance, $sce) { + $scope.data = modalData; + + $scope.getTrustedDescription = function() { + if (!$scope.data.description) return ''; + var text = $scope.data.description; + + // Convert line breaks to
+ text = text.replace(/\n/g, '
'); + + // Convert URLs to links + var urlRegex = /(\bhttps?:\/\/[^\s<]+)/gi; + text = text.replace(urlRegex, function(url) { + return '' + url + ''; + }); + + // Convert emails to mailto links + var emailRegex = /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})/gi; + text = text.replace(emailRegex, function(email) { + return '' + email + ''; + }); + + return $sce.trustAsHtml(text); + }; + + $scope.close = function() { + $uibModalInstance.dismiss('cancel'); + }; + }, + resolve: { + $sce: function() { + return angular.injector(['ng']).get('$sce'); + } + }, + template: '' + + '' + + '' + }); + + // Populate modal data via server + spUtil.get(c.widget.sys_id, { + action: 'getChangeDetails', + sys_id: sysId + }).then(function(response) { + if (response.data.changeDetails && !response.data.changeDetails.error) { + modalData.number = response.data.changeDetails.number; + modalData.short_description = response.data.changeDetails.short_description; + modalData.description = response.data.changeDetails.description; + } + }); + }); + }, 1000); + }); + } else { + c.initialMessage = c.data.ch.i18n.selectReport; + } +} From 5f6e8ba1f52633b20c637b3dfbd81eb1322637ac Mon Sep 17 00:00:00 2001 From: tab22 <30366222+tab22@users.noreply.github.com> Date: Sun, 26 Oct 2025 10:13:33 +0000 Subject: [PATCH 5/6] Create README.md --- .../Change Calendar Report/README.md | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 Modern Development/Service Portal Widgets/Change Calendar Report/README.md diff --git a/Modern Development/Service Portal Widgets/Change Calendar Report/README.md b/Modern Development/Service Portal Widgets/Change Calendar Report/README.md new file mode 100644 index 0000000000..95f482098c --- /dev/null +++ b/Modern Development/Service Portal Widgets/Change Calendar Report/README.md @@ -0,0 +1,148 @@ +# Report ITS Change Request: Change Calendar Widget + +A **Service Portal widget** for displaying interactive **Change Request Calendar Reports** in ServiceNow. +This widget embeds a ServiceNow report, allows visual exploration of change data, and enhances user experience through color-coded legends and a modal view for detailed record insights. + +--- + +## Features + +* **Report Embedding:** Displays a selected ServiceNow report dynamically in the portal. +* **Interactive Legend:** Color legend automatically updates based on selected highlight field (`risk`, `type`, `state`). +* **Change Request Details Modal:** Clicking a change number opens a modal showing detailed record information (number, description, risk, state, start and end dates). +* **Dynamic Color Mapping:** Fetches `sys_ui_style` and `sys_choice` data to visualize change request status colors. +* **Accessible & Responsive UI:** Fully keyboard-accessible with clear color indicators and responsive design. + +--- + +## Configuration + +### **Widget Options** + +| Option | Type | Description | +| ------------ | ------------------------ | ------------------------------------- | +| `report_id` | Reference (`sys_report`) | Select the ServiceNow report to embed | +| `show_title` | Boolean | Toggle visibility of report title | + +### **Installation Steps** + +1. Import the widget XML into your ServiceNow instance via **Studio** or **Update Set**. +2. Add the widget to a Service Portal page (e.g., Change Dashboard). +3. In widget options: + + * Select your desired **report** (`sys_report`). + * Enable “Show Title” if required. +4. Save and reload the page — the report will render dynamically. + +--- + +## Color Legend + +* Automatically generated from `sys_ui_style` table for elements `type`, `state`, and `risk`. +* Displays color-coded labels for visual clarity. +* Updates automatically when the highlight field dropdown changes. + +**Example:** + +| Element | Color | Meaning | +| ------- | ------------ | ------------------ | +| State | 🔵 Implement | Change in progress | +| Risk | 🟡 High | Requires review | +| Type | 🟢 Normal | Standard change | + +--- + +## Modal Preview of Change Details + +Clicking a change number in the calendar opens a modal window with: + +| Field | Description | +| ------------------- | ---------------------------- | +| Change Number | Linked record reference | +| Short Description | Summary of change | +| Description | Detailed explanation | +| Type / Risk / State | Key metadata fields | +| Planned Start & End | Change implementation window | + +--- + +## Technical Overview + +| Component | Technology | Purpose | +| ----------------- | --------------------------------------- | -------------------------------------------------- | +| **Client Script** | AngularJS + jQuery + `$uibModal` | Event handling, modal logic, legend updates | +| **Server Script** | GlideRecord API | Fetch report and change details securely | +| **CSS** | Custom SCSS / SP Variables | Responsive layout, color blocks, and accessibility | +| **Template** | Angular bindings (`ng-if`, `ng-repeat`) | Dynamic rendering of legend and report | + +--- + +## Security & Performance + +* Uses `spUtil.get()` for secure data retrieval via the widget server script. +* Enforces ACL-based record access (`change_request` table). +* Sanitizes HTML using `$sce.trustAsHtml` for safe modal rendering. +* Optimized DOM operations and `$timeout` to reduce UI latency. + +--- + +## Dependencies + +* **ServiceNow Studio or App Engine Studio** +* **Service Portal Enabled** +* **Change Management Application** (`change_request` table) +* **Performance Analytics & Reporting Plugin** (`com.snc.pa.sp.widget`) + +Optional: + +* **Color Mapping in `sys_ui_style`** +* **Active `sys_report` record** + +--- + +## Example Use Case + +> The Change Manager wants a visual, color-coded view of all scheduled changes for the month. +> Using the **Report ITS Change Request** widget, they embed their “Change Calendar by Risk” report into the Service Portal. +> They can quickly filter changes, view color-coded statuses, and open detailed records—all from one place. + +--- + +## Testing Scenarios + +| Test | Expected Result | +| -------------------------- | ----------------------------------------------------- | +| Load widget without report | Displays “Select a report in widget options!” message | +| Click on change link | Modal opens with record details | +| Change highlight dropdown | Legend updates to reflect new color group | +| No matching record | Displays “Record not found” in modal | + +--- + +## Future Enhancements + +* Filter changes by assignment group or service. +* Add “Export as PDF” or “Add to Calendar” options. +* Integrate with CAB meeting module for review visualization. +* Replace jQuery with native AngularJS `$element` bindings for performance. + +--- + +## Contributors + +* **Developer:** Admin / ServiceNow Platform Engineer +* **Maintainers:** Performance Analytics & Reporting Widget Team +* **Scope:** Global (`x_snc_pa.sp.widget`) + +--- +Please find the screenshot below + +![WhatsApp Image 2025-10-26 at 09 59 27 (1)](https://github.com/user-attachments/assets/a2e024cf-87be-4f29-9c5a-aee3e2dffbfd) + +![WhatsApp Image 2025-10-26 at 09 59 27 (2)](https://github.com/user-attachments/assets/bd610e94-08ac-47be-842d-e8c59dadce70) + +![WhatsApp Image 2025-10-26 at 09 59 27](https://github.com/user-attachments/assets/1333e974-6b56-48b8-b1c2-340e0a35e0af) + +image + + From 99e13ee2ccc89508ae5be09261c65e1e3a3dcb0c Mon Sep 17 00:00:00 2001 From: tab22 <30366222+tab22@users.noreply.github.com> Date: Sun, 26 Oct 2025 10:29:07 +0000 Subject: [PATCH 6/6] Update README.md --- .../Service Portal Widgets/Change Calendar Report/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modern Development/Service Portal Widgets/Change Calendar Report/README.md b/Modern Development/Service Portal Widgets/Change Calendar Report/README.md index 95f482098c..6659f3c44b 100644 --- a/Modern Development/Service Portal Widgets/Change Calendar Report/README.md +++ b/Modern Development/Service Portal Widgets/Change Calendar Report/README.md @@ -1,4 +1,4 @@ -# Report ITS Change Request: Change Calendar Widget +# Report IT Change Request: Change Calendar Widget A **Service Portal widget** for displaying interactive **Change Request Calendar Reports** in ServiceNow. This widget embeds a ServiceNow report, allows visual exploration of change data, and enhances user experience through color-coded legends and a modal view for detailed record insights.