534 lines
20 KiB
JavaScript
534 lines
20 KiB
JavaScript
function createContentModal({
|
|
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,
|
|
closeCustomerFilterPanel,
|
|
closeOwnerFilterPanel,
|
|
closePreferencesModal,
|
|
createSettingsIcon,
|
|
debugState,
|
|
ensureExtensionStyles,
|
|
getCustomerOptions,
|
|
getOwnerOptions,
|
|
getSavedOpportunityTypeValues,
|
|
getSavedOpptyProductsCacheHours,
|
|
getSavedTheme,
|
|
openPreferencesModal,
|
|
opportunitiesFilterState,
|
|
opportunitiesQueryState,
|
|
preferencesState,
|
|
renderCustomerFilter,
|
|
renderCustomerFilterList,
|
|
renderOpportunitiesTable,
|
|
renderOwnerFilter,
|
|
renderOwnerFilterList,
|
|
renderStageFilterButtons,
|
|
renderStatusFilterButtons,
|
|
requestOpportunitiesForPeriod,
|
|
requestTokenRelayOnce,
|
|
resetCustomerAndOwnerFiltersForPeriod,
|
|
resetOpportunitiesTable,
|
|
restoreFilterPreferences,
|
|
saveFilterPreferences,
|
|
toggleCustomerFilterPanel,
|
|
toggleDebugPanel,
|
|
toggleOwnerFilterPanel,
|
|
environment
|
|
}) {
|
|
const { document, window } = environment;
|
|
function openOpportunitiesModal() {
|
|
ensureExtensionStyles();
|
|
debugState.debugSecretsVisible = false;
|
|
|
|
const existingModal = document.getElementById(MODAL_ID);
|
|
|
|
if (existingModal) {
|
|
existingModal.hidden = false;
|
|
existingModal.querySelector("select").focus();
|
|
document.documentElement.classList.add("opportunities-extension-scroll-lock");
|
|
ensureEscapeKeyHandler();
|
|
requestTokenRelayOnce().catch(() => {});
|
|
return;
|
|
}
|
|
|
|
const overlay = document.createElement("div");
|
|
debugState.requestLog = [];
|
|
authSessionState.tokenRelayRequest = null;
|
|
authSessionState.consumerTokenServiceRequest = null;
|
|
authSessionState.accessToken = "";
|
|
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");
|
|
overlay.setAttribute("aria-describedby", "opportunities-extension-subtitle");
|
|
|
|
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 subtitle = document.createElement("p");
|
|
subtitle.id = "opportunities-extension-subtitle";
|
|
subtitle.textContent = "Lista de oportunidades do HCM Opportunities List";
|
|
|
|
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;
|
|
subtitleRow.append(subtitle, authBadge, productsProgressBadge, productsCacheTimestamp);
|
|
|
|
const titleBlock = document.createElement("div");
|
|
titleBlock.append(title, subtitleRow);
|
|
|
|
const closeButton = document.createElement("button");
|
|
closeButton.type = "button";
|
|
closeButton.className = "opportunities-extension-icon-button";
|
|
closeButton.setAttribute("aria-label", "Fechar");
|
|
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", "Abrir preferências");
|
|
preferencesButton.title = "Preferences";
|
|
preferencesButton.append(createSettingsIcon());
|
|
preferencesButton.addEventListener("click", openPreferencesModal);
|
|
|
|
const headerActions = document.createElement("div");
|
|
headerActions.className = "opportunities-extension-header-actions";
|
|
headerActions.append(themeButton, 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();
|
|
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();
|
|
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());
|
|
form.append(field, customerFilter, ownerFilter, tableSearchField, statusFilter, stageFilter);
|
|
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("aria-label", "Opportunities");
|
|
tableSurface.append(opportunitiesTable);
|
|
|
|
body.append(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);
|
|
document.documentElement.classList.add("opportunities-extension-scroll-lock");
|
|
ensureEscapeKeyHandler();
|
|
renderStageFilterButtons();
|
|
renderStatusFilterButtons();
|
|
renderCustomerFilter();
|
|
renderOwnerFilter();
|
|
renderOpportunitiesTable();
|
|
requestOpportunitiesForPeriod(select.value);
|
|
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;
|
|
|
|
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() {
|
|
const modal = document.getElementById(MODAL_ID);
|
|
|
|
if (modal) {
|
|
closePreferencesModal();
|
|
closeAccountPlansModal();
|
|
closeInstallBaseModal();
|
|
closeServiceRequestsModal();
|
|
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;
|
|
}
|
|
|
|
if (event.ctrlKey && event.key.toLowerCase() === "d") {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
toggleDebugPanel();
|
|
return;
|
|
}
|
|
|
|
if (event.key === "Escape") {
|
|
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 };
|
|
}
|