Skip to main content
Solved

Code Page Formula Button

  • September 11, 2025
  • 0 replies
  • 856 views

EmberKrumwied
Forum|alt.badge.img+13

I have a formula button whose intended purpose is to check a box on the record (to start a pipeline) then redirect to a code page that is coded to pause, then check if a status field has changed, if not, pause again. If the field is changed it returns the user to the form originally launched from.

I'm trying to cobble together different posts/search results as I haven't been able to find any direct examples of my need.

This formula works to open the code page and passes the correct parameters for checking the status, but it doesn't check the box and thus the pipeline never runs and the page never closes.

var text codePageID = "6"; // Replace with your actual code page ID
var text recordID = ToText([Record ID#]);
var text currentStatus = URLEncode([Charges Pipeline Status]); // Replace with your field's name

var text url = URLRoot() & "db/" & AppID() & "?a=dbpage&pageid=" & $codePageID & "&rid=" & $recordID & "&initialStatus=" & $currentStatus;

"<a style= \" text-decoration:none; background: #b4b55d; border-radius: 15px; color: #fff; display: inline-block; padding: 10px 10px 10px 10px; width: 100%; font-size: 14px; text-align: center;\" href='" & $url & "'>Create New Charges</a>"

This formula correctly edits the checkbox but doesn't pass the status parameters (I do have the correct app token inserted in the field - replaced with [apptoken] here for reference:

var text urlToExecute = URLRoot() & "db/" & Dbid() & "?a=API_EditRecord&apptoken=[apptoken]"
    & "&rid=" & [Record ID#] & "&_fid_12=true";

"<a style= \" text-decoration:none; background: #b4b55d; border-radius: 15px; color: #fff; display: inline-block; padding: 10px 10px 10px 10px; width: 100%; font-size: 14px; text-align: center;\"  href='" &  AppID() & "?a=dbpage&pageid=3" & "&url=" & URLEncode($urlToExecute) & "'>Create New Charges</a>"

This is my attempt at trying to combine the 2 formulas above:

var text codePageID = "6"; // Replace with your actual code page ID
var text recordID = ToText([Record ID#]);
var text currentStatus = URLEncode([Charges Pipeline Status]); // Replace with your field's name

var text url = URLRoot() & "db/" & Dbid() & "?a=API_EditRecord&apptoken=pamm2bcsqv939bufwq8hwzvwx4"
    & "&rid=" & [Record ID#] & "&_fid_12=1" & AppID() & "?a=dbpage&pageid=" & $codePageID & "&rid=" & $recordID & "&initialStatus=" & $currentStatus;

"<a style= \" text-decoration:none; background: #b4b55d; border-radius: 15px; color: #fff; display: inline-block; padding: 10px 10px 10px 10px; width: 100%; font-size: 14px; text-align: center;\" href='" & $url & "'>Create New Charges</a>"

When I use the third formula, I get an error that says the XML file does not appear to have any style information associated with it and shows the following tree:

<qdbapi>
<action>API_EditRecord</action>
<errcode>0</errcode>
<errtext>No error</errtext>
<rid>7</rid>
<num_fields_changed>0</num_fields_changed>
<update_id>1757601111721</update_id>
</qdbapi>

Here is the script for the code page:

<script>
    // Configuration
    const recordId = new URLSearchParams(window.location.search).get('rid');
    const initialStatus = new URLSearchParams(window.location.search).get('initialStatus');
    const dbid = 'bvcwn2ues'; // <<< REPLACE WITH YOUR TABLE'S DBID
    const appToken = 'pamm2bcsqv939bufwq8hwzvwx4'; // <<< REPLACE WITH YOUR APP TOKEN IF NEEDED
    const statusFieldId = '40'; // <<< REPLACE WITH YOUR STATUS FIELD'S FID
    const checkInterval = 120000; // 120 seconds in milliseconds

    function checkStatus() {
        $.ajax({
            url: window.location.origin + '/db/' + dbid + '?a=API_DoQuery&qid=1&options=csv&apptoken=' + appToken + '&query={' + recordId + '.EX.' + recordId + '}',
            type: 'GET',
            success: function(response) {
                const lines = response.trim().split('\n');
                if (lines.length > 1) {
                    const data = lines[1].split(',');
                    const currentStatus = data[statusFieldId];

                    if (currentStatus !== initialStatus) {
                        // Status has changed! Redirect to the updated record.
                        window.location.href = window.location.origin + '/db/' + dbid + '?a=dr&rid=' + recordId;
                    } else {
                        // Status is the same. Pause and check again.
                        console.log('Status has not changed. Checking again in ' + checkInterval / 120000 + ' seconds.');
                        setTimeout(checkStatus, checkInterval);
                    }
                } else {
                    console.log('Record not found or API error. Retrying...');
                    setTimeout(checkStatus, checkInterval);
                }
            },
            error: function(xhr, status, error) {
                console.error('API Error: ' + status, error);
                setTimeout(checkStatus, checkInterval);
            }
        });
    }

    // Start the check after a brief initial pause
    setTimeout(checkStatus, checkInterval);

</script>

I'm thinking I might need to add another const under the Configuration, but I just don't know enough to determine that.  Or, I'm thinking that the structure of the button formula itself is where adjustments need to be made.

 

Best answer by EmberKrumwied

I appreciate all the assistance, but I have decided to go a different way. This option was to have simplified the mass creation of new records, but the pipeline process could take up to 10 minutes to fully complete, whereas, exporting to excel, making minor adjustments, then reimporting them takes only a minute or two.

MarkShnierYou
Forum|alt.badge.img+24

I cannot help you with code pages, but I do know that in general when you are trying to string together two different URLs, the syntax is this. 

 

$URLONE 
& "&rdr=" & URLEncode($URLTWO)

 

so applying that you your code

var text url = URLRoot() & "db/" & Dbid() & "?a=API_EditRecord&apptoken=pamm2bcsqv939bufwq8hwzvwx4"
    & "&rid=" & [Record ID#] & "&_fid_12=1"

& "&rdr=" & URLEncode(AppID() & "?a=dbpage&pageid=" & $codePageID & "&rid=" & $recordID & "&initialStatus=" & $currentStatus);

 

 


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 11, 2025

Thank you. That change successfully checked the box (which started the pipeline) but I still got the XML code page error. I'll keep digging. Thanks again.


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 12, 2025

So I think I am getting closer, but I am now getting "Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received". Bolded text below is replaced for this post.

Here is the button formula:

var text appToken = "[appToken]";
var text codePageID = "7"; // Replace with your actual code page ID
var text recordID = ToText([Record ID#]);
var text currentStatus = URLEncode([Charges Pipeline Status]); // Replace with your field's name

var text url = URLRoot() & "db/" & Dbid() & "?a=API_EditRecord&apptoken=" & $appToken & "&rid=" & $recordID & "&_fid_12=1&_fid_40=Pipeline Running"
    & "&rdr=" &URLEncode(URLRoot() & "db/" & Dbid() & "?a=dbpage&pageid=" & $codePageID & "&rid=" & $recordID & "&initialStatus=" & $currentStatus);

"<a style= \" text-decoration:none; background: #b4b55d; border-radius: 15px; color: #fff; display: inline-block; padding: 10px 10px 10px 10px; width: 100%; font-size: 14px; text-align: center;\" href='" & $url & "'>Create New Charges</a>"

Here is the code page:

<!DOCTYPE html>
<html>
<head>
  <title>Pipeline Monitor</title>
  <style>
    body { font-family: Arial, sans-serif; text-align:center; margin:50px; }
    #countdown { font-size: 1.5em; margin:20px; }
    #progress-container { width: 80%; margin: 20px auto; background: #eee; border-radius: 10px; height: 25px; display:none; }
    #progress-bar { height: 25px; width: 0; background: #4caf50; border-radius: 10px; }
    #status { margin-top: 20px; font-size: 1.1em; color: #333; }
  </style>
</head>
<body>
  <h2>Creating New Charges...</h2>
  <div id="countdown"></div>
  <div id="progress-container"><div id="progress-bar"></div></div>
  <div id="status"></div>

  <script>
    // --- Config ---
    const maxDuration = 6 * 60 * 1000; // 6 minutes
    const statusFieldId = 40;          // field to watch for changes
    const startTime = Date.now();

    let firstRun = true;
    let initialStatus = "Pipeline Running";
    let statusChecker = null;

    // --- URL Params ---
    const urlParams = new URLSearchParams(window.location.search);
    const recordId = urlParams.get("rid"); // Record ID from URL

    // --- Quickbase ---
    const dbid = "[_DBID]"; // Your table's DBID
    const token = "[appToken]"; // Your app token
    const quickbaseDomain = "[Domain]"; // Your Quickbase domain

    // --- Helpers ---
    function returnToRecord() {
      window.location.href = `${quickbaseDomain}/db/${dbid}?a=dr&rid=${recordId}`;
    }

    function updateStatus(msg) {
      document.getElementById("status").textContent = msg;
    }

    function countdown(seconds, callback) {
      const display = document.getElementById("countdown");
      let remaining = seconds;
      display.style.display = "block";
      display.textContent = `Waiting ${remaining} seconds for pipeline to start...`;
      const timer = setInterval(() => {
        remaining--;
        display.textContent = `Waiting ${remaining} seconds for pipeline to start...`;
        if (remaining <= 0) {
          clearInterval(timer);
          callback();
        }
      }, 1000);
    }

    function progressBar(seconds, callback) {
      const display = document.getElementById("countdown");
      const container = document.getElementById("progress-container");
      const bar = document.getElementById("progress-bar");
      container.style.display = "block";
      bar.style.width = "0%";

      let remaining = seconds;
      const timer = setInterval(() => {
        remaining--;
        display.textContent = `Waiting on pipeline to complete, refreshes occur every ${seconds} seconds, ${remaining} seconds to next refresh...`;
        bar.style.width = ((seconds - remaining) / seconds * 100) + "%";

        if (remaining <= 0) {
          clearInterval(timer);
          callback();
        }
      }, 1000);
    }

    function monitor() {
      if (Date.now() - startTime > maxDuration) {
        updateStatus("Pipeline should be complete and new Charges created. Returning...");
        clearInterval(statusChecker);
        returnToRecord(); // immediate redirect
        return;
      }

      if (firstRun) {
        firstRun = false;
        countdown(30, monitor);
      } else {
        progressBar(90, monitor);
      }
    }

    // --- Status Polling ---
    async function getRecordStatus() {
     const statusValue = 'Completed'; 
     const query = `{3.EX.'${recordId}'}AND{40.EX.'${statusValue}'}`; // fid=3 is Record ID#
      const url = `${quickbaseDomain}/db/${dbid}?a=API_DoQuery&query=${encodeURIComponent(query)}&apptoken=${token}`;
      const res = await fetch(url);
      if (!res.ok) throw new Error("Quickbase API call failed: " + res.status);
      const text = await res.text();
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(text, "text/xml");
      const node = xmlDoc.querySelector(`f[id="${statusFieldId}"]`);
      return node ? node.textContent : null;
    }

    async function checkForStatusChange() {
      try {
        const currentStatus = await getRecordStatus();
        if (initialStatus === "Pipeline Running") {
          initialStatus = currentStatus; // set baseline
          updateStatus("Pipeline status: " + initialStatus);
        } else if (currentStatus !== initialStatus) {
          updateStatus(`Status changed from "${initialStatus}" to "${currentStatus}". Returning...`);
          clearInterval(statusChecker);
          returnToRecord(); // immediate redirect
        }
      } catch (err) {
        console.error("Error fetching record:", err);
      }
    }

    // --- Start process ---
    (async () => {
      if (!recordId) {
        updateStatus("No recordId (?rid=) found in URL.");
        return;
      }

      // get initial status then start monitoring
      initialStatus = await getRecordStatus();
      updateStatus("Pipeline status: " + initialStatus);

      // start status polling every 45s
      statusChecker = setInterval(checkForStatusChange, 45000);

      // start countdown/progress cycle
      monitor();
    })();
  </script>
</body>
</html>


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 12, 2025

Made the changes, confirmed the domain, reran the code page and still getting the:

Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 12, 2025

Chrome, with following extensions...

 


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 15, 2025

Network screenshot

 


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 15, 2025

Console screenshot

 


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 16, 2025

Apologies, in your request I took it to mean to get screenshots of the Network and Console tab for the 1st process ran. The error doesn't occur until later in the process/code.

Here is the Network tab, when the error occurs:

And here is the console tab:

Really appreciate your help. Thanks


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • September 22, 2025

Here are the updated screen shots. There are no other errors that I can tell.


EmberKrumwied
Forum|alt.badge.img+13
  • Author
  • Registered
  • Answer
  • September 25, 2025

I appreciate all the assistance, but I have decided to go a different way. This option was to have simplified the mass creation of new records, but the pipeline process could take up to 10 minutes to fully complete, whereas, exporting to excel, making minor adjustments, then reimporting them takes only a minute or two.