Forum Discussion

EmberKrumwied's avatar
EmberKrumwied
Qrew Officer
21 days ago
Solved

Code Page Formula Button

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.

 

  • 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.

16 Replies

  • 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);

     

     

  • 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.

    • Denin's avatar
      Denin
      Qrew Cadet

      Change this part:

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

      Like this:

      "&rdr=" & URLEncode( URLRoot() & "db/" & Dbid() & "?a=dbpage&pageID=" & $codePageID & "&rid=" & $recordID & "&initialStatus=" & $currentStatus);

  • 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>

    • Denin's avatar
      Denin
      Qrew Cadet

      The formula wasn't redirecting to the code page after the API request, which is now solved.

      The remaining problems are with the code page.

      First, for domain, are you including "https://" before you put your realm name? The full value should look like "https://myrealm.quickbase.com".

      Then in the code page, you should update the query selector:

      const node = xmlDoc.querySelector(`f[id="${statusFieldId}"]`);

      The XML tree you posted earlier is an XML API response. When you run the polling in the code page, if the API request is successful, it would return a similar response, and you need to parse that to identify when to perform a redirect. What is the field that should be updated to "Completed"? Assuming it's a field called status, then you need to update the query selector line like this:

      const node = xmlDoc.querySelector(`Status`);

      If you right click in the code page, you can go to "Inspect" or "Inspect Element", then select the Network tab and reload the page. Then look for the API request and see what information is sent and what the response is, assuming the request is being sent properly.

      I tested in my builder account and it worked fine with these changes.

  • 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

    • Denin's avatar
      Denin
      Qrew Cadet

      What web browser are you using, and with what extensions?

    • Denin's avatar
      Denin
      Qrew Cadet

      Can you inspect element on the code page, click Network, then reload the page with the Network tab open. Next, in the Network tab, click the first network request and make sure the Headers tab is selected (still under Network). And can you provide a screenshot of that? It will look similar to this:

      Then can you click the Console tab and provide a screenshot of that too? Cover up any sensitive info if there is any, maybe your realm name or table IDs in the URL.

    • Denin's avatar
      Denin
      Qrew Cadet

      Has the API request run when you took the console screenshot? Doesn't seem like there are any issues here. Where do you see the error "Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received"?

  • 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

    • Denin's avatar
      Denin
      Qrew Cadet

      Can you share what it shows under the Payload and Response tabs?

      Also in console does it ever show any other type of errors or only that one?

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

  • 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.