Repository: adobecom/unity
Pull request #314: MWPW-167050 Acrobat Batch 1b
Author: login: sanjayms01
Labels: name: acrobat
name: verified
Created: 2025-03-19T15:55:03Z | Merged: 2025-03-28T01:00:02Z

This PR adds the following changes:
- Refactor Acrobat Workflow for scalability
- Config updates for each batch 1b verb
- Modularize the transition screen 

Resolves: [MWPW-167050](https://jira.corp.adobe.com/browse/MWPW-167050)

**Test URLs:**
- Before:
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/delete-pdf-pages?unitylibs=stage
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/add-pages-to-pdf?unitylibs=stage
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/rearrange-pdf?unitylibs=stage
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/extract-pdf-pages?unitylibs=stage
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/request-signature?unitylibs=stage
- After: 
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/delete-pdf-pages?unitylibs=batch1b
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/add-pages-to-pdf?unitylibs=batch1b
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/rearrange-pdf?unitylibs=batch1b
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/extract-pdf-pages?unitylibs=batch1b
    - https://stage--dc--adobecom.hlx.page/acrobat/online/test/request-signature?unitylibs=batch1b



Diff:
diff --git a/unitylibs/core/workflow/workflow-acrobat/action-binder.js b/unitylibs/core/workflow/workflow-acrobat/action-binder.js
index f255d05b7..66d95dff7 100644
--- a/unitylibs/core/workflow/workflow-acrobat/action-binder.js
+++ b/unitylibs/core/workflow/workflow-acrobat/action-binder.js
@@ -7,8 +7,6 @@
 import {
   unityConfig,
   getUnityLibs,
-  createTag,
-  localizeLink,
   priorityLoad,
   loadArea,
   loadImg,
@@ -120,27 +118,41 @@ export default class ActionBinder {
     FILE_TOO_LARGE: 'verb_upload_error_file_too_large_multi',
   };
 
-  constructor(unityEl, workflowCfg, wfblock, canvasArea, actionMap = {}, limits = {}) {
+  static LIMITS_MAP = {
+    fillsign: ['single', 'fillsign'],
+    'compress-pdf': ['hybrid'],
+    'add-comment': ['single'],
+    'number-pages': ['single'],
+    'split-pdf': ['single', 'split-pdf'],
+    'crop-pages': ['single'],
+    'delete-pages': ['single', 'page-limit-500'],
+    'insert-pdf': ['single', 'page-limit-500'],
+    'extract-pages': ['single', 'page-limit-500'],
+    'reorder-pages': ['single', 'page-limit-500'],
+    'sendforsignature': ['single', 'sendforsignature'],
+  };
+
+  constructor(unityEl, workflowCfg, wfblock, canvasArea, actionMap = {}) {
     this.unityEl = unityEl;
     this.workflowCfg = workflowCfg;
     this.block = wfblock;
-    this.actionMap = actionMap;
-    this.limits = limits;
     this.canvasArea = canvasArea;
+    this.actionMap = actionMap;
+    this.limits = {};
     this.operations = [];
     this.acrobatApiConfig = this.getAcrobatApiConfig();
     this.serviceHandler = new ServiceHandler();
     this.uploadHandler = null;
     this.splashScreenEl = null;
+    this.transitionScreen = null;
     this.promiseStack = [];
     this.signedOut = this.isSignedOut();
     this.redirectUrl = '';
     this.redirectWithoutUpload = false;
-    this.LOADER_DELAY = 800;
-    this.LOADER_INCREMENT = 30;
     this.LOADER_LIMIT = 95;
     this.MULTI_FILE = false;
     this.applySignedInSettings();
+    this.initActionListeners = this.initActionListeners.bind(this);
   }
 
   isSignedOut() {
@@ -181,21 +193,11 @@ export default class ActionBinder {
     if (this.workflowCfg.targetCfg.showSplashScreen) {
       parr.push(
         `${getUnityLibs()}/core/styles/splash-screen.css`,
-        `${this.splashFragmentLink}.plain.html`,
       );
     }
     await priorityLoad(parr);
   }
 
-  async getAccountType() {
-    try {
-      return window.adobeIMS.getAccountType();
-    } catch (e) {
-      await this.dispatchErrorToast('verb_upload_error_generic', 500, `Exception raised when getting account type: ${e.message}`, true);
-      return '';
-    }
-  }
-
   async dispatchErrorToast(code, status, info = null, lanaOnly = false, showError = true) {
     if (showError) {
       const errorMessage = code in this.workflowCfg.errors
@@ -225,40 +227,6 @@ export default class ActionBinder {
     this.block.dispatchEvent(new CustomEvent(unityConfig.trackAnalyticsEvent, { detail }));
   }
 
-  updateProgressBar(layer, percentage) {
-    const p = Math.min(percentage, this.LOADER_LIMIT);
-    const spb = layer.querySelector('.spectrum-ProgressBar');
-    spb?.setAttribute('value', p);
-    spb?.setAttribute('aria-valuenow', p);
-    layer.querySelector('.spectrum-ProgressBar-percentage').innerHTML = `${p}%`;
-    layer.querySelector('.spectrum-ProgressBar-fill').style.width = `${p}%`;
-  }
-
-  createProgressBar() {
-    const pdom = `<div class="spectrum-ProgressBar spectrum-ProgressBar--sizeM spectrum-ProgressBar--sideLabel" value="0" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
-    <div class="spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-ProgressBar-label"></div>
-    <div class="spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-ProgressBar-percentage">0%</div>
-    <div class="spectrum-ProgressBar-track">
-      <div class="spectrum-ProgressBar-fill" style="width: 0%;"></div>
-    </div>
-    </div>`;
-    return createTag('div', { class: 'progress-holder' }, pdom);
-  }
-
-  progressBarHandler(s, delay, i, initialize = false) {
-    if (!s) return;
-    delay = Math.min(delay + 100, 2000);
-    i = Math.max(i - 5, 5);
-    const progressBar = s.querySelector('.spectrum-ProgressBar');
-    if (!initialize && progressBar?.getAttribute('value') >= this.LOADER_LIMIT) return;
-    if (initialize) this.updateProgressBar(s, 0);
-    setTimeout(() => {
-      const v = initialize ? 0 : parseInt(progressBar.getAttribute('value'), 10);
-      this.updateProgressBar(s, v + i);
-      this.progressBarHandler(s, delay, i);
-    }, delay);
-  }
-
   isMixedFileTypes(files) {
     const fileTypes = new Set(files.map((file) => file.type));
     return fileTypes.size > 1 ? 'mixed' : files[0].type;
@@ -354,7 +322,9 @@ export default class ActionBinder {
         this.redirectUrl = response.url;
       })
       .catch(async (e) => {
-        await this.showSplashScreen();
+        const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+        this.transitionScreen = new TransitionScreen(this.splashScreenEl, this.initActionListeners, this.LOADER_LIMIT, this.workflowCfg);
+        await this.transitionScreen.showSplashScreen();
         await this.dispatchErrorToast('verb_upload_error_generic', e.status || 500, `Exception thrown when retrieving redirect URL. Message: ${e.message}, Options: ${JSON.stringify(cOpts)}`, false, e.showError);
       });
   }
@@ -396,7 +366,26 @@ export default class ActionBinder {
     else await this.uploadHandler.multiFileUserUpload(sanitizedFiles, filesData);
   }
 
+  async loadVerbLimits(workflowName, keys) {
+    try {
+      const response = await fetch(`${getUnityLibs()}/core/workflow/${workflowName}/limits.json`);
+      if (!response.ok) throw new Error('Error loading verb limits');
+      const limits = await response.json();
+      const combinedLimits = keys.reduce((acc, key) => {
+        if (limits[key]) Object.entries(limits[key]).forEach(([k, v]) => { acc[k] = v; });
+        return acc;
+      }, {});
+      if (!combinedLimits || Object.keys(combinedLimits).length === 0) await this.dispatchErrorToast('verb_upload_error_generic', 500, 'No verb limits found', false);
+      return combinedLimits;
+    } catch (e) {
+      await this.dispatchErrorToast('verb_upload_error_generic', 500, `Exception thrown when loading verb limits: ${e.message}`, false);
+      return {};
+    }
+  }
+
   async processSingleFile(files, eventName) {
+    this.limits = await this.loadVerbLimits(this.workflowCfg.name, ActionBinder.LIMITS_MAP[this.workflowCfg.enabledFeatures[0]]);
+    if (!this.limits || Object.keys(this.limits).length === 0) return;
     if (!files || files.length > this.limits.maxNumFiles) {
       await this.dispatchErrorToast('verb_upload_error_only_accept_one_file');
       return;
@@ -406,31 +395,13 @@ export default class ActionBinder {
     await this.handleSingleFileUpload(file, eventName);
   }
 
-  async fillsign(files, eventName) {
-    await this.processSingleFile(files, eventName);
-  }
-
-  async addComment(files, eventName) {
-    await this.processSingleFile(files, eventName);
-  }
-
-  async numberPages(files, eventName) {
-    await this.processSingleFile(files, eventName);
-  }
-
-  async splitPdf(files, eventName) {
-    await this.processSingleFile(files, eventName);
-  }
-
-  async cropPages(files, eventName) {
-    await this.processSingleFile(files, eventName);
-  }
-
-  async compress(files, totalFileSize, eventName) {
+  async processHybrid(files, totalFileSize, eventName) {
     if (!files) {
       await this.dispatchErrorToast('verb_upload_error_only_accept_one_file');
       return;
     }
+    this.limits = await this.loadVerbLimits(this.workflowCfg.name, ActionBinder.LIMITS_MAP[this.workflowCfg.enabledFeatures[0]]);
+    if (!this.limits || Object.keys(this.limits).length === 0) return;
     const isSingleFile = files.length === 1;
     if (isSingleFile) await this.handleSingleFileUpload(files[0], eventName);
     else await this.handleMultiFileUpload(files, totalFileSize, eventName);
@@ -461,7 +432,9 @@ export default class ActionBinder {
   async continueInApp() {
     if (!this.redirectUrl || !(this.operations.length || this.redirectWithoutUpload)) return;
     this.LOADER_LIMIT = 100;
-    this.updateProgressBar(this.splashScreenEl, 100);
+    const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+    this.transitionScreen = new TransitionScreen(this.transitionScreen.splashScreenEl, this.initActionListeners, this.LOADER_LIMIT, this.workflowCfg);
+    this.transitionScreen.updateProgressBar(this.transitionScreen.splashScreenEl, 100);
     try {
       await this.waitForCookie(2000);
       if (!this.checkCookie()) {
@@ -472,13 +445,15 @@ export default class ActionBinder {
         window.location.href = `${this.redirectUrl}&feedback=${this.multiFileFailure}`;
       } else window.location.href = this.redirectUrl;
     } catch (e) {
-      await this.showSplashScreen();
+      await this.transitionScreen.showSplashScreen();
       await this.dispatchErrorToast('verb_upload_error_generic', 500, `Exception thrown when redirecting to product; ${e.message}`, false, e.showError);
     }
   }
 
   async cancelAcrobatOperation() {
-    await this.showSplashScreen();
+    const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+    this.transitionScreen = new TransitionScreen(this.transitionScreen.splashScreenEl, this.initActionListeners, this.LOADER_LIMIT, this.workflowCfg);
+    await this.transitionScreen.showSplashScreen();
     this.redirectUrl = '';
     this.dispatchAnalyticsEvent('cancel');
     const e = new Error();
@@ -488,44 +463,22 @@ export default class ActionBinder {
     this.promiseStack.unshift(cancelPromise);
   }
 
-  async acrobatActionMaps(values, files, totalFileSize, eventName) {
+  async acrobatActionMaps(value, files, totalFileSize, eventName) {
     await this.handlePreloads();
-    for (const value of values) {
-      switch (true) {
-        case value.actionType === 'fillsign':
-          this.promiseStack = [];
-          await this.fillsign(files, eventName);
-          break;
-        case value.actionType === 'compress':
-          this.promiseStack = [];
-          await this.compress(files, totalFileSize, eventName);
-          break;
-        case value.actionType === 'addcomment':
-          this.promiseStack = [];
-          await this.addComment(files, eventName);
-          break;
-        case value.actionType === 'numberpages':
-          this.promiseStack = [];
-          await this.numberPages(files, eventName);
-          break;
-        case value.actionType === 'splitpdf':
-          this.promiseStack = [];
-          await this.splitPdf(files, eventName);
-          break;
-        case value.actionType === 'croppages':
-          this.promiseStack = [];
-          await this.cropPages(files, eventName);
-          break;
-        case value.actionType === 'continueInApp':
-          await this.continueInApp();
-          break;
-        case value.actionType === 'interrupt':
-          await this.cancelAcrobatOperation();
-          break;
-        default:
-          break;
-      }
+    const uploadType = ActionBinder.LIMITS_MAP[this.workflowCfg.enabledFeatures[0]][0];
+    switch (value) {
+      case 'upload':
+        this.promiseStack = [];
+        if (uploadType === 'single') await this.processSingleFile(files, eventName);
+        else if (uploadType === 'hybrid') await this.processHybrid(files, totalFileSize, eventName);
+        break;
+      case 'interrupt':
+        await this.cancelAcrobatOperation();
+        break;
+      default:
+        break;
     }
+    await this.continueInApp();
   }
 
   extractFiles(e) {
@@ -548,80 +501,28 @@ export default class ActionBinder {
     return { files, totalFileSize };
   }
 
-  async loadSplashFragment() {
-    if (!this.workflowCfg.targetCfg.showSplashScreen) return;
-    this.splashFragmentLink = localizeLink(`${window.location.origin}${this.workflowCfg.targetCfg.splashScreenConfig.fragmentLink}`);
-    const resp = await fetch(`${this.splashFragmentLink}.plain.html`);
-    const html = await resp.text();
-    const doc = new DOMParser().parseFromString(html, 'text/html');
-    const sections = doc.querySelectorAll('body > div');
-    const f = createTag('div', { class: 'fragment splash-loader decorate', style: 'display: none' });
-    f.append(...sections);
-    const splashDiv = document.querySelector(
-      this.workflowCfg.targetCfg.splashScreenConfig.splashScreenParent,
-    );
-    splashDiv.append(f);
-    const img = f.querySelector('img');
-    if (img) loadImg(img);
-    await loadArea(f);
-    this.splashScreenEl = f;
-    return f;
-  }
-
-  async delayedSplashLoader() {
-    let eventListeners = ['mousemove', 'keydown', 'click', 'touchstart'];
-    const interactionHandler = async () => {
-      await this.loadSplashFragment();
-      cleanup(interactionHandler);
-    };
-
-    const timeoutHandler = async () => {
-      await this.loadSplashFragment();
-      cleanup(interactionHandler);
-    };
-
-    // Timeout to load after 8 seconds
-    let timeoutId = setTimeout(timeoutHandler, 8000);
-
-    const cleanup = (handler) => {
-      if (timeoutId) {
-        clearTimeout(timeoutId);
-        timeoutId = null;
-      }
-      if (eventListeners) {
-        eventListeners.forEach((event) => document.removeEventListener(event, handler));
-        eventListeners = null;
-      }
-    };
-    eventListeners.forEach((event) => document.addEventListener(
-      event,
-      interactionHandler,
-      { once: true },
-    ));
-  }
-
   async initActionListeners(b = this.block, actMap = this.actionMap) {
-    for (const [key, values] of Object.entries(actMap)) {
+    for (const [key, value] of Object.entries(actMap)) {
       const el = b.querySelector(key);
       if (!el) return;
       switch (true) {
         case el.nodeName === 'A':
           el.addEventListener('click', async (e) => {
             e.preventDefault();
-            await this.acrobatActionMaps(values);
+            await this.acrobatActionMaps(value);
           });
           break;
         case el.nodeName === 'DIV':
           el.addEventListener('drop', async (e) => {
             e.preventDefault();
             const { files, totalFileSize } = this.extractFiles(e);
-            await this.acrobatActionMaps(values, files, totalFileSize, 'drop');
+            await this.acrobatActionMaps(value, files, totalFileSize, 'drop');
           });
           break;
         case el.nodeName === 'INPUT':
           el.addEventListener('change', async (e) => {
             const { files, totalFileSize } = this.extractFiles(e);
-            await this.acrobatActionMaps(values, files, totalFileSize, 'change');
+            await this.acrobatActionMaps(value, files, totalFileSize, 'change');
             e.target.value = '';
           });
           break;
@@ -629,39 +530,10 @@ export default class ActionBinder {
           break;
       }
     }
-    if (b === this.block) await this.delayedSplashLoader();
-  }
-
-  async handleSplashProgressBar() {
-    const pb = this.createProgressBar();
-    this.splashScreenEl.querySelector('.icon-progress-bar').replaceWith(pb);
-    this.progressBarHandler(this.splashScreenEl, this.LOADER_DELAY, this.LOADER_INCREMENT, true);
-  }
-
-  handleOperationCancel() {
-    const actMap = { 'a.con-button[href*="#_cancel"]': [{ actionType: 'interrupt' }] };
-    this.initActionListeners(this.splashScreenEl, actMap);
-  }
-
-  splashVisibilityController(displayOn) {
-    if (!displayOn) {
-      this.LOADER_LIMIT = 95;
-      this.splashScreenEl.parentElement?.classList.remove('hide-splash-overflow');
-      this.splashScreenEl.classList.remove('show');
-      return;
-    }
-    this.progressBarHandler(this.splashScreenEl, this.LOADER_DELAY, this.LOADER_INCREMENT, true);
-    this.splashScreenEl.classList.add('show');
-    this.splashScreenEl.parentElement?.classList.add('hide-splash-overflow');
-  }
-
-  async showSplashScreen(displayOn = false) {
-    if (!this.splashScreenEl && !this.workflowCfg.targetCfg.showSplashScreen) return;
-    if (this.splashScreenEl.classList.contains('decorate')) {
-      if (this.splashScreenEl.querySelector('.icon-progress-bar')) await this.handleSplashProgressBar();
-      if (this.splashScreenEl.querySelector('a.con-button[href*="#_cancel"]')) this.handleOperationCancel();
-      this.splashScreenEl.classList.remove('decorate');
+    if (b === this.block) {
+      const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+      this.transitionScreen = new TransitionScreen(this.splashScreenEl, this.initActionListeners, this.LOADER_LIMIT, this.workflowCfg);
+      await this.transitionScreen.delayedSplashLoader();
     }
-    this.splashVisibilityController(displayOn);
   }
 }
diff --git a/unitylibs/core/workflow/workflow-acrobat/limits.json b/unitylibs/core/workflow/workflow-acrobat/limits.json
new file mode 100644
index 000000000..4b24707cc
--- /dev/null
+++ b/unitylibs/core/workflow/workflow-acrobat/limits.json
@@ -0,0 +1,80 @@
+{
+  "single": {
+    "maxNumFiles": 1,
+    "maxFileSize": 104857600,
+    "allowedFileTypes": ["application/pdf"]
+  },
+  "hybrid": {
+    "maxFileSize": 2147483648,
+    "allowedFileTypes": [
+      "application/pdf",
+      "application/msword",
+      "application/xml",
+      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+      "application/vnd.ms-powerpoint",
+      "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+      "application/x-tika-ooxml",
+      "application/vnd.ms-excel",
+      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+      "application/x-tika-msworks-spreadsheet",
+      "application/vnd.adobe.form.fillsign",
+      "application/illustrator",
+      "application/rtf",
+      "application/x-indesign",
+      "image/jpeg",
+      "image/png",
+      "image/bmp",
+      "image/gif",
+      "image/vnd.adobe.photoshop",
+      "image/tiff",
+      "message/rfc822",
+      "text/plain"
+    ]
+  },
+  "fillsign": {
+    "pageLimit": {
+      "maxNumPages": 100
+    }
+  },
+  "split-pdf": {
+    "maxFileSize": 1073741824,
+    "pageLimit": {
+      "minNumPages": 2,
+      "maxNumPages": 1500
+    },
+    "signedInallowedFileTypes": [
+      "application/msword",
+      "application/xml",
+      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+      "application/vnd.ms-powerpoint",
+      "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+      "application/x-tika-ooxml",
+      "application/vnd.ms-excel",
+      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+      "application/x-tika-msworks-spreadsheet",
+      "application/vnd.adobe.form.fillsign",
+      "application/illustrator",
+      "application/rtf",
+      "application/x-indesign",
+      "image/jpeg",
+      "image/png",
+      "image/bmp",
+      "image/gif",
+      "image/vnd.adobe.photoshop",
+      "image/tiff",
+      "message/rfc822",
+      "text/plain"
+    ]
+  },
+  "page-limit-500": {
+    "pageLimit": {
+      "maxNumPages": 500
+    }
+  },
+  "sendforsignature": {
+    "maxFileSize": 5242880,
+    "pageLimit": {
+      "maxNumPages": 25
+    }
+  }
+}
diff --git a/unitylibs/core/workflow/workflow-acrobat/target-config.json b/unitylibs/core/workflow/workflow-acrobat/target-config.json
index c314c9973..a4f3386ac 100644
--- a/unitylibs/core/workflow/workflow-acrobat/target-config.json
+++ b/unitylibs/core/workflow/workflow-acrobat/target-config.json
@@ -1,285 +1,17 @@
 {
-  "verb-widget.fillsign": {
-    "type": "pdf",
+  "verb-widget": {
     "selector": ".verb-wrapper",
-    "handler": "render",
     "renderWidget": false,
     "source": ".verb-wrapper .verb-container",
     "target": ".verb-wrapper .verb-container",
-    "limits": {
-      "maxNumFiles": 1,
-      "maxFileSize": 104857600,
-      "pageLimit": {
-        "maxNumPages": 100
-      },
-      "allowedFileTypes": ["application/pdf"]
-    },
-    "showSplashScreen": true,
-    "splashScreenConfig": {
-      "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen",
-      "splashScreenParent": "body"
-    },
-    "actionMap": {
-      ".verb-wrapper": [
-        {
-          "actionType": "fillsign"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ],
-      "#file-upload": [
-        {
-          "actionType": "fillsign"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ]
-    }
-  },
-  "verb-widget.compress-pdf": {
-    "type": "pdf",
-    "selector": ".verb-wrapper",
-    "handler": "render",
-    "renderWidget": false,
-    "source": ".verb-wrapper .verb-container",
-    "target": ".verb-wrapper .verb-container",
-    "limits": {
-      "maxFileSize": 2147483648,
-      "allowedFileTypes": [
-        "application/pdf",
-        "application/msword",
-        "application/xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-        "application/vnd.ms-powerpoint",
-        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-        "application/x-tika-ooxml",
-        "application/vnd.ms-excel",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-        "application/x-tika-msworks-spreadsheet",
-        "application/vnd.adobe.form.fillsign",
-        "application/illustrator",
-        "application/rtf",
-        "application/x-indesign",
-        "image/jpeg",
-        "image/png",
-        "image/bmp",
-        "image/gif",
-        "image/vnd.adobe.photoshop",
-        "image/tiff",
-        "message/rfc822",
-        "text/plain"
-      ]
-    },
-    "showSplashScreen": true,
-    "splashScreenConfig": {
-      "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen",
-      "splashScreenParent": "body"
-    },
-    "actionMap": {
-      ".verb-wrapper": [
-        {
-          "actionType": "compress"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ],
-      "#file-upload": [
-        {
-          "actionType": "compress"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ]
-    }
-  },
-  "verb-widget.add-comment": {
-    "type": "pdf",
-    "selector": ".verb-wrapper",
-    "handler": "render",
-    "renderWidget": false,
-    "source": ".verb-wrapper .verb-container",
-    "target": ".verb-wrapper .verb-container",
-    "limits": {
-      "maxNumFiles": 1,
-      "maxFileSize": 104857600,
-      "allowedFileTypes": ["application/pdf"]
-    },
-    "showSplashScreen": true,
-    "splashScreenConfig": {
-      "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen",
-      "splashScreenParent": "body"
-    },
-    "actionMap": {
-      ".verb-wrapper": [
-        {
-          "actionType": "addcomment"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ],
-      "#file-upload": [
-        {
-          "actionType": "addcomment"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ]
-    }
-  },
-  "verb-widget.split-pdf": {
-    "type": "pdf",
-    "selector": ".verb-wrapper",
-    "handler": "render",
-    "renderWidget": false,
-    "source": ".verb-wrapper .verb-container",
-    "target": ".verb-wrapper .verb-container",
-    "limits": {
-      "maxNumFiles": 1,
-      "maxFileSize": 1073741824,
-      "pageLimit": {
-        "minNumPages": 2,
-        "maxNumPages": 1500
-      },
-      "allowedFileTypes": ["application/pdf"],
-      "signedInallowedFileTypes": [
-        "application/msword",
-        "application/xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-        "application/vnd.ms-powerpoint",
-        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-        "application/x-tika-ooxml",
-        "application/vnd.ms-excel",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-        "application/x-tika-msworks-spreadsheet",
-        "application/vnd.adobe.form.fillsign",
-        "application/illustrator",
-        "application/rtf",
-        "application/x-indesign",
-        "image/jpeg",
-        "image/png",
-        "image/bmp",
-        "image/gif",
-        "image/vnd.adobe.photoshop",
-        "image/tiff",
-        "message/rfc822",
-        "text/plain"
-      ]
-    },
-    "showSplashScreen": true,
-    "splashScreenConfig": {
-      "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen",
-      "splashScreenParent": "body"
-    },
-    "actionMap": {
-      ".verb-wrapper": [
-        {
-          "actionType": "splitpdf"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ],
-      "#file-upload": [
-        {
-          "actionType": "splitpdf"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ]
-    }
-  },
-  "verb-widget.number-pages": {
-    "type": "pdf",
-    "selector": ".verb-wrapper",
-    "handler": "render",
-    "renderWidget": false,
-    "source": ".verb-wrapper .verb-container",
-    "target": ".verb-wrapper .verb-container",
-    "limits": {
-      "maxNumFiles": 1,
-      "maxFileSize": 104857600,
-      "allowedFileTypes": ["application/pdf"]
-    },
-    "showSplashScreen": true,
-    "splashScreenConfig": {
-      "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen",
-      "splashScreenParent": "body"
-    },
-    "actionMap": {
-      ".verb-wrapper": [
-        {
-          "actionType": "numberpages"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ],
-      "#file-upload": [
-        {
-          "actionType": "numberpages"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ]
-    }
-  },
-  "verb-widget.crop-pages": {
-    "type": "pdf",
-    "selector": ".verb-wrapper",
-    "handler": "render",
-    "renderWidget": false,
-    "source": ".verb-wrapper .verb-container",
-    "target": ".verb-wrapper .verb-container",
-    "limits": {
-      "maxNumFiles": 1,
-      "maxFileSize": 104857600,
-      "allowedFileTypes": ["application/pdf"]
-    },
     "showSplashScreen": true,
     "splashScreenConfig": {
       "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen",
       "splashScreenParent": "body"
     },
     "actionMap": {
-      ".verb-wrapper": [
-        {
-          "actionType": "croppages"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ],
-      "#file-upload": [
-        {
-          "actionType": "croppages"
-        },
-        {
-          "actionType": "continueInApp"
-        }
-      ]
-    }
-  },
-  "marquee": {
-    "type": "pdf",
-    "selector": ".action-area",
-    "handler": "render",
-    "renderWidget": false,
-    "source": ".action-area .con-button",
-    "target": ".action-area .con-button",
-    "actionMap": {
-      ".action-area .con-button": [
-        {
-          "actionType": "upload"
-        }
-      ]
+      ".verb-wrapper": "upload",
+      "#file-upload": "upload"
     }
   }
-}
+}
\ No newline at end of file
diff --git a/unitylibs/core/workflow/workflow-acrobat/upload-handler.js b/unitylibs/core/workflow/workflow-acrobat/upload-handler.js
index c0dc7e888..128e24bdd 100644
--- a/unitylibs/core/workflow/workflow-acrobat/upload-handler.js
+++ b/unitylibs/core/workflow/workflow-acrobat/upload-handler.js
@@ -141,7 +141,9 @@ export default class UploadHandler {
           await this.actionBinder.dispatchErrorToast('verb_upload_error_generic', 500, `Unexpected response from finalize call: ${assetData.id}, ${JSON.stringify(finalizeJson)}`);
           return false;
         }
-        await this.actionBinder.showSplashScreen();
+        const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+        this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+        await this.transitionScreen.showSplashScreen();
         await this.actionBinder.dispatchErrorToast('verb_upload_error_generic', 500, `Unexpected response from finalize call: ${assetData.id}, ${JSON.stringify(finalizeJson)}`);
         this.actionBinder.operations = [];
         return false;
@@ -151,7 +153,9 @@ export default class UploadHandler {
         await this.actionBinder.dispatchErrorToast('verb_upload_error_generic', e.status || 500, `Exception thrown when verifying content: ${e.message}, ${assetData.id}`, false, e.showError);
         return false;
       }
-      await this.actionBinder.showSplashScreen();
+      const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+      this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+      await this.transitionScreen.showSplashScreen();
       await this.actionBinder.dispatchErrorToast('verb_upload_error_generic', e.status || 500, `Exception thrown when verifying content: ${e.message}, ${assetData.id}`, false, e.showError);
       this.actionBinder.operations = [];
       return false;
@@ -172,7 +176,9 @@ export default class UploadHandler {
           if (this.actionBinder?.limits?.pageLimit?.maxNumPages
             && metadata.numPages > this.actionBinder.limits.pageLimit.maxNumPages
           ) {
-            await this.actionBinder.showSplashScreen();
+            const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+            this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+            await this.transitionScreen.showSplashScreen();
             await this.actionBinder.dispatchErrorToast('verb_upload_error_max_page_count');
             resolve(true);
             return;
@@ -180,7 +186,9 @@ export default class UploadHandler {
           if (this.actionBinder?.limits?.pageLimit?.minNumPages
             && metadata.numPages < this.actionBinder.limits.pageLimit.minNumPages
           ) {
-            await this.actionBinder.showSplashScreen();
+            const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+            this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+            await this.transitionScreen.showSplashScreen();
             await this.actionBinder.dispatchErrorToast('verb_upload_error_min_page_count');
             resolve(true);
             return;
@@ -210,7 +218,9 @@ export default class UploadHandler {
         }, totalDuration);
       });
     } catch (e) {
-      await this.actionBinder.showSplashScreen();
+      const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+      this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+      await this.transitionScreen.showSplashScreen();
       await this.actionBinder.dispatchErrorToast('verb_upload_error_generic', e.status || 500, `Exception thrown when verifying PDF page count; ${e.message}`, false, e.showError);
       this.actionBinder.operations = [];
       return false;
@@ -236,7 +246,9 @@ export default class UploadHandler {
 
   async dispatchGenericError(info = null, showError = true) {
     this.actionBinder.operations = [];
-    await this.actionBinder.showSplashScreen();
+    const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+    this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+    await this.transitionScreen.showSplashScreen();
     await this.actionBinder.dispatchErrorToast('verb_upload_error_generic', 500, info, false, showError);
   }
 
@@ -334,8 +346,10 @@ export default class UploadHandler {
   }
 
   async singleFileGuestUpload(file, fileData) {
+    const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+    this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
     try {
-      await this.actionBinder.showSplashScreen(true);
+      await this.transitionScreen.showSplashScreen(true);
       if (this.isNonPdf([file])) {
         await this.actionBinder.delay(3000);
         const redirectSuccess = await this.actionBinder.handleRedirect(this.getGuestConnPayload('nonpdf'));
@@ -345,18 +359,20 @@ export default class UploadHandler {
       }
       await this.uploadSingleFile(file, fileData);
     } catch (e) {
-      await this.actionBinder.showSplashScreen();
+      await this.transitionScreen.showSplashScreen();
       this.actionBinder.operations = [];
       await this.handleUploadError(e);
     }
   }
 
   async singleFileUserUpload(file, fileData) {
+    const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+    this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
     try {
-      await this.actionBinder.showSplashScreen(true);
+      await this.transitionScreen.showSplashScreen(true);
       await this.uploadSingleFile(file, fileData, this.isNonPdf([file]));
     } catch (e) {
-      await this.actionBinder.showSplashScreen();
+      await this.transitionScreen.showSplashScreen();
       this.actionBinder.operations = [];
       await this.handleUploadError(e);
     }
@@ -387,7 +403,10 @@ export default class UploadHandler {
       return;
     }
     this.actionBinder.LOADER_LIMIT = 75;
-    this.actionBinder.updateProgressBar(this.actionBinder.splashScreenEl, 75);
+
+    const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+    this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+    this.transitionScreen.updateProgressBar(this.actionBinder.transitionScreen.splashScreenEl, 75);
     cOpts = {
       targetProduct: this.actionBinder.workflowCfg.productName,
       assetId: assetDataArray[0].id,
@@ -422,15 +441,17 @@ export default class UploadHandler {
     if (allVerified === 0) return;
     if (files.length !== allVerified) this.actionBinder.multiFileFailure = 'uploaderror';
     this.actionBinder.LOADER_LIMIT = 95;
-    this.actionBinder.updateProgressBar(this.actionBinder.splashScreenEl, 95);
+    this.transitionScreen.updateProgressBar(this.actionBinder.transitionScreen.splashScreenEl, 95);
   }
 
   async multiFileGuestUpload(filesData) {
     try {
-      await this.actionBinder.showSplashScreen(true);
+      const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+      this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT, this.actionBinder.workflowCfg);
+      await this.transitionScreen.showSplashScreen(true);
       await this.actionBinder.delay(3000);
       this.actionBinder.LOADER_LIMIT = 85;
-      this.actionBinder.updateProgressBar(this.actionBinder.splashScreenEl, 85);
+      this.transitionScreen.updateProgressBar(this.actionBinder.transitionScreen.splashScreenEl, 85);
       const redirectSuccess = await this.actionBinder.handleRedirect(this.getGuestConnPayload('multifile'));
       if (!redirectSuccess) return;
       this.actionBinder.redirectWithoutUpload = true;
@@ -442,7 +463,9 @@ export default class UploadHandler {
 
   async multiFileUserUpload(files, filesData) {
     try {
-      await this.actionBinder.showSplashScreen(true);
+      const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`);
+      this.transitionScreen = new TransitionScreen(this.actionBinder.transitionScreen.splashScreenEl, this.actionBinder.initActionListeners, this.actionBinder.LOADER_LIMIT);
+      await this.transitionScreen.showSplashScreen(true);
       await this.uploadMultiFile(files, filesData);
     } catch (e) {
       await this.dispatchGenericError(`Exception raised when uploading multiple files for a signed-in user; ${e.message}, Files data: ${JSON.stringify(filesData)}`, e.showError);
diff --git a/unitylibs/core/workflow/workflow.js b/unitylibs/core/workflow/workflow.js
index 8954721ab..96629e51f 100644
--- a/unitylibs/core/workflow/workflow.js
+++ b/unitylibs/core/workflow/workflow.js
@@ -176,7 +176,6 @@ class WfInitiator {
     } else {
       this.actionMap = this.targetConfig.actionMap;
     }
-    this.limits = this.targetConfig.limits;
     const { default: ActionBinder } = await import(`${getUnityLibs()}/core/workflow/${this.workflowCfg.name}/action-binder.js`);
     await new ActionBinder(
       this.el,
@@ -184,7 +183,6 @@ class WfInitiator {
       this.targetBlock,
       this.interactiveArea,
       this.actionMap,
-      this.limits,
     ).initActionListeners();
   }
 
@@ -273,6 +271,11 @@ class WfInitiator {
           'number-pages',
           'split-pdf',
           'crop-pages',
+          'delete-pages',
+          'insert-pdf',
+          'extract-pages',
+          'reorder-pages',
+          'sendforsignature'
         ]),
       },
       'workflow-ai': {
diff --git a/unitylibs/scripts/transition-screen.js b/unitylibs/scripts/transition-screen.js
new file mode 100644
index 000000000..e3960840d
--- /dev/null
+++ b/unitylibs/scripts/transition-screen.js
@@ -0,0 +1,136 @@
+import {
+  createTag,
+  localizeLink,
+  loadImg,
+  loadArea,
+} from '../scripts/utils.js';
+
+export default class TransitionScreen {
+  constructor(splashScreenEl, initActionListeners, loaderLimit, workflowCfg) {
+    this.splashScreenEl = splashScreenEl;
+    this.initActionListeners = initActionListeners;
+    this.LOADER_LIMIT = loaderLimit;
+    this.workflowCfg = workflowCfg;
+    this.LOADER_DELAY = 800;
+    this.LOADER_INCREMENT = 30;
+  }
+
+  updateProgressBar(layer, percentage) {
+    const p = Math.min(percentage, this.LOADER_LIMIT);
+    const spb = layer.querySelector('.spectrum-ProgressBar');
+    spb?.setAttribute('value', p);
+    spb?.setAttribute('aria-valuenow', p);
+    layer.querySelector('.spectrum-ProgressBar-percentage').innerHTML = `${p}%`;
+    layer.querySelector('.spectrum-ProgressBar-fill').style.width = `${p}%`;
+  }
+
+  createProgressBar() {
+    const pdom = `<div class="spectrum-ProgressBar spectrum-ProgressBar--sizeM spectrum-ProgressBar--sideLabel" value="0" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
+    <div class="spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-ProgressBar-label"></div>
+    <div class="spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-ProgressBar-percentage">0%</div>
+    <div class="spectrum-ProgressBar-track">
+      <div class="spectrum-ProgressBar-fill" style="width: 0%;"></div>
+    </div>
+    </div>`;
+    return createTag('div', { class: 'progress-holder' }, pdom);
+  }
+
+  progressBarHandler(s, delay, i, initialize = false) {
+    if (!s) return;
+    delay = Math.min(delay + 100, 2000);
+    i = Math.max(i - 5, 5);
+    const progressBar = s.querySelector('.spectrum-ProgressBar');
+    if (!initialize && progressBar?.getAttribute('value') >= this.LOADER_LIMIT) return;
+    if (initialize) this.updateProgressBar(s, 0);
+    setTimeout(() => {
+      const v = initialize ? 0 : parseInt(progressBar.getAttribute('value'), 10);
+      this.updateProgressBar(s, v + i);
+      this.progressBarHandler(s, delay, i);
+    }, delay);
+  }
+
+  async loadSplashFragment() {
+    if (!this.workflowCfg.targetCfg.showSplashScreen) return;
+    this.splashFragmentLink = localizeLink(`${window.location.origin}${this.workflowCfg.targetCfg.splashScreenConfig.fragmentLink}`);
+    const resp = await fetch(`${this.splashFragmentLink}.plain.html`);
+    const html = await resp.text();
+    const doc = new DOMParser().parseFromString(html, 'text/html');
+    const sections = doc.querySelectorAll('body > div');
+    const f = createTag('div', { class: 'fragment splash-loader decorate', style: 'display: none' });
+    f.append(...sections);
+    const splashDiv = document.querySelector(
+      this.workflowCfg.targetCfg.splashScreenConfig.splashScreenParent,
+    );
+    splashDiv.append(f);
+    const img = f.querySelector('img');
+    if (img) loadImg(img);
+    await loadArea(f);
+    this.splashScreenEl = f;
+    return f;
+  }
+
+  async delayedSplashLoader() {
+    let eventListeners = ['mousemove', 'keydown', 'click', 'touchstart'];
+    const interactionHandler = async () => {
+      await this.loadSplashFragment();
+      cleanup(interactionHandler);
+    };
+
+    const timeoutHandler = async () => {
+      await this.loadSplashFragment();
+      cleanup(interactionHandler);
+    };
+
+    // Timeout to load after 8 seconds
+    let timeoutId = setTimeout(timeoutHandler, 8000);
+
+    const cleanup = (handler) => {
+      if (timeoutId) {
+        clearTimeout(timeoutId);
+        timeoutId = null;
+      }
+      if (eventListeners) {
+        eventListeners.forEach((event) => document.removeEventListener(event, handler));
+        eventListeners = null;
+      }
+    };
+    eventListeners.forEach((event) => document.addEventListener(
+      event,
+      interactionHandler,
+      { once: true },
+    ));
+  }
+
+  async handleSplashProgressBar() {
+    const pb = this.createProgressBar();
+    this.splashScreenEl.querySelector('.icon-progress-bar').replaceWith(pb);
+    this.progressBarHandler(this.splashScreenEl, this.LOADER_DELAY, this.LOADER_INCREMENT, true);
+  }
+
+  handleOperationCancel() {
+    const actMap = { 'a.con-button[href*="#_cancel"]': 'interrupt' };
+    this.initActionListeners(this.splashScreenEl, actMap);
+  }
+
+  splashVisibilityController(displayOn) {
+    if (!displayOn) {
+      this.LOADER_LIMIT = 95;
+      this.splashScreenEl.parentElement?.classList.remove('hide-splash-overflow');
+      this.splashScreenEl.classList.remove('show');
+      return;
+    }
+    this.progressBarHandler(this.splashScreenEl, this.LOADER_DELAY, this.LOADER_INCREMENT, true);
+    this.splashScreenEl.classList.add('show');
+    this.splashScreenEl.parentElement?.classList.add('hide-splash-overflow');
+  }
+
+  async showSplashScreen(displayOn = false) {
+    if (!this.splashScreenEl && !this.workflowCfg.targetCfg.showSplashScreen) return;
+    if (this.splashScreenEl.classList.contains('decorate')) {
+      if (this.splashScreenEl.querySelector('.icon-progress-bar')) await this.handleSplashProgressBar();
+      if (this.splashScreenEl.querySelector('a.con-button[href*="#_cancel"]')) this.handleOperationCancel();
+      this.splashScreenEl.classList.remove('decorate');
+    }
+    this.splashVisibilityController(displayOn);
+  }
+}


Reviews:
user: login: arugupta1992
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-20T16:01:24Z
user: login: rohitsahu
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T05:54:11Z
user: login: sanjayms01
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T06:42:00Z
user: login: Ruchika4
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-27T20:27:57Z
user: login: rgclayton
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T23:33:09Z

Review comments:
diff_hunk: @@ -7,11 +7,7 @@
 import {
   unityConfig,
   getUnityLibs,
-  createTag,
-  localizeLink,
   priorityLoad,
-  loadArea,
-  loadImg,
 } from '../../../scripts/utils.js';
 
 class ServiceHandler {
path: unitylibs/core/workflow/workflow-acrobat/action-binder.js
user: login: arugupta1992
body: Network layer should also be extracted out of action-binder.js and made common across the workflows. Could be part of the next refactoring effort.
created_at: 2025-03-20T16:01:23Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,136 @@
+import {
+  createTag,
+  localizeLink,
+  loadImg,
+  loadArea,
+} from '../scripts/utils.js';
+
+export default class TransitionScreen {
+  constructor(splashScreenEl, initActionListeners, loaderLimit, workflowCfg) {
+    this.splashScreenEl = splashScreenEl;
+    this.initActionListeners = initActionListeners;
+    this.LOADER_LIMIT = loaderLimit;
+    this.workflowCfg = workflowCfg;
+    this.LOADER_DELAY = 800;
+    this.LOADER_INCREMENT = 30;
+  }
+
+  updateProgressBar(layer, percentage) {
+    const p = Math.min(percentage, this.LOADER_LIMIT);
+    const spb = layer.querySelector('.spectrum-ProgressBar');
+    spb?.setAttribute('value', p);
+    spb?.setAttribute('aria-valuenow', p);
+    layer.querySelector('.spectrum-ProgressBar-percentage').innerHTML = `${p}%`;
+    layer.querySelector('.spectrum-ProgressBar-fill').style.width = `${p}%`;
+  }
+
+  createProgressBar() {
+    const pdom = `<div class="spectrum-ProgressBar spectrum-ProgressBar--sizeM spectrum-ProgressBar--sideLabel" value="0" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
path: unitylibs/scripts/transition-screen.js
user: login: rohitsahu
body: @sanjayms01 We should keep the UI layer separate from script? For a more modular approach, we can have separate UI, data and script modules. We can discuss more on this.
Given that this is a blocker PR for this release, I am okay to take the refactoring changes separately.
created_at: 2025-03-26T05:54:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,136 @@
+import {
+  createTag,
+  localizeLink,
+  loadImg,
+  loadArea,
+} from '../scripts/utils.js';
+
+export default class TransitionScreen {
+  constructor(splashScreenEl, initActionListeners, loaderLimit, workflowCfg) {
+    this.splashScreenEl = splashScreenEl;
+    this.initActionListeners = initActionListeners;
+    this.LOADER_LIMIT = loaderLimit;
+    this.workflowCfg = workflowCfg;
+    this.LOADER_DELAY = 800;
+    this.LOADER_INCREMENT = 30;
+  }
+
+  updateProgressBar(layer, percentage) {
+    const p = Math.min(percentage, this.LOADER_LIMIT);
+    const spb = layer.querySelector('.spectrum-ProgressBar');
+    spb?.setAttribute('value', p);
+    spb?.setAttribute('aria-valuenow', p);
+    layer.querySelector('.spectrum-ProgressBar-percentage').innerHTML = `${p}%`;
+    layer.querySelector('.spectrum-ProgressBar-fill').style.width = `${p}%`;
+  }
+
+  createProgressBar() {
+    const pdom = `<div class="spectrum-ProgressBar spectrum-ProgressBar--sizeM spectrum-ProgressBar--sideLabel" value="0" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
path: unitylibs/scripts/transition-screen.js
user: login: sanjayms01
body: Sounds good, let's definitely discuss more on this later to see how we can further improve this.
created_at: 2025-03-27T06:42:00Z
author_association: COLLABORATOR

PR comments:
user: login: aem-code-sync[bot]
created_at: 2025-03-19T15:55:07Z
body: <!-- aem-bot-dash -->
Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
In case there are problems, just click a checkbox below to rerun the respective action.

- [ ] <!-- action-psi --> Re-run PSI checks
- [ ] <!-- action-sync --> Re-sync branch

<details>
<summary>Commits</summary>
<!-- commit-history -->

* [cafbd97](https://github.com/adobecom/unity/commit/cafbd9703e22766ec6c3016ad4dc7d83118e5d55) :white_check_mark: (latest)
* [7849b8d](https://github.com/adobecom/unity/commit/7849b8d1f091aa4755c2956f015f7512d918e54f) :white_check_mark:
* [8648c74](https://github.com/adobecom/unity/commit/8648c74c32f88173efc6c6fc690eceb0aee392cb) :white_check_mark:
* [9d65823](https://github.com/adobecom/unity/commit/9d6582389551a31d8c12b981c5f22bf6da2e8b59) :white_check_mark:
* [e2b87ca](https://github.com/adobecom/unity/commit/e2b87caac786c631202b8da03885f41ff4e7021f) :white_check_mark:

<!-- commit-history-end -->
</details>
author_association: NONE
user: login: sigadamvenkata
created_at: 2025-03-27T18:34:51Z
body: we do see file uploads are not working in branch URLs ...getting 'unable to process' error
![{351AEB40-8232-4F89-B4FD-53AB9AA71828}](https://github.com/user-attachments/assets/b03f923c-1fe5-4771-8eba-1dd83899fd71)


author_association: COLLABORATOR
user: login: skumar09
created_at: 2025-03-28T00:59:19Z
body: @sanjayms01 
- Verified all the above pages, including the invalid file format validation, on the feature branch.
- The sign-in feature will be verified on the stage environment.
- I think we can proceed to merge the PR into the stage branch.

cc: @Ruchika4 @sigadamvenkata 
author_association: COLLABORATOR