Badge indicator showing whether an opportunity has an associated Workload Workbench Action
This commit is contained in:
87
src/features/opportunities/workload-actions.js
Normal file
87
src/features/opportunities/workload-actions.js
Normal file
@@ -0,0 +1,87 @@
|
||||
function createOpportunityWorkloadActions({ authSessionState, requestConsumerTokenServiceOnce, sendRuntimeMessage,
|
||||
appendBackgroundRequestLog, queueProductsTableRender, productsContentState, messageTypes, environment }) {
|
||||
let session = newSession();
|
||||
function newSession() { return { enabled: true, records: new Map(), queue: [], running: 0 }; }
|
||||
function reset(enabled = true) {
|
||||
session.enabled = false;
|
||||
session.queue.length = 0;
|
||||
session = newSession();
|
||||
session.enabled = enabled;
|
||||
}
|
||||
function idsFor(products) {
|
||||
return [...new Set((products?.items || []).map(item => String(item?.RevnId ?? "").trim()).filter(Boolean))];
|
||||
}
|
||||
function loadProducts(products) {
|
||||
if (!session.enabled) return;
|
||||
for (const revnId of idsFor(products)) {
|
||||
if (session.records.has(revnId)) continue;
|
||||
const record = { status: "loading", actions: [], hasNext: false };
|
||||
session.records.set(revnId, record);
|
||||
session.queue.push({ revnId, record });
|
||||
}
|
||||
pump(session);
|
||||
}
|
||||
function reopen() {
|
||||
reset();
|
||||
for (const products of productsContentState.opptyProductsByNumber.values()) loadProducts(products);
|
||||
queueProductsTableRender();
|
||||
}
|
||||
function pump(current) {
|
||||
while (current.enabled && current === session && current.running < 4 && current.queue.length) {
|
||||
const job = current.queue.shift();
|
||||
current.running += 1;
|
||||
run(current, job).finally(() => { current.running -= 1; pump(current); });
|
||||
}
|
||||
}
|
||||
async function run(current, { revnId, record }) {
|
||||
try {
|
||||
const authorizations = await requestConsumerTokenServiceOnce();
|
||||
if (!current.enabled || current !== session) return;
|
||||
const result = await sendRuntimeMessage({ type: messageTypes.requestWlActions, revnId, authorization: authorizations?.workloadActions });
|
||||
if (!current.enabled || current !== session) return;
|
||||
appendBackgroundRequestLog(result?.debugRequests);
|
||||
if (!result?.ok) throw new Error(result?.error || "Unable to load workload actions.");
|
||||
record.actions = result.actions;
|
||||
record.hasNext = result.hasNext;
|
||||
record.status = "ready";
|
||||
} catch (error) {
|
||||
if (!current.enabled || current !== session) return;
|
||||
record.status = "error";
|
||||
record.error = error.message || "Unable to load workload actions.";
|
||||
} finally {
|
||||
if (current.enabled && current === session) queueProductsTableRender();
|
||||
}
|
||||
}
|
||||
function createBadge(optyNumber) {
|
||||
if (!session.enabled || !optyNumber) return null;
|
||||
const products = productsContentState.opptyProductsByNumber.get(String(optyNumber));
|
||||
if (!products) return null;
|
||||
const ids = idsFor(products);
|
||||
const records = ids.map(id => session.records.get(id));
|
||||
const actions = records.flatMap(record => record?.actions || []);
|
||||
const pending = records.some(record => !record || record.status === "loading");
|
||||
const uncertain = products.error || (products.items || []).some(item => !String(item?.RevnId ?? "").trim())
|
||||
|| records.some(record => record?.status === "error" || record?.hasNext);
|
||||
const principal = authSessionState.principal;
|
||||
const owned = Boolean(principal) && actions.some(action => typeof action.owner === "string" && action.owner.trim() === principal);
|
||||
const state = owned ? "matched" : pending ? "loading" : uncertain || !principal ? "error" : "missing";
|
||||
const badge = environment.document.createElement("span");
|
||||
badge.className = "opportunities-extension-crm-sr-badge opportunities-extension-wl-actions-badge";
|
||||
badge.setAttribute("data-state", state);
|
||||
badge.textContent = state === "matched" ? "\u2705 Act" : state === "missing" ? "\u26d4 Act" : state === "loading" ? "Act ..." : "Act ?";
|
||||
badge.title = actions.length ? actions.map(action => [
|
||||
`Action: ${action.name ?? "N/D"}`, `ID: ${action.id ?? "N/D"}`, `Status: ${action.status ?? "N/D"}`,
|
||||
`Owner: ${action.owner || "N/D"}`, `Email: ${action.ownerEmail || "N/D"}`, `Workload: ${action.workloadName || "N/D"}`,
|
||||
`Description: ${action.workloadDescription || "N/D"}`, `Start: ${action.startDate || "N/D"}`, `End: ${action.endDate || "N/D"}`
|
||||
].join("\n")).join("\n\n") : state === "missing" ? "No actions found for this opportunity's products."
|
||||
: state === "loading" ? "Loading workload actions..." : "Unable to confirm workload actions. Check product RevnId and Debug Requests.";
|
||||
if (records.some(record => record?.hasNext)) badge.title += "\nAdditional actions exist; pagination is not enabled.";
|
||||
if (!principal) badge.title += "\nTokenrelay principal is unavailable; ownership cannot be verified.";
|
||||
else if (actions.length && state === "missing") badge.title += "\nNone of the returned actions belong to the signed-in principal.";
|
||||
if (actions.length && (pending || uncertain)) badge.title += "\nSome workloads are still loading or could not be fully checked.";
|
||||
badge.tabIndex = 0;
|
||||
badge.setAttribute("aria-label", badge.title);
|
||||
return badge;
|
||||
}
|
||||
return { reset, reopen, loadProducts, createBadge };
|
||||
}
|
||||
Reference in New Issue
Block a user