Files
opportunities-extension/src/features/opportunities/crm-service-requests.js

99 lines
5.3 KiB
JavaScript

function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment }) {
let status = "idle";
let byOpportunity = new Map();
let pending = null;
let generation = 0;
let loadedItems = [];
function reset() { closeSrForm(); closePopover(); generation += 1; pending = null; status = "idle"; byOpportunity = new Map(); loadedItems = []; reportProgress("idle"); }
function load(token, version, cacheOptions = null) {
if (!allowsCrmActivities() || !isCurrentPeriodRequest(version)) return;
if (status === "ready") {
return Promise.resolve(cacheOptions ? activityCache.write("sr", "all", cacheOptions.cachedAt, loadedItems) : undefined).then(() => true);
}
if (!pending) {
const request = run(token, version, generation, cacheOptions);
pending = request;
request.finally(() => { if (pending === request) pending = null; });
}
return pending;
}
async function run(token, version, currentGeneration, cacheOptions) {
const isActive = () => currentGeneration === generation;
status = "loading";
try {
const cached = cacheOptions ? await activityCache.read("sr", "all", cacheOptions.cachedAt) : null;
if (!isActive()) return false;
if (!cached && cacheOptions?.cacheOnly) { status = "idle"; return false; }
let items = cached?.items;
if (!items) {
reportProgress("running", 0);
items = await requestCrmServiceRequest(token, version, isActive, (count, total) => {
if (isActive()) reportProgress("running", count, total);
});
if (items && isActive() && cacheOptions) await activityCache.write("sr", "all", cacheOptions.cachedAt, items);
}
if (!items || !isActive()) return false;
const grouped = new Map();
for (const item of items) {
const number = item?.OpportunityName_Id_c?.OptyNumber;
if (!number) continue;
const key = String(number);
if (!grouped.has(key)) grouped.set(key, []);
grouped.get(key).push(item);
}
byOpportunity = grouped;
loadedItems = items;
status = "ready";
if (!cached) reportProgress("complete", items.length, items.length);
return true;
} catch {
if (isActive()) { status = "error"; reportProgress("error"); }
return false;
}
}
function getSummary(optyNumber) {
if (!allowsCrmActivities() || !optyNumber) return null;
const items = byOpportunity.get(String(optyNumber)) || [];
const hours = items.map(item => item.ReportedHours_c).filter(value =>
(typeof value === "number" || (typeof value === "string" && value.trim() !== "")) && Number.isFinite(Number(value))
).map(Number);
return {
state: status === "ready" ? items.length ? "matched" : "missing" : status,
reportedHours: hours.length === items.length ? hours.reduce((total, value) => total + value, 0) : null,
hasUnderFourHours: status === "ready" && hours.some(value => value < 4)
};
}
function createBadge(optyNumber) {
if (!allowsCrmActivities() || !optyNumber || status === "idle" || status === "loading") return null;
const items = byOpportunity.get(String(optyNumber)) || [];
const badge = environment.document.createElement(items.length && status === "ready" ? "button" : "span");
badge.className = "opportunities-extension-crm-sr-badge";
badge.setAttribute("data-state", status === "error" ? "error" : items.length ? "matched" : "missing");
const summary = getSummary(optyNumber);
const reportedHours = summary.reportedHours !== null
? new Intl.NumberFormat("en-US", { useGrouping: false, maximumFractionDigits: 10 }).format(summary.reportedHours)
: "N/A";
setBadgeContent(badge, "sr", status === "error" ? "?" : items.length ? `${reportedHours}${reportedHours === "N/A" ? "" : "h"}` : "0h");
const display = value => value === null || value === undefined || value === "" ? "N/A" : String(value);
const descriptions = items.map(item => [
`Title: ${display(item.Title)}`,
`Opportunity: ${display(item.OpportunityName_Id_c?.OptyNumber)}`,
`Reported Hours: ${display(item.ReportedHours_c)}`,
`Customer Deadline: ${display(formatDate(item.CustomerDeadline_c))}`,
`Owner: ${[item.Owner?.PersonFirstName, item.Owner?.PersonLastName].filter(Boolean).join(" ") || "N/A"}`,
`Email: ${display(item.Owner?.PrimaryEmail?.EmailAddress)}`,
`Do Not Call: ${display(item.DoNotCallFlag)}`, `Do Not Email: ${display(item.DoNotEmailFlag)}`
].join("\n"));
const description = status === "error" ? "Unable to check CRM service requests. See Debug Requests."
: !items.length ? "No matching CRM service request with status SVC_PENDING_DELIVERY in your SR team."
: items.map((item, index) => `SR Number: ${display(item.SrNumber)}\n${descriptions[index]}`).join("\n\n");
badge.setAttribute("aria-label", description);
badge.title = "Whether a Service Request has been created for the opportunities.";
if (items.length && status === "ready") bindPopover(badge, items, descriptions);
if (!items.length && status === "ready") badge.append(createSrButton(String(optyNumber)));
badge.tabIndex = 0;
return badge;
}
return { reset, load, createBadge, getSummary };
}