From ca3b509918fd1957fb2ec1cb30718da33021b2d1 Mon Sep 17 00:00:00 2001 From: Siddhartha Dasari <143531683+Siddhartha03D@users.noreply.github.com> Date: Sat, 4 Oct 2025 20:51:45 +0530 Subject: [PATCH 1/3] Added Auto Save Draft Feature that Implements automatic form data and Adds draft restoration --- .../Auto Save Draft Feature/script.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js diff --git a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js new file mode 100644 index 0000000000..89cf714501 --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js @@ -0,0 +1,66 @@ +/** + * Auto-save draft feature for Catalog Client Script + * + * This script automatically saves form data as a draft in the browser's sessionStorage + * every minute if changes are detected. It also provides functionality to restore + * the last saved draft when the form is loaded. + */ + +// Executes when the form loads +function onLoad() { + var autosaveInterval = 60000; // 1 minute + + // Try to restore previous draft + restoreLastDraft(); + + // Set up auto-save interval + setInterval(function() { + if (g_form.isModified()) { + saveDraft(); + } + }, autosaveInterval); +} + +// Saves the current form state as a draft +function saveDraft() { + try { + var draftData = {}; + g_form.serialize(draftData); + + var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); + sessionStorage.setItem(draftKey, JSON.stringify({ + timestamp: new Date().getTime(), + data: draftData + })); + + g_form.addInfoMessage('Draft saved automatically'); + } catch (e) { + console.error('Error saving draft: ' + e); + } +} + +// Restores the last saved draft if available +function restoreLastDraft() { + try { + var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); + var savedDraft = sessionStorage.getItem(draftKey); + + if (savedDraft) { + var draftData = JSON.parse(savedDraft); + var timestamp = new Date(draftData.timestamp); + + // Ask user if they want to restore the draft + if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { + Object.keys(draftData.data).forEach(function(field) { + g_form.setValue(field, draftData.data[field]); + }); + g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); + } else { + // Clear the draft if user chooses not to restore + sessionStorage.removeItem(draftKey); + } + } + } catch (e) { + console.error('Error restoring draft: ' + e); + } +} \ No newline at end of file From 8627b11793c53a83f33a9d64a5a1d22ec1aaf6f7 Mon Sep 17 00:00:00 2001 From: Siddhartha Dasari <143531683+Siddhartha03D@users.noreply.github.com> Date: Sat, 4 Oct 2025 21:07:16 +0530 Subject: [PATCH 2/3] Delete Client-Side Components/Catalog Client Script/Auto Save Draft Feature directory --- .../Auto Save Draft Feature/script.js | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js diff --git a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js deleted file mode 100644 index 89cf714501..0000000000 --- a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Auto-save draft feature for Catalog Client Script - * - * This script automatically saves form data as a draft in the browser's sessionStorage - * every minute if changes are detected. It also provides functionality to restore - * the last saved draft when the form is loaded. - */ - -// Executes when the form loads -function onLoad() { - var autosaveInterval = 60000; // 1 minute - - // Try to restore previous draft - restoreLastDraft(); - - // Set up auto-save interval - setInterval(function() { - if (g_form.isModified()) { - saveDraft(); - } - }, autosaveInterval); -} - -// Saves the current form state as a draft -function saveDraft() { - try { - var draftData = {}; - g_form.serialize(draftData); - - var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); - sessionStorage.setItem(draftKey, JSON.stringify({ - timestamp: new Date().getTime(), - data: draftData - })); - - g_form.addInfoMessage('Draft saved automatically'); - } catch (e) { - console.error('Error saving draft: ' + e); - } -} - -// Restores the last saved draft if available -function restoreLastDraft() { - try { - var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); - var savedDraft = sessionStorage.getItem(draftKey); - - if (savedDraft) { - var draftData = JSON.parse(savedDraft); - var timestamp = new Date(draftData.timestamp); - - // Ask user if they want to restore the draft - if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { - Object.keys(draftData.data).forEach(function(field) { - g_form.setValue(field, draftData.data[field]); - }); - g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); - } else { - // Clear the draft if user chooses not to restore - sessionStorage.removeItem(draftKey); - } - } - } catch (e) { - console.error('Error restoring draft: ' + e); - } -} \ No newline at end of file From 7c4b1498572ece3479a302245c269c56fd78f7c7 Mon Sep 17 00:00:00 2001 From: Siddhartha Dasari <143531683+Siddhartha03D@users.noreply.github.com> Date: Sat, 4 Oct 2025 21:08:02 +0530 Subject: [PATCH 3/3] Added Auto Save Draft --- .../Auto Save Draft Feature/README.md | 114 ++++++++++++++++ .../advanced_implementation.js | 125 ++++++++++++++++++ .../basic_implementation.js | 58 ++++++++ .../Auto Save Draft Feature/script.js | 66 +++++++++ 4 files changed, 363 insertions(+) create mode 100644 Client-Side Components/Catalog Client Script/Auto Save Draft Feature/README.md create mode 100644 Client-Side Components/Catalog Client Script/Auto Save Draft Feature/advanced_implementation.js create mode 100644 Client-Side Components/Catalog Client Script/Auto Save Draft Feature/basic_implementation.js create mode 100644 Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js diff --git a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/README.md b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/README.md new file mode 100644 index 0000000000..9b7aea40d3 --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/README.md @@ -0,0 +1,114 @@ +# Auto Save Draft Feature for Catalog Items + +This snippet provides automatic draft saving functionality for ServiceNow Catalog Items, helping prevent data loss by automatically saving form data at regular intervals. + +## Overview + +The feature includes two implementations: +1. Basic Implementation (`basic_implementation.js`) +2. Advanced Implementation (`advanced_implementation.js`) + +## Basic Implementation + +### Features +- Auto-saves form data every minute +- Stores single draft in sessionStorage +- Provides draft restoration on form load +- Basic error handling and user feedback + +### Usage +```javascript +// Apply in Catalog Client Script +// Select "onLoad" for "Client script runs" +// Copy content from basic_implementation.js +``` + +## Advanced Implementation + +### Enhanced Features +- Multiple draft support (keeps last 3 drafts) +- Advanced draft management +- Draft selection dialog +- Detailed metadata tracking +- Improved error handling +- User-friendly notifications + +### Usage +```javascript +// Apply in Catalog Client Script +// Select "onLoad" for "Client script runs" +// Copy content from advanced_implementation.js +``` + +## Technical Details + +### Dependencies +- ServiceNow Platform UI Framework +- GlideForm API +- GlideModal (advanced implementation only) + +### Browser Support +- Modern browsers with sessionStorage support +- ES5+ compatible + +### Security Considerations +- Uses browser's sessionStorage (cleared on session end) +- No sensitive data transmission +- Instance-specific storage + +## Implementation Guide + +1. Create a new Catalog Client Script: + - Table: Catalog Client Script [catalog_script_client] + - Type: onLoad + - Active: true + +2. Choose implementation: + - For basic needs: Copy `basic_implementation.js` + - For advanced features: Copy `advanced_implementation.js` + +3. Apply to desired Catalog Items: + - Select applicable Catalog Items + - Test in dev environment first + +## Best Practices + +1. Testing: + - Test with various form states + - Verify draft restoration + - Check browser storage limits + +2. Performance: + - Default 60-second interval is recommended + - Adjust based on form complexity + - Monitor browser memory usage + +3. User Experience: + - Clear feedback messages + - Confirmation dialogs + - Error notifications + +## Limitations + +- Browser session dependent +- Storage size limits +- Form field compatibility varies + +## Troubleshooting + +Common issues and solutions: +1. Draft not saving + - Check browser console for errors + - Verify sessionStorage availability + - Check form modification detection + +2. Restoration fails + - Validate stored data format + - Check browser storage permissions + - Verify form field compatibility + +## Version Information + +- Compatible with ServiceNow: Rome and later +- Browser Requirements: Modern browsers with ES5+ support +- Last Updated: October 2025 \ No newline at end of file diff --git a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/advanced_implementation.js b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/advanced_implementation.js new file mode 100644 index 0000000000..8ef56456ec --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/advanced_implementation.js @@ -0,0 +1,125 @@ +/** + * Advanced Auto-save Draft Implementation with Enhanced Features + * This version adds multi-draft support and advanced error handling + */ + +function onLoad() { + var autosaveInterval = 60000; // 1 minute + var maxDrafts = 3; // Maximum number of drafts to keep + + // Initialize draft manager + initializeDraftManager(); + + // Try to restore previous draft + restoreLastDraft(); + + // Set up auto-save interval + setInterval(function() { + if (g_form.isModified()) { + saveAdvancedDraft(); + } + }, autosaveInterval); +} + +function initializeDraftManager() { + window.draftManager = { + maxDrafts: 3, + draftPrefix: 'catalogDraft_' + g_form.getUniqueValue() + '_', + + getAllDrafts: function() { + var drafts = []; + for (var i = 0; i < sessionStorage.length; i++) { + var key = sessionStorage.key(i); + if (key.startsWith(this.draftPrefix)) { + drafts.push({ + key: key, + data: JSON.parse(sessionStorage.getItem(key)) + }); + } + } + return drafts.sort((a, b) => b.data.timestamp - a.data.timestamp); + }, + + cleanup: function() { + var drafts = this.getAllDrafts(); + if (drafts.length > this.maxDrafts) { + drafts.slice(this.maxDrafts).forEach(function(draft) { + sessionStorage.removeItem(draft.key); + }); + } + } + }; +} + +function saveAdvancedDraft() { + try { + var draftData = {}; + g_form.serialize(draftData); + + // Add metadata + var draftKey = window.draftManager.draftPrefix + new Date().getTime(); + var draftInfo = { + timestamp: new Date().getTime(), + data: draftData, + user: g_user.userName, + catalog_item: g_form.getTableName(), + fields_modified: g_form.getModifiedFields() + }; + + sessionStorage.setItem(draftKey, JSON.stringify(draftInfo)); + window.draftManager.cleanup(); + + // Show success message with draft count + var remainingDrafts = window.draftManager.getAllDrafts().length; + g_form.addInfoMessage('Draft saved. You have ' + remainingDrafts + ' saved draft(s).'); + + } catch (e) { + console.error('Error saving draft: ' + e); + g_form.addErrorMessage('Failed to save draft: ' + e.message); + } +} + +function restoreLastDraft() { + try { + var drafts = window.draftManager.getAllDrafts(); + + if (drafts.length > 0) { + // If multiple drafts exist, show selection dialog + if (drafts.length > 1) { + showDraftSelectionDialog(drafts); + } else { + promptToRestoreDraft(drafts[0].data); + } + } + } catch (e) { + console.error('Error restoring draft: ' + e); + g_form.addErrorMessage('Failed to restore draft: ' + e.message); + } +} + +function showDraftSelectionDialog(drafts) { + var dialog = new GlideModal('select_draft_dialog'); + dialog.setTitle('Available Drafts'); + + var html = '
'; + drafts.forEach(function(draft, index) { + var date = new Date(draft.data.timestamp).toLocaleString(); + html += '
'; + html += 'Draft ' + (index + 1) + ' - ' + date; + html += '
Modified fields: ' + draft.data.fields_modified.join(', '); + html += '
'; + }); + html += '
'; + + dialog.renderWithContent(html); +} + +function promptToRestoreDraft(draftInfo) { + var timestamp = new Date(draftInfo.timestamp); + if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { + Object.keys(draftInfo.data).forEach(function(field) { + g_form.setValue(field, draftInfo.data[field]); + }); + g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); + } +} \ No newline at end of file diff --git a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/basic_implementation.js b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/basic_implementation.js new file mode 100644 index 0000000000..8665dff034 --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/basic_implementation.js @@ -0,0 +1,58 @@ +/** + * Basic Auto-save Draft Implementation + * This version provides core functionality for auto-saving catalog item form data + */ + +function onLoad() { + var autosaveInterval = 60000; // 1 minute + + // Try to restore previous draft + restoreLastDraft(); + + // Set up auto-save interval + setInterval(function() { + if (g_form.isModified()) { + saveDraft(); + } + }, autosaveInterval); +} + +function saveDraft() { + try { + var draftData = {}; + g_form.serialize(draftData); + + var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); + sessionStorage.setItem(draftKey, JSON.stringify({ + timestamp: new Date().getTime(), + data: draftData + })); + + g_form.addInfoMessage('Draft saved automatically'); + } catch (e) { + console.error('Error saving draft: ' + e); + } +} + +function restoreLastDraft() { + try { + var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); + var savedDraft = sessionStorage.getItem(draftKey); + + if (savedDraft) { + var draftData = JSON.parse(savedDraft); + var timestamp = new Date(draftData.timestamp); + + if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { + Object.keys(draftData.data).forEach(function(field) { + g_form.setValue(field, draftData.data[field]); + }); + g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); + } else { + sessionStorage.removeItem(draftKey); + } + } + } catch (e) { + console.error('Error restoring draft: ' + e); + } +} \ No newline at end of file diff --git a/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js new file mode 100644 index 0000000000..89cf714501 --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Auto Save Draft Feature/script.js @@ -0,0 +1,66 @@ +/** + * Auto-save draft feature for Catalog Client Script + * + * This script automatically saves form data as a draft in the browser's sessionStorage + * every minute if changes are detected. It also provides functionality to restore + * the last saved draft when the form is loaded. + */ + +// Executes when the form loads +function onLoad() { + var autosaveInterval = 60000; // 1 minute + + // Try to restore previous draft + restoreLastDraft(); + + // Set up auto-save interval + setInterval(function() { + if (g_form.isModified()) { + saveDraft(); + } + }, autosaveInterval); +} + +// Saves the current form state as a draft +function saveDraft() { + try { + var draftData = {}; + g_form.serialize(draftData); + + var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); + sessionStorage.setItem(draftKey, JSON.stringify({ + timestamp: new Date().getTime(), + data: draftData + })); + + g_form.addInfoMessage('Draft saved automatically'); + } catch (e) { + console.error('Error saving draft: ' + e); + } +} + +// Restores the last saved draft if available +function restoreLastDraft() { + try { + var draftKey = 'catalogDraft_' + g_form.getUniqueValue(); + var savedDraft = sessionStorage.getItem(draftKey); + + if (savedDraft) { + var draftData = JSON.parse(savedDraft); + var timestamp = new Date(draftData.timestamp); + + // Ask user if they want to restore the draft + if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) { + Object.keys(draftData.data).forEach(function(field) { + g_form.setValue(field, draftData.data[field]); + }); + g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString()); + } else { + // Clear the draft if user chooses not to restore + sessionStorage.removeItem(draftKey); + } + } + } catch (e) { + console.error('Error restoring draft: ' + e); + } +} \ No newline at end of file