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>