function createInstallBaseView({ MODAL_ID, createCloseIcon, formatOracleResponseDate, createSearch, tableSorting, environment }) { const { document } = environment; let active = null; const columns = [ ["productGroupName", "Product Group"], ["productName", "Product"], ["currentSupport", "Current Support", "amount"], ["orderNumber", "Order Number"], ["supportStatus", "Support Status", "status"], ["quantityType", "Quantity Type"], ["quantity", "Quantity", "quantity"], ["supportStartDate", "Support Start Date", "date"], ["supportEndDate", "Support End Date", "date"], ["licenseValue", "License Value", "amount"], ["shipDate", "Ship Date", "date"] ]; function element(tag, className, text) { const node = document.createElement(tag); if (className) node.className = className; if (text !== undefined) node.textContent = text; return node; } function closeInstallBaseModal() { if (!active) return false; const previous = active; active = null; previous.modal.remove(); if (previous.trigger?.isConnected) previous.trigger.focus(); return true; } function formatValue(value, type) { if (value === null || value === undefined || value === "") return "-"; if (type === "date") return formatOracleResponseDate(value); if ((type === "amount" || type === "quantity") && Number.isFinite(Number(value))) { return new Intl.NumberFormat("en-US", { minimumFractionDigits: type === "amount" ? 2 : 0, maximumFractionDigits: type === "amount" ? 2 : 4 }).format(Number(value)); } return String(value).trim(); } const sortColumns = columns.map(([key, , type]) => ({ key, type: ["amount", "quantity"].includes(type) ? "number" : type })); function createTable(items, loading = false, sorting = tableSorting.create("installed-base", sortColumns), onSort) { const table = element("table", "opportunities-extension-install-base-table"); table.setAttribute("data-table-layout", "standard"); table.setAttribute("aria-label", "Installed base items"); const head = element("thead"); const header = element("tr"); const group = element("colgroup"); columns.forEach(([key, label]) => { const col = element("col"); col.className = `install-base-col-${key}`; group.append(col); const th = element("th"); th.scope = "col"; sorting.header(th, key, label, onSort, loading); header.append(th); }); head.append(header); const body = element("tbody"); (loading ? Array.from({ length: 8 }, () => ({})) : sorting.select(items)).forEach(item => { const row = element("tr"); columns.forEach(([key, , type]) => { const cell = element("td"); if (loading) { const bar = element("span", "opportunities-extension-account-plans-skeleton-bar"); bar.setAttribute("aria-hidden", "true"); cell.append(bar); } else { const value = item[key]; const text = formatValue(value, type); if (type === "status") { const badge = element("span", "opportunities-extension-install-base-status", text); badge.dataset.status = text.toUpperCase(); cell.append(badge); } else cell.textContent = text; if (type) cell.dataset.format = type; } row.append(cell); }); body.append(row); }); table.append(group, head, body); return table; } function openInstallBaseModal(opportunity, trigger) { closeInstallBaseModal(); const overlay = document.getElementById(MODAL_ID); if (!overlay || overlay.hidden) return { render() {} }; const modal = element("section", "opportunities-extension-account-plans-modal"); modal.setAttribute("role", "dialog"); modal.setAttribute("aria-modal", "true"); modal.setAttribute("aria-busy", "true"); modal.setAttribute("aria-labelledby", "opportunities-extension-install-base-title"); const dialog = element("div", "opportunities-extension-account-plans-dialog opportunities-extension-install-base-dialog"); const header = element("header", "opportunities-extension-account-plans-header"); const heading = element("div"); const title = element("h2", "", "Installed Base"); title.id = "opportunities-extension-install-base-title"; heading.append(title, element("p", "", opportunity.CustomerAccount?.PartyUniqueName || "Customer")); const close = element("button", "opportunities-extension-icon-button"); close.type = "button"; close.title = "Close"; close.setAttribute("aria-label", "Close installed base"); close.append(createCloseIcon()); close.addEventListener("click", closeInstallBaseModal); header.append(heading, close); const content = element("div", "opportunities-extension-install-base-content"); content.tabIndex = 0; content.setAttribute("role", "region"); content.setAttribute("aria-label", "Installed base results"); const status = element("p", "opportunities-extension-install-base-message", "Loading installed base..."); status.setAttribute("role", "status"); let loadedItems = [], loaded = false, failure = ""; const sorting = tableSorting.create("installed-base", sortColumns); const search = createSearch({ label: "Search installed base", onSearch: renderResults, getValues: item => columns.flatMap(([key, , type]) => [item[key], formatValue(item[key], type)]) }); const toolbar = element("div", "opportunities-extension-customer-table-toolbar"); toolbar.append(search.input); content.append(status, createTable([], true)); dialog.append(header, toolbar, content); modal.append(dialog); overlay.append(modal); active = { modal, trigger }; modal.addEventListener("keydown", event => { if (event.key === "Tab") { event.preventDefault(); const controls = [close, search.input, content, ...content.querySelectorAll("button")].filter(control => !control.disabled); const index = controls.indexOf(document.activeElement); controls[(index + (event.shiftKey ? -1 : 1) + controls.length) % controls.length].focus(); } }); close.focus(); function renderResults() { if (!loaded || active?.modal !== modal || overlay.hidden) return; const items = failure ? [] : search.filter(loadedItems); content.textContent = ""; status.textContent = failure || (loadedItems.length ? `${items.length} of ${loadedItems.length} installed base items${items.length ? "" : ". No matching records."}` : "No installed base items found for this customer."); status.setAttribute("role", failure ? "alert" : "status"); content.append(status); if (!failure && items.length) content.append(createTable(items, false, sorting, key => { renderResults(); content.querySelector(`[data-sort-key="${key}"]`)?.focus(); })); content.scrollTop = 0; } return { render(items, error = "") { // A closed or replaced dialog must never receive a late response. if (active?.modal !== modal || overlay.hidden) return; modal.setAttribute("aria-busy", "false"); loadedItems = items; failure = error; loaded = true; search.reset(); renderResults(); } }; } return { openInstallBaseModal, closeInstallBaseModal, createTable }; }