function createContentModal({ openBookmarkPicker, getShortcutBookmarkStatus, ACCOUNT_PLANS_MODAL_ID, AUTH_STATUS, CUSTOMER_FILTER_BUTTON_ID, CUSTOMER_FILTER_LIST_ID, CUSTOMER_FILTER_PANEL_ID, CUSTOMER_FILTER_SEARCH_ID, CUSTOMER_FILTER_SELECT_ALL_ID, DEBUG_PANEL_ID, ESCAPE_LISTENER_KEY, MODAL_ID, OPPORTUNITIES_TABLE_ID, OWNER_FILTER_BUTTON_ID, OWNER_FILTER_LIST_ID, OWNER_FILTER_PANEL_ID, OWNER_FILTER_SEARCH_ID, OWNER_FILTER_SELECT_ALL_ID, PERIOD_OPTIONS, PREFERENCES_MODAL_ID, PRODUCTS_CACHE_TIMESTAMP_ID, PRODUCTS_PROGRESS_ID, STAGE_DASHBOARD_ID, STAGE_FILTERS_ID, STATUS_FILTERS_ID, TABLE_SEARCH_ID, applyTheme, authSessionState, closeAccountPlansModal, closeInstallBaseModal, closeServiceRequestsModal, closeOrganizationChart, closeActionForm, closeCustomerFilterPanel, closeOwnerFilterPanel, closePreferencesModal, createSettingsIcon, createRefreshAllButton, createManageTimeButton, createActivityFilterControls, mountOpportunityViews, debugState, ensureExtensionStyles, getCustomerOptions, getOwnerOptions, getSavedOpportunityTypeValues, getSavedOpptyProductsCacheHours, getSavedTheme, openPreferencesModal, opportunitiesFilterState, opportunitiesQueryState, preferencesState, renderCustomerFilter, renderCustomerFilterList, renderOpportunitiesTable, renderOwnerFilter, renderOwnerFilterList, renderStageFilterButtons, renderStatusFilterButtons, requestOpportunitiesForPeriod, closeCrmSrPopover, requestTokenRelayOnce, ensureCurrentUser, prepareSrSession, resetCustomerAndOwnerFiltersForPeriod, resetOpportunitiesTable, restoreFilterPreferences, saveFilterPreferences, toggleCustomerFilterPanel, toggleDebugPanel, toggleOwnerFilterPanel, environment }) { const { document, window, setTimeout, clearTimeout, matchMedia } = environment; let authCompletion = null; let bookmarkHintTimer = null; let bookmarkHintVersion = 0; function hideBookmarkHint() { bookmarkHintVersion++; if (bookmarkHintTimer !== null) clearTimeout(bookmarkHintTimer); bookmarkHintTimer = null; const hint = document.getElementById("opportunities-extension-bookmark-hint"); if (hint) hint.hidden = true; } async function refreshBookmarkHint() { hideBookmarkHint(); const version = bookmarkHintVersion; try { const result = await getShortcutBookmarkStatus?.(); const modal = document.getElementById(MODAL_ID); const hint = document.getElementById("opportunities-extension-bookmark-hint"); if (version !== bookmarkHintVersion || !modal || modal.hidden || !hint || !result?.ok || result.bookmarked) return; hint.hidden = false; bookmarkHintTimer = setTimeout(hideBookmarkHint, 20000); } catch { /* A failed bookmarks check must not interrupt opportunities. */ } } function resetAuthCompletion() { if (!authCompletion) return; clearTimeout(authCompletion.waitTimer); clearTimeout(authCompletion.hideTimer); authCompletion.badge.setAttribute("data-fading", "false"); authCompletion = null; } function openOpportunitiesModal() { ensureExtensionStyles(); const oldDebug = document.getElementById(DEBUG_PANEL_ID); if (oldDebug) { oldDebug.hidden = true; oldDebug.textContent = ""; } debugState.debugSecretsVisible = false; const existingModal = document.getElementById(MODAL_ID); if (existingModal) { existingModal.hidden = false; refreshBookmarkHint(); prepareSrSession?.(); existingModal.querySelector("select").focus(); document.documentElement.classList.add("opportunities-extension-scroll-lock"); ensureEscapeKeyHandler(); ensureCurrentUser().then(() => { if (existingModal.hidden) return; return requestTokenRelayOnce(); }) .then(() => renderOpportunitiesTable()).catch(() => {}); return; } const overlay = document.createElement("div"); overlay.setAttribute("lang", "en"); authSessionState.tokenRelayRequest = null; authSessionState.consumerTokenServiceRequest = null; authSessionState.accessToken = ""; authSessionState.principal = ""; opportunitiesQueryState.periodRequestVersion = 0; authSessionState.authStatus = AUTH_STATUS.idle; resetOpportunitiesTable(); preferencesState.selectedOpportunityTypeValues = new Set(getSavedOpportunityTypeValues()); preferencesState.opptyProductsCacheHours = getSavedOpptyProductsCacheHours(); overlay.id = MODAL_ID; overlay.className = "opportunities-extension-modal"; overlay.setAttribute("data-theme", getSavedTheme()); overlay.setAttribute("role", "dialog"); overlay.setAttribute("aria-modal", "true"); overlay.setAttribute("aria-labelledby", "opportunities-extension-title"); const shell = document.createElement("section"); shell.className = "opportunities-extension-shell"; const header = document.createElement("header"); header.className = "opportunities-extension-header"; const title = document.createElement("h1"); title.id = "opportunities-extension-title"; title.textContent = "Opportunities Extension"; const authBadge = document.createElement("span"); authBadge.className = "opportunities-extension-auth-badge opportunities-extension-auth-badge-pending"; authBadge.setAttribute("data-auth-status", AUTH_STATUS.pending); authBadge.textContent = "Authenticating"; const subtitleRow = document.createElement("div"); subtitleRow.className = "opportunities-extension-subtitle-row"; const productsProgressBadge = document.createElement("span"); productsProgressBadge.id = PRODUCTS_PROGRESS_ID; productsProgressBadge.className = "opportunities-extension-products-progress"; productsProgressBadge.setAttribute("role", "status"); productsProgressBadge.hidden = true; const productsCacheTimestamp = document.createElement("span"); productsCacheTimestamp.id = PRODUCTS_CACHE_TIMESTAMP_ID; productsCacheTimestamp.className = "opportunities-extension-products-cache-timestamp"; productsCacheTimestamp.setAttribute("role", "status"); productsCacheTimestamp.hidden = true; const bookmarkHint = document.createElement("span"); bookmarkHint.id = "opportunities-extension-bookmark-hint"; bookmarkHint.className = "opportunities-extension-products-cache-timestamp"; bookmarkHint.setAttribute("role", "status"); bookmarkHint.textContent = "Save to favorites: Ctrl+Shift+F"; bookmarkHint.hidden = true; subtitleRow.append(productsCacheTimestamp, bookmarkHint, authBadge, productsProgressBadge); const titleBlock = document.createElement("div"); titleBlock.className = "opportunities-extension-title-row"; titleBlock.append(title, subtitleRow); const closeButton = document.createElement("button"); closeButton.type = "button"; closeButton.className = "opportunities-extension-icon-button"; closeButton.setAttribute("aria-label", "Close"); closeButton.textContent = "X"; closeButton.addEventListener("click", closeOpportunitiesModal); const themeButton = document.createElement("button"); themeButton.type = "button"; themeButton.className = "opportunities-extension-icon-button opportunities-extension-theme-toggle"; themeButton.addEventListener("click", () => { const nextTheme = overlay.getAttribute("data-theme") === "dark" ? "light" : "dark"; applyTheme(overlay, themeButton, nextTheme); }); applyTheme(overlay, themeButton, overlay.getAttribute("data-theme")); const preferencesButton = document.createElement("button"); preferencesButton.type = "button"; preferencesButton.className = "opportunities-extension-icon-button"; preferencesButton.setAttribute("aria-label", "Open preferences"); preferencesButton.title = "Preferences"; preferencesButton.append(createSettingsIcon()); preferencesButton.addEventListener("click", openPreferencesModal); const headerActions = document.createElement("div"); headerActions.className = "opportunities-extension-header-actions"; headerActions.append(themeButton, createRefreshAllButton(() => select.value), createManageTimeButton(), preferencesButton, closeButton); header.append(titleBlock, headerActions); const body = document.createElement("main"); body.className = "opportunities-extension-body"; const form = document.createElement("form"); form.className = "opportunities-extension-form"; const field = document.createElement("label"); field.className = "opportunities-extension-field"; const labelText = document.createElement("span"); labelText.textContent = "Reference"; const selectWrap = document.createElement("span"); selectWrap.className = "opportunities-extension-select-wrap"; const select = document.createElement("select"); select.name = "opportunityPeriod"; PERIOD_OPTIONS.forEach((optionLabel) => { const option = document.createElement("option"); option.value = optionLabel; option.textContent = optionLabel; select.append(option); }); select.addEventListener("change", () => { resetCustomerAndOwnerFiltersForPeriod(); saveFilterPreferences(select.value); requestOpportunitiesForPeriod(select.value); }); selectWrap.append(select); field.append(labelText, selectWrap); restoreFilterPreferences(select); const stageFilter = document.createElement("section"); stageFilter.className = "opportunities-extension-stage-filter"; const stageFilterLabel = document.createElement("span"); stageFilterLabel.className = "opportunities-extension-stage-filter-label"; stageFilterLabel.textContent = "Stage"; const stageFilters = document.createElement("div"); stageFilters.id = STAGE_FILTERS_ID; stageFilters.className = "opportunities-extension-stage-filter-controls"; stageFilters.setAttribute("role", "group"); stageFilters.setAttribute("aria-label", "Filter by stage"); stageFilter.append(stageFilterLabel, stageFilters); const customerFilter = document.createElement("section"); customerFilter.className = "opportunities-extension-customer-filter"; const customerFilterLabel = document.createElement("span"); customerFilterLabel.className = "opportunities-extension-customer-filter-label"; customerFilterLabel.textContent = "Customer"; const customerFilterButton = document.createElement("button"); customerFilterButton.id = CUSTOMER_FILTER_BUTTON_ID; customerFilterButton.type = "button"; customerFilterButton.className = "opportunities-extension-customer-filter-trigger"; customerFilterButton.setAttribute("aria-expanded", "false"); customerFilterButton.setAttribute("aria-controls", CUSTOMER_FILTER_PANEL_ID); customerFilterButton.addEventListener("click", toggleCustomerFilterPanel); const customerFilterPanel = document.createElement("section"); customerFilterPanel.id = CUSTOMER_FILTER_PANEL_ID; customerFilterPanel.className = "opportunities-extension-customer-filter-panel"; customerFilterPanel.hidden = true; const customerSearchInput = document.createElement("input"); customerSearchInput.id = CUSTOMER_FILTER_SEARCH_ID; customerSearchInput.className = "opportunities-extension-customer-filter-search"; customerSearchInput.type = "search"; customerSearchInput.placeholder = "Search customers"; customerSearchInput.setAttribute("aria-label", "Search customers"); customerSearchInput.addEventListener("input", () => { opportunitiesFilterState.customerSearch = customerSearchInput.value; saveFilterPreferences(); renderCustomerFilterList(); }); const selectAllLabel = document.createElement("label"); selectAllLabel.className = "opportunities-extension-customer-filter-select-all"; const selectAllCheckbox = document.createElement("input"); selectAllCheckbox.id = CUSTOMER_FILTER_SELECT_ALL_ID; selectAllCheckbox.type = "checkbox"; selectAllCheckbox.addEventListener("change", () => { const customerOptions = getCustomerOptions(); opportunitiesFilterState.selectedCustomerKeys = selectAllCheckbox.checked ? new Set(customerOptions.map((customer) => customer.key)) : new Set(); opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); renderOwnerFilter(); renderOpportunitiesTable(); }); const selectAllText = document.createElement("span"); selectAllText.textContent = "Select all"; selectAllLabel.append(selectAllCheckbox, selectAllText); const customerFilterList = document.createElement("div"); customerFilterList.id = CUSTOMER_FILTER_LIST_ID; customerFilterList.className = "opportunities-extension-customer-filter-list"; customerFilterPanel.append(customerSearchInput, selectAllLabel, customerFilterList); customerFilter.append(customerFilterLabel, customerFilterButton, customerFilterPanel); const ownerFilter = document.createElement("section"); ownerFilter.className = "opportunities-extension-customer-filter"; const ownerFilterLabel = document.createElement("span"); ownerFilterLabel.className = "opportunities-extension-customer-filter-label"; ownerFilterLabel.textContent = "Owner"; const ownerFilterButton = document.createElement("button"); ownerFilterButton.id = OWNER_FILTER_BUTTON_ID; ownerFilterButton.type = "button"; ownerFilterButton.className = "opportunities-extension-customer-filter-trigger"; ownerFilterButton.setAttribute("aria-expanded", "false"); ownerFilterButton.setAttribute("aria-controls", OWNER_FILTER_PANEL_ID); ownerFilterButton.addEventListener("click", toggleOwnerFilterPanel); const ownerFilterPanel = document.createElement("section"); ownerFilterPanel.id = OWNER_FILTER_PANEL_ID; ownerFilterPanel.className = "opportunities-extension-customer-filter-panel"; ownerFilterPanel.hidden = true; const ownerSearchInput = document.createElement("input"); ownerSearchInput.id = OWNER_FILTER_SEARCH_ID; ownerSearchInput.className = "opportunities-extension-customer-filter-search"; ownerSearchInput.type = "search"; ownerSearchInput.placeholder = "Search owners"; ownerSearchInput.setAttribute("aria-label", "Search owners"); ownerSearchInput.addEventListener("input", () => { opportunitiesFilterState.ownerSearch = ownerSearchInput.value; saveFilterPreferences(); renderOwnerFilterList(); }); const ownerSelectAllLabel = document.createElement("label"); ownerSelectAllLabel.className = "opportunities-extension-customer-filter-select-all"; const ownerSelectAllCheckbox = document.createElement("input"); ownerSelectAllCheckbox.id = OWNER_FILTER_SELECT_ALL_ID; ownerSelectAllCheckbox.type = "checkbox"; ownerSelectAllCheckbox.addEventListener("change", () => { const ownerOptions = getOwnerOptions(); opportunitiesFilterState.selectedOwnerKeys = ownerSelectAllCheckbox.checked ? new Set(ownerOptions.map((owner) => owner.key)) : new Set(); opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); renderCustomerFilter(); renderOpportunitiesTable(); }); const ownerSelectAllText = document.createElement("span"); ownerSelectAllText.textContent = "Select all"; ownerSelectAllLabel.append(ownerSelectAllCheckbox, ownerSelectAllText); const ownerFilterList = document.createElement("div"); ownerFilterList.id = OWNER_FILTER_LIST_ID; ownerFilterList.className = "opportunities-extension-customer-filter-list"; ownerFilterPanel.append(ownerSearchInput, ownerSelectAllLabel, ownerFilterList); ownerFilter.append(ownerFilterLabel, ownerFilterButton, ownerFilterPanel); const tableSearchField = document.createElement("label"); tableSearchField.className = "opportunities-extension-table-search-field"; const tableSearchLabel = document.createElement("span"); tableSearchLabel.textContent = "Search"; const tableSearchInput = document.createElement("input"); tableSearchInput.id = TABLE_SEARCH_ID; tableSearchInput.type = "search"; tableSearchInput.placeholder = "Search opportunities"; tableSearchInput.setAttribute("aria-label", "Search opportunities"); tableSearchInput.value = opportunitiesFilterState.tableSearch; tableSearchInput.addEventListener("input", () => { opportunitiesFilterState.tableSearch = tableSearchInput.value; saveFilterPreferences(); renderOpportunitiesTable(); }); tableSearchField.append(tableSearchLabel, tableSearchInput); const statusFilter = document.createElement("section"); statusFilter.className = "opportunities-extension-status-filter"; const statusFilterLabel = document.createElement("span"); statusFilterLabel.className = "opportunities-extension-status-filter-label"; statusFilterLabel.textContent = "Status"; const statusFilters = document.createElement("div"); statusFilters.id = STATUS_FILTERS_ID; statusFilters.className = "opportunities-extension-status-filter-controls"; statusFilters.setAttribute("role", "group"); statusFilters.setAttribute("aria-label", "Filter by status"); statusFilter.append(statusFilterLabel, statusFilters); form.addEventListener("submit", (event) => event.preventDefault()); const badgeFilters = createActivityFilterControls(form, statusFilter, stageFilter); form.append(field, customerFilter, ownerFilter, tableSearchField, badgeFilters); body.append(form); const stageDashboard = document.createElement("section"); stageDashboard.id = STAGE_DASHBOARD_ID; stageDashboard.className = "opportunities-extension-stage-dashboard"; stageDashboard.setAttribute("aria-label", "Opportunity amount by stage"); body.append(stageDashboard); const tableSurface = document.createElement("section"); tableSurface.className = "opportunities-extension-table-surface"; const opportunitiesTable = document.createElement("table"); opportunitiesTable.id = OPPORTUNITIES_TABLE_ID; opportunitiesTable.className = "opportunities-extension-table"; opportunitiesTable.setAttribute("data-table-layout", "standard"); opportunitiesTable.setAttribute("aria-label", "Opportunities"); tableSurface.append(opportunitiesTable); mountOpportunityViews(body, tableSurface); const debugPanel = document.createElement("aside"); debugPanel.id = DEBUG_PANEL_ID; debugPanel.className = "opportunities-extension-debug-panel"; debugPanel.hidden = true; body.append(debugPanel); shell.append(header, body); overlay.append(shell); document.body.append(overlay); refreshBookmarkHint(); document.documentElement.classList.add("opportunities-extension-scroll-lock"); ensureEscapeKeyHandler(); renderStageFilterButtons(); renderStatusFilterButtons(); renderCustomerFilter(); renderOwnerFilter(); renderOpportunitiesTable(); requestOpportunitiesForPeriod(select.value); prepareSrSession?.(); select.focus(); } function updateAuthBadge(status) { const badge = document.querySelector(`#${MODAL_ID} .opportunities-extension-auth-badge`); if (!badge) { return; } const badgeStatus = status === AUTH_STATUS.authenticated || status === AUTH_STATUS.unauthenticated ? status : AUTH_STATUS.pending; if (authCompletion && (authCompletion.badge !== badge || badgeStatus !== AUTH_STATUS.authenticated)) resetAuthCompletion(); badge.hidden = Boolean(authCompletion?.hidden); if (badgeStatus === AUTH_STATUS.authenticated && !authCompletion) { const current = { badge, hidden: false, waitTimer: null, hideTimer: null }; authCompletion = current; badge.setAttribute("data-fading", "false"); current.waitTimer = setTimeout(() => { if (authCompletion !== current) return; badge.setAttribute("data-fading", "true"); const reducedMotion = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches; current.hideTimer = setTimeout(() => { if (authCompletion !== current) return; current.hidden = true; badge.hidden = true; }, reducedMotion ? 0 : 300); }, 5000); } badge.setAttribute("data-auth-status", badgeStatus); badge.className = `opportunities-extension-auth-badge opportunities-extension-auth-badge-${badgeStatus}`; badge.textContent = badgeStatus === AUTH_STATUS.authenticated ? "Authenticated" : badgeStatus === AUTH_STATUS.unauthenticated ? "Unauthenticated" : "Authenticating"; } function closeOpportunitiesModal() { if (closeOrganizationChart()) return; hideBookmarkHint(); resetAuthCompletion(); closeCrmSrPopover(); const modal = document.getElementById(MODAL_ID); if (modal) { closePreferencesModal(); closeAccountPlansModal(); closeInstallBaseModal(); closeServiceRequestsModal(); closeActionForm(false); modal.hidden = true; } debugState.debugSecretsVisible = false; document.documentElement.classList.remove("opportunities-extension-scroll-lock"); } function ensureEscapeKeyHandler() { if (window[ESCAPE_LISTENER_KEY]) { return; } window[ESCAPE_LISTENER_KEY] = true; document.addEventListener("keydown", (event) => { const modal = document.getElementById(MODAL_ID); if (!modal || modal.hidden) { return; } const shortcutKey = event.key.toLowerCase(); if (event.ctrlKey && event.shiftKey && !event.altKey && !event.metaKey && (shortcutKey === "d" || shortcutKey === "f")) { event.preventDefault(); event.stopPropagation(); if (event.repeat) return; if (shortcutKey === "d") toggleDebugPanel(); else openBookmarkPicker(); return; } if (event.key === "Escape") { if (closeOrganizationChart()) { event.preventDefault(); return; } if (closeActionForm()) { event.preventDefault(); return; } if (closeServiceRequestsModal()) { event.preventDefault(); return; } if (closeInstallBaseModal()) { event.preventDefault(); return; } const accountPlansModal = document.getElementById(ACCOUNT_PLANS_MODAL_ID); if (accountPlansModal && !accountPlansModal.hidden) { closeAccountPlansModal(); event.preventDefault(); return; } const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID); if (preferencesModal && !preferencesModal.hidden) { closePreferencesModal(); event.preventDefault(); return; } if (closeCustomerFilterPanel()) { event.preventDefault(); return; } if (closeOwnerFilterPanel()) { event.preventDefault(); return; } closeOpportunitiesModal(); } }); document.addEventListener("pointerdown", (event) => { const modal = document.getElementById(MODAL_ID); const panel = document.getElementById(CUSTOMER_FILTER_PANEL_ID); const ownerPanel = document.getElementById(OWNER_FILTER_PANEL_ID); if (!modal || modal.hidden) { return; } if (panel && !panel.hidden) { const customerFilter = panel.closest(".opportunities-extension-customer-filter"); if (customerFilter && !customerFilter.contains(event.target)) { closeCustomerFilterPanel(false); } } if (ownerPanel && !ownerPanel.hidden) { const ownerFilter = ownerPanel.closest(".opportunities-extension-customer-filter"); if (ownerFilter && !ownerFilter.contains(event.target)) { closeOwnerFilterPanel(false); } } }); } return { openOpportunitiesModal, updateAuthBadge, closeOpportunitiesModal, ensureEscapeKeyHandler, hideBookmarkHint, refreshBookmarkHint }; }