Release version 1.0.0
This commit is contained in:
14
README.md
14
README.md
@@ -1,6 +1,6 @@
|
||||
# Opportunities Extension
|
||||
# Opportunities Extension 1.0.0
|
||||
|
||||
Extensao de navegador para adicionar o atalho "Opportunities Extension" nas paginas Oracle Fusion permitidas.
|
||||
Extensao de navegador para adicionar o atalho "Opportunities Extension" nas paginas Oracle Fusion permitidas, consultar oportunidades e exibi-las em uma tabela filtravel.
|
||||
|
||||
## Estrutura de desenvolvimento
|
||||
|
||||
@@ -19,6 +19,14 @@ npm run build
|
||||
|
||||
O build recria `dist/chromium` e `dist/firefox` a partir da mesma fonte.
|
||||
|
||||
## Release 1.0.0
|
||||
|
||||
- Consulta paginada de oportunidades com filtros por periodo, customer, owner, status, stage e busca textual.
|
||||
- Ordenacao por coluna, links para oportunidades e contas, copia do numero da oportunidade e indicadores por stage.
|
||||
- Preferencias de tipos de oportunidade persistidas no navegador.
|
||||
- Tema claro/escuro baseado no Oracle Redwood.
|
||||
- Outputs separados para Chromium/Edge e Firefox.
|
||||
|
||||
## Como carregar em modo desenvolvimento
|
||||
|
||||
### Chrome ou Microsoft Edge
|
||||
@@ -39,4 +47,4 @@ O build recria `dist/chromium` e `dist/firefox` a partir da mesma fonte.
|
||||
- `https://eeho.fa.us2.oraclecloud.com/hcmUI/faces/FuseWelcome`
|
||||
- `https://eeho.fa.us2.oraclecloud.com/fscmUI/faces/FuseWelcome`
|
||||
|
||||
Ao encontrar o grupo `#yourapps_groupNode_sales`, a extensao adiciona o tile antes do item `.flat-grid-cell.flat-grid-cell-addicon`. Ao clicar no tile, exibe o alerta `Deu certo`.
|
||||
Ao encontrar o grupo `#yourapps_groupNode_sales`, a extensao adiciona o tile antes do item `.flat-grid-cell.flat-grid-cell-addicon`. Ao clicar no tile, abre a interface de oportunidades.
|
||||
|
||||
412
dist/chromium/content.js
vendored
412
dist/chromium/content.js
vendored
@@ -26,6 +26,8 @@
|
||||
const STAGE_DASHBOARD_ID = "opportunities-extension-stage-dashboard";
|
||||
const FILTER_PREFERENCES_STORAGE_KEY = "opportunities-extension-filter-preferences";
|
||||
const THEME_STORAGE_KEY = "opportunities-extension-theme";
|
||||
const OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY = "opportunities-extension-opportunity-type-preferences";
|
||||
const PREFERENCES_MODAL_ID = "opportunities-extension-preferences-modal";
|
||||
const OPPORTUNITY_DETAIL_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-detail?puid=";
|
||||
const ACCOUNT_DETAIL_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/accounts/accounts-detail?id=";
|
||||
const DEBUG_BODY_LIMIT = 12000;
|
||||
@@ -40,6 +42,24 @@
|
||||
};
|
||||
const STAGE_OPTIONS = ["SQL", "PIPELINE", "UPSIDE", "FORECAST", "WON"];
|
||||
const STATUS_OPTIONS = ["OPEN", "CLOSED", "LOST", "WON"];
|
||||
const OPPORTUNITY_TYPE_OPTIONS = [
|
||||
{ label: "All records I can see", value: "ORA_ALLOPTIES" },
|
||||
{ label: "I am credit receiver", value: "ORA_CREDITRECEIVER_ISME" },
|
||||
{ label: "I am on the team", value: "ORA_MYSALESTEAMOPTIES" },
|
||||
{ label: "I or my subordinate is a credit receiver", value: "ORA_CREDITRECEIVER_MYORG" },
|
||||
{ label: "I own", value: "ORA_MYOPTIES" },
|
||||
{ label: "My subordinates are on the team", value: "ORA_MYSUBORDSSALESTEAMOPTIES" },
|
||||
{ label: "My subordinates own", value: "ORA_MYSUBORDINATESOPTIES" },
|
||||
{ label: "My territory", value: "ORA_MYASSGTERROPTIES" },
|
||||
{ label: "My territory hierarchy", value: "ORA_MYASSGDESCTERROPTIES" },
|
||||
{ label: "My territory membership", value: "ORA_MYASSGMEMBTERROPTIES" },
|
||||
{ label: "My territory membership hierarchy", value: "ORA_MYASSGMEMBDESCTERROPTIES" }
|
||||
];
|
||||
const DEFAULT_OPPORTUNITY_TYPE_VALUES = [
|
||||
"ORA_MYSALESTEAMOPTIES",
|
||||
"ORA_MYASSGTERROPTIES",
|
||||
"ORA_CREDITRECEIVER_ISME"
|
||||
];
|
||||
let tokenRelayRequest = null;
|
||||
let accessToken = "";
|
||||
let periodRequestVersion = 0;
|
||||
@@ -52,6 +72,7 @@
|
||||
let selectedOwnerKeys = new Set();
|
||||
let ownerSearch = "";
|
||||
let tableSearch = "";
|
||||
let selectedOpportunityTypeValues = new Set(DEFAULT_OPPORTUNITY_TYPE_VALUES);
|
||||
let hasSavedCustomerFilter = false;
|
||||
let hasSavedOwnerFilter = false;
|
||||
let customerFilterInitialized = false;
|
||||
@@ -176,6 +197,7 @@
|
||||
periodRequestVersion = 0;
|
||||
authStatus = AUTH_STATUS.idle;
|
||||
resetOpportunitiesTable();
|
||||
selectedOpportunityTypeValues = new Set(getSavedOpportunityTypeValues());
|
||||
overlay.id = MODAL_ID;
|
||||
overlay.className = "opportunities-extension-modal";
|
||||
overlay.setAttribute("data-theme", getSavedTheme());
|
||||
@@ -226,9 +248,17 @@
|
||||
});
|
||||
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, closeButton);
|
||||
headerActions.append(themeButton, preferencesButton, closeButton);
|
||||
|
||||
header.append(titleBlock, headerActions);
|
||||
|
||||
@@ -634,6 +664,15 @@
|
||||
const dateRange = getFiscalPeriodRange(period, new Date());
|
||||
const requestVersion = ++periodRequestVersion;
|
||||
|
||||
if (selectedOpportunityTypeValues.size === 0) {
|
||||
setOpportunitiesTableState({
|
||||
items: [],
|
||||
status: "empty",
|
||||
message: "Select at least one Opportunity Type View in Preferences."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dateRange) {
|
||||
setOpportunitiesTableState({
|
||||
items: [],
|
||||
@@ -1753,10 +1792,11 @@
|
||||
criteria: [
|
||||
{
|
||||
op: "$or",
|
||||
criteria: [
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_MYSALESTEAMOPTIES" },
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_MYASSGTERROPTIES" }
|
||||
]
|
||||
criteria: Array.from(selectedOpportunityTypeValues, (value) => ({
|
||||
op: "$eq",
|
||||
attribute: "RecordSet",
|
||||
value
|
||||
}))
|
||||
},
|
||||
{
|
||||
op: "$wi",
|
||||
@@ -2235,6 +2275,7 @@
|
||||
const modal = document.getElementById(MODAL_ID);
|
||||
|
||||
if (modal) {
|
||||
closePreferencesModal();
|
||||
modal.hidden = true;
|
||||
}
|
||||
|
||||
@@ -2288,6 +2329,185 @@
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createSettingsIcon() {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
const upperControl = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
const lowerControl = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("class", "opportunities-extension-settings-icon");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("focusable", "false");
|
||||
appendPath(svg, "", "M4 7h16M4 17h16");
|
||||
|
||||
[upperControl, lowerControl].forEach((control, index) => {
|
||||
control.setAttribute("cx", index === 0 ? "9" : "15");
|
||||
control.setAttribute("cy", index === 0 ? "7" : "17");
|
||||
control.setAttribute("r", "2.5");
|
||||
control.setAttribute("fill", "currentColor");
|
||||
control.setAttribute("stroke", "currentColor");
|
||||
control.setAttribute("stroke-width", "1.8");
|
||||
});
|
||||
|
||||
svg.querySelector("path").setAttribute("fill", "none");
|
||||
svg.querySelector("path").setAttribute("stroke", "currentColor");
|
||||
svg.querySelector("path").setAttribute("stroke-linecap", "round");
|
||||
svg.querySelector("path").setAttribute("stroke-width", "1.8");
|
||||
svg.append(upperControl, lowerControl);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createCloseIcon() {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("focusable", "false");
|
||||
path.setAttribute("d", "m6 6 12 12M18 6 6 18");
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke", "currentColor");
|
||||
path.setAttribute("stroke-linecap", "round");
|
||||
path.setAttribute("stroke-width", "1.8");
|
||||
svg.append(path);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function getSavedOpportunityTypeValues() {
|
||||
try {
|
||||
const rawPreferences = localStorage.getItem(OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY);
|
||||
|
||||
if (!rawPreferences) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
|
||||
const preferences = JSON.parse(rawPreferences);
|
||||
const values = Array.isArray(preferences.opportunityTypeValues) ? preferences.opportunityTypeValues : null;
|
||||
|
||||
if (!values) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
|
||||
const allowedValues = new Set(OPPORTUNITY_TYPE_OPTIONS.map((option) => option.value));
|
||||
return values.filter((value) => allowedValues.has(value));
|
||||
} catch (error) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
}
|
||||
|
||||
function openPreferencesModal() {
|
||||
const overlay = document.getElementById(MODAL_ID);
|
||||
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (existingModal) {
|
||||
existingModal.hidden = false;
|
||||
existingModal.querySelector("input")?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const preferencesModal = document.createElement("section");
|
||||
preferencesModal.id = PREFERENCES_MODAL_ID;
|
||||
preferencesModal.className = "opportunities-extension-preferences-modal";
|
||||
preferencesModal.setAttribute("role", "dialog");
|
||||
preferencesModal.setAttribute("aria-modal", "true");
|
||||
preferencesModal.setAttribute("aria-labelledby", "opportunities-extension-preferences-title");
|
||||
|
||||
const dialog = document.createElement("div");
|
||||
dialog.className = "opportunities-extension-preferences-dialog";
|
||||
|
||||
const header = document.createElement("header");
|
||||
header.className = "opportunities-extension-preferences-header";
|
||||
|
||||
const title = document.createElement("h2");
|
||||
title.id = "opportunities-extension-preferences-title";
|
||||
title.textContent = "Preferences";
|
||||
|
||||
const closeButton = document.createElement("button");
|
||||
closeButton.type = "button";
|
||||
closeButton.className = "opportunities-extension-icon-button";
|
||||
closeButton.setAttribute("aria-label", "Fechar preferências");
|
||||
closeButton.title = "Close";
|
||||
closeButton.append(createCloseIcon());
|
||||
closeButton.addEventListener("click", closePreferencesModal);
|
||||
header.append(title, closeButton);
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.className = "opportunities-extension-preferences-content";
|
||||
|
||||
const fieldset = document.createElement("fieldset");
|
||||
fieldset.className = "opportunities-extension-preferences-fieldset";
|
||||
|
||||
const legend = document.createElement("legend");
|
||||
legend.textContent = "Opportunity Type View";
|
||||
fieldset.append(legend);
|
||||
|
||||
const selectedValues = new Set(getSavedOpportunityTypeValues());
|
||||
const options = document.createElement("div");
|
||||
options.className = "opportunities-extension-preferences-options";
|
||||
|
||||
OPPORTUNITY_TYPE_OPTIONS.forEach((option) => {
|
||||
const label = document.createElement("label");
|
||||
label.className = "opportunities-extension-preferences-option";
|
||||
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.name = "opportunityTypeView";
|
||||
checkbox.value = option.value;
|
||||
checkbox.checked = selectedValues.has(option.value);
|
||||
|
||||
const text = document.createElement("span");
|
||||
text.textContent = option.label;
|
||||
label.append(checkbox, text);
|
||||
options.append(label);
|
||||
});
|
||||
|
||||
fieldset.append(options);
|
||||
content.append(fieldset);
|
||||
|
||||
const footer = document.createElement("footer");
|
||||
footer.className = "opportunities-extension-preferences-footer";
|
||||
|
||||
const saveButton = document.createElement("button");
|
||||
saveButton.type = "button";
|
||||
saveButton.className = "opportunities-extension-button opportunities-extension-button-primary";
|
||||
saveButton.textContent = "Save";
|
||||
saveButton.addEventListener("click", () => {
|
||||
const selectedValues = Array.from(preferencesModal.querySelectorAll("input[name='opportunityTypeView']:checked"), (checkbox) => checkbox.value);
|
||||
selectedOpportunityTypeValues = new Set(selectedValues);
|
||||
|
||||
try {
|
||||
localStorage.setItem(OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY, JSON.stringify({
|
||||
opportunityTypeValues: selectedValues
|
||||
}));
|
||||
} catch (error) {
|
||||
// Continue with the saved selection for the active modal when storage is unavailable.
|
||||
}
|
||||
|
||||
closePreferencesModal();
|
||||
const periodSelect = overlay.querySelector("select[name='opportunityPeriod']");
|
||||
requestOpportunitiesForPeriod(periodSelect?.value || PERIOD_OPTIONS[0]);
|
||||
});
|
||||
|
||||
footer.append(saveButton);
|
||||
dialog.append(header, content, footer);
|
||||
preferencesModal.append(dialog);
|
||||
overlay.append(preferencesModal);
|
||||
preferencesModal.querySelector("input")?.focus();
|
||||
}
|
||||
|
||||
function closePreferencesModal() {
|
||||
const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (preferencesModal) {
|
||||
preferencesModal.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function ensureEscapeKeyHandler() {
|
||||
if (window[ESCAPE_LISTENER_KEY]) {
|
||||
return;
|
||||
@@ -2309,6 +2529,14 @@
|
||||
}
|
||||
|
||||
if (event.key === "Escape") {
|
||||
const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (preferencesModal && !preferencesModal.hidden) {
|
||||
closePreferencesModal();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (closeCustomerFilterPanel()) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
@@ -2485,6 +2713,11 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.opportunities-extension-icon-button .opportunities-extension-settings-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-icon-button:hover,
|
||||
.opportunities-extension-icon-button:focus {
|
||||
border-color: #b8b2ad;
|
||||
@@ -3313,6 +3546,135 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
background: rgba(0, 0, 0, .42);
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-dialog {
|
||||
display: grid;
|
||||
width: min(680px, 100%);
|
||||
max-height: min(720px, calc(100vh - 48px));
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid #8f8a85;
|
||||
border-radius: 4px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, .24);
|
||||
color: #312d2a;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid #dedbd7;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-content {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-fieldset {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-fieldset legend {
|
||||
margin-bottom: 12px;
|
||||
padding: 0;
|
||||
color: #312d2a;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 4px 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
padding: 9px 8px;
|
||||
border-radius: 4px;
|
||||
color: #312d2a;
|
||||
font-size: 14px;
|
||||
line-height: 1.35;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option:hover {
|
||||
background: #f0eeeb;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex: 0 0 18px;
|
||||
margin: 0;
|
||||
accent-color: #00758f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 14px 20px;
|
||||
border-top: 1px solid #dedbd7;
|
||||
}
|
||||
|
||||
.opportunities-extension-button {
|
||||
min-width: 88px;
|
||||
min-height: 36px;
|
||||
padding: 7px 16px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-button-primary {
|
||||
border-color: #00758f;
|
||||
background: #00758f;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.opportunities-extension-button-primary:hover,
|
||||
.opportunities-extension-button-primary:focus-visible {
|
||||
border-color: #005e73;
|
||||
background: #005e73;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.opportunities-extension-debug-panel {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
@@ -3511,6 +3873,7 @@
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-stage-dashboard-card,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-table-surface,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-panel,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-dialog,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-panel,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-item[open] {
|
||||
border-color: #4e4a46;
|
||||
@@ -3548,6 +3911,9 @@
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-select-all,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-option,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-header h2,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-fieldset legend,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-heading,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-item pre,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-panel h2 {
|
||||
@@ -3559,6 +3925,30 @@
|
||||
border-bottom-color: #4e4a46;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-header,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-footer {
|
||||
border-color: #4e4a46;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option:hover {
|
||||
background: #3b3937;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option input {
|
||||
accent-color: #43c4d5;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary {
|
||||
border-color: #00a6be;
|
||||
background: #008aa6;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary:focus-visible {
|
||||
border-color: #43c4d5;
|
||||
background: #006d7a;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-option:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-table tbody tr:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-sort-button:hover,
|
||||
@@ -3826,6 +4216,18 @@
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-dialog {
|
||||
max-height: calc(100vh - 24px);
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-options {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.opportunities-extension-stage-filter-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
2
dist/chromium/manifest.json
vendored
2
dist/chromium/manifest.json
vendored
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Opportunities Extension",
|
||||
"description": "Adiciona um atalho de Opportunities Extension nas paginas Oracle Fusion permitidas.",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"permissions": [
|
||||
"cookies"
|
||||
],
|
||||
|
||||
412
dist/firefox/content.js
vendored
412
dist/firefox/content.js
vendored
@@ -26,6 +26,8 @@
|
||||
const STAGE_DASHBOARD_ID = "opportunities-extension-stage-dashboard";
|
||||
const FILTER_PREFERENCES_STORAGE_KEY = "opportunities-extension-filter-preferences";
|
||||
const THEME_STORAGE_KEY = "opportunities-extension-theme";
|
||||
const OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY = "opportunities-extension-opportunity-type-preferences";
|
||||
const PREFERENCES_MODAL_ID = "opportunities-extension-preferences-modal";
|
||||
const OPPORTUNITY_DETAIL_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-detail?puid=";
|
||||
const ACCOUNT_DETAIL_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/accounts/accounts-detail?id=";
|
||||
const DEBUG_BODY_LIMIT = 12000;
|
||||
@@ -40,6 +42,24 @@
|
||||
};
|
||||
const STAGE_OPTIONS = ["SQL", "PIPELINE", "UPSIDE", "FORECAST", "WON"];
|
||||
const STATUS_OPTIONS = ["OPEN", "CLOSED", "LOST", "WON"];
|
||||
const OPPORTUNITY_TYPE_OPTIONS = [
|
||||
{ label: "All records I can see", value: "ORA_ALLOPTIES" },
|
||||
{ label: "I am credit receiver", value: "ORA_CREDITRECEIVER_ISME" },
|
||||
{ label: "I am on the team", value: "ORA_MYSALESTEAMOPTIES" },
|
||||
{ label: "I or my subordinate is a credit receiver", value: "ORA_CREDITRECEIVER_MYORG" },
|
||||
{ label: "I own", value: "ORA_MYOPTIES" },
|
||||
{ label: "My subordinates are on the team", value: "ORA_MYSUBORDSSALESTEAMOPTIES" },
|
||||
{ label: "My subordinates own", value: "ORA_MYSUBORDINATESOPTIES" },
|
||||
{ label: "My territory", value: "ORA_MYASSGTERROPTIES" },
|
||||
{ label: "My territory hierarchy", value: "ORA_MYASSGDESCTERROPTIES" },
|
||||
{ label: "My territory membership", value: "ORA_MYASSGMEMBTERROPTIES" },
|
||||
{ label: "My territory membership hierarchy", value: "ORA_MYASSGMEMBDESCTERROPTIES" }
|
||||
];
|
||||
const DEFAULT_OPPORTUNITY_TYPE_VALUES = [
|
||||
"ORA_MYSALESTEAMOPTIES",
|
||||
"ORA_MYASSGTERROPTIES",
|
||||
"ORA_CREDITRECEIVER_ISME"
|
||||
];
|
||||
let tokenRelayRequest = null;
|
||||
let accessToken = "";
|
||||
let periodRequestVersion = 0;
|
||||
@@ -52,6 +72,7 @@
|
||||
let selectedOwnerKeys = new Set();
|
||||
let ownerSearch = "";
|
||||
let tableSearch = "";
|
||||
let selectedOpportunityTypeValues = new Set(DEFAULT_OPPORTUNITY_TYPE_VALUES);
|
||||
let hasSavedCustomerFilter = false;
|
||||
let hasSavedOwnerFilter = false;
|
||||
let customerFilterInitialized = false;
|
||||
@@ -176,6 +197,7 @@
|
||||
periodRequestVersion = 0;
|
||||
authStatus = AUTH_STATUS.idle;
|
||||
resetOpportunitiesTable();
|
||||
selectedOpportunityTypeValues = new Set(getSavedOpportunityTypeValues());
|
||||
overlay.id = MODAL_ID;
|
||||
overlay.className = "opportunities-extension-modal";
|
||||
overlay.setAttribute("data-theme", getSavedTheme());
|
||||
@@ -226,9 +248,17 @@
|
||||
});
|
||||
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, closeButton);
|
||||
headerActions.append(themeButton, preferencesButton, closeButton);
|
||||
|
||||
header.append(titleBlock, headerActions);
|
||||
|
||||
@@ -634,6 +664,15 @@
|
||||
const dateRange = getFiscalPeriodRange(period, new Date());
|
||||
const requestVersion = ++periodRequestVersion;
|
||||
|
||||
if (selectedOpportunityTypeValues.size === 0) {
|
||||
setOpportunitiesTableState({
|
||||
items: [],
|
||||
status: "empty",
|
||||
message: "Select at least one Opportunity Type View in Preferences."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dateRange) {
|
||||
setOpportunitiesTableState({
|
||||
items: [],
|
||||
@@ -1753,10 +1792,11 @@
|
||||
criteria: [
|
||||
{
|
||||
op: "$or",
|
||||
criteria: [
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_MYSALESTEAMOPTIES" },
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_MYASSGTERROPTIES" }
|
||||
]
|
||||
criteria: Array.from(selectedOpportunityTypeValues, (value) => ({
|
||||
op: "$eq",
|
||||
attribute: "RecordSet",
|
||||
value
|
||||
}))
|
||||
},
|
||||
{
|
||||
op: "$wi",
|
||||
@@ -2235,6 +2275,7 @@
|
||||
const modal = document.getElementById(MODAL_ID);
|
||||
|
||||
if (modal) {
|
||||
closePreferencesModal();
|
||||
modal.hidden = true;
|
||||
}
|
||||
|
||||
@@ -2288,6 +2329,185 @@
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createSettingsIcon() {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
const upperControl = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
const lowerControl = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("class", "opportunities-extension-settings-icon");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("focusable", "false");
|
||||
appendPath(svg, "", "M4 7h16M4 17h16");
|
||||
|
||||
[upperControl, lowerControl].forEach((control, index) => {
|
||||
control.setAttribute("cx", index === 0 ? "9" : "15");
|
||||
control.setAttribute("cy", index === 0 ? "7" : "17");
|
||||
control.setAttribute("r", "2.5");
|
||||
control.setAttribute("fill", "currentColor");
|
||||
control.setAttribute("stroke", "currentColor");
|
||||
control.setAttribute("stroke-width", "1.8");
|
||||
});
|
||||
|
||||
svg.querySelector("path").setAttribute("fill", "none");
|
||||
svg.querySelector("path").setAttribute("stroke", "currentColor");
|
||||
svg.querySelector("path").setAttribute("stroke-linecap", "round");
|
||||
svg.querySelector("path").setAttribute("stroke-width", "1.8");
|
||||
svg.append(upperControl, lowerControl);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createCloseIcon() {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("focusable", "false");
|
||||
path.setAttribute("d", "m6 6 12 12M18 6 6 18");
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke", "currentColor");
|
||||
path.setAttribute("stroke-linecap", "round");
|
||||
path.setAttribute("stroke-width", "1.8");
|
||||
svg.append(path);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function getSavedOpportunityTypeValues() {
|
||||
try {
|
||||
const rawPreferences = localStorage.getItem(OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY);
|
||||
|
||||
if (!rawPreferences) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
|
||||
const preferences = JSON.parse(rawPreferences);
|
||||
const values = Array.isArray(preferences.opportunityTypeValues) ? preferences.opportunityTypeValues : null;
|
||||
|
||||
if (!values) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
|
||||
const allowedValues = new Set(OPPORTUNITY_TYPE_OPTIONS.map((option) => option.value));
|
||||
return values.filter((value) => allowedValues.has(value));
|
||||
} catch (error) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
}
|
||||
|
||||
function openPreferencesModal() {
|
||||
const overlay = document.getElementById(MODAL_ID);
|
||||
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (existingModal) {
|
||||
existingModal.hidden = false;
|
||||
existingModal.querySelector("input")?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const preferencesModal = document.createElement("section");
|
||||
preferencesModal.id = PREFERENCES_MODAL_ID;
|
||||
preferencesModal.className = "opportunities-extension-preferences-modal";
|
||||
preferencesModal.setAttribute("role", "dialog");
|
||||
preferencesModal.setAttribute("aria-modal", "true");
|
||||
preferencesModal.setAttribute("aria-labelledby", "opportunities-extension-preferences-title");
|
||||
|
||||
const dialog = document.createElement("div");
|
||||
dialog.className = "opportunities-extension-preferences-dialog";
|
||||
|
||||
const header = document.createElement("header");
|
||||
header.className = "opportunities-extension-preferences-header";
|
||||
|
||||
const title = document.createElement("h2");
|
||||
title.id = "opportunities-extension-preferences-title";
|
||||
title.textContent = "Preferences";
|
||||
|
||||
const closeButton = document.createElement("button");
|
||||
closeButton.type = "button";
|
||||
closeButton.className = "opportunities-extension-icon-button";
|
||||
closeButton.setAttribute("aria-label", "Fechar preferências");
|
||||
closeButton.title = "Close";
|
||||
closeButton.append(createCloseIcon());
|
||||
closeButton.addEventListener("click", closePreferencesModal);
|
||||
header.append(title, closeButton);
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.className = "opportunities-extension-preferences-content";
|
||||
|
||||
const fieldset = document.createElement("fieldset");
|
||||
fieldset.className = "opportunities-extension-preferences-fieldset";
|
||||
|
||||
const legend = document.createElement("legend");
|
||||
legend.textContent = "Opportunity Type View";
|
||||
fieldset.append(legend);
|
||||
|
||||
const selectedValues = new Set(getSavedOpportunityTypeValues());
|
||||
const options = document.createElement("div");
|
||||
options.className = "opportunities-extension-preferences-options";
|
||||
|
||||
OPPORTUNITY_TYPE_OPTIONS.forEach((option) => {
|
||||
const label = document.createElement("label");
|
||||
label.className = "opportunities-extension-preferences-option";
|
||||
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.name = "opportunityTypeView";
|
||||
checkbox.value = option.value;
|
||||
checkbox.checked = selectedValues.has(option.value);
|
||||
|
||||
const text = document.createElement("span");
|
||||
text.textContent = option.label;
|
||||
label.append(checkbox, text);
|
||||
options.append(label);
|
||||
});
|
||||
|
||||
fieldset.append(options);
|
||||
content.append(fieldset);
|
||||
|
||||
const footer = document.createElement("footer");
|
||||
footer.className = "opportunities-extension-preferences-footer";
|
||||
|
||||
const saveButton = document.createElement("button");
|
||||
saveButton.type = "button";
|
||||
saveButton.className = "opportunities-extension-button opportunities-extension-button-primary";
|
||||
saveButton.textContent = "Save";
|
||||
saveButton.addEventListener("click", () => {
|
||||
const selectedValues = Array.from(preferencesModal.querySelectorAll("input[name='opportunityTypeView']:checked"), (checkbox) => checkbox.value);
|
||||
selectedOpportunityTypeValues = new Set(selectedValues);
|
||||
|
||||
try {
|
||||
localStorage.setItem(OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY, JSON.stringify({
|
||||
opportunityTypeValues: selectedValues
|
||||
}));
|
||||
} catch (error) {
|
||||
// Continue with the saved selection for the active modal when storage is unavailable.
|
||||
}
|
||||
|
||||
closePreferencesModal();
|
||||
const periodSelect = overlay.querySelector("select[name='opportunityPeriod']");
|
||||
requestOpportunitiesForPeriod(periodSelect?.value || PERIOD_OPTIONS[0]);
|
||||
});
|
||||
|
||||
footer.append(saveButton);
|
||||
dialog.append(header, content, footer);
|
||||
preferencesModal.append(dialog);
|
||||
overlay.append(preferencesModal);
|
||||
preferencesModal.querySelector("input")?.focus();
|
||||
}
|
||||
|
||||
function closePreferencesModal() {
|
||||
const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (preferencesModal) {
|
||||
preferencesModal.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function ensureEscapeKeyHandler() {
|
||||
if (window[ESCAPE_LISTENER_KEY]) {
|
||||
return;
|
||||
@@ -2309,6 +2529,14 @@
|
||||
}
|
||||
|
||||
if (event.key === "Escape") {
|
||||
const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (preferencesModal && !preferencesModal.hidden) {
|
||||
closePreferencesModal();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (closeCustomerFilterPanel()) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
@@ -2485,6 +2713,11 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.opportunities-extension-icon-button .opportunities-extension-settings-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-icon-button:hover,
|
||||
.opportunities-extension-icon-button:focus {
|
||||
border-color: #b8b2ad;
|
||||
@@ -3313,6 +3546,135 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
background: rgba(0, 0, 0, .42);
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-dialog {
|
||||
display: grid;
|
||||
width: min(680px, 100%);
|
||||
max-height: min(720px, calc(100vh - 48px));
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid #8f8a85;
|
||||
border-radius: 4px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, .24);
|
||||
color: #312d2a;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid #dedbd7;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-content {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-fieldset {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-fieldset legend {
|
||||
margin-bottom: 12px;
|
||||
padding: 0;
|
||||
color: #312d2a;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 4px 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
padding: 9px 8px;
|
||||
border-radius: 4px;
|
||||
color: #312d2a;
|
||||
font-size: 14px;
|
||||
line-height: 1.35;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option:hover {
|
||||
background: #f0eeeb;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex: 0 0 18px;
|
||||
margin: 0;
|
||||
accent-color: #00758f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 14px 20px;
|
||||
border-top: 1px solid #dedbd7;
|
||||
}
|
||||
|
||||
.opportunities-extension-button {
|
||||
min-width: 88px;
|
||||
min-height: 36px;
|
||||
padding: 7px 16px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-button-primary {
|
||||
border-color: #00758f;
|
||||
background: #00758f;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.opportunities-extension-button-primary:hover,
|
||||
.opportunities-extension-button-primary:focus-visible {
|
||||
border-color: #005e73;
|
||||
background: #005e73;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.opportunities-extension-debug-panel {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
@@ -3511,6 +3873,7 @@
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-stage-dashboard-card,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-table-surface,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-panel,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-dialog,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-panel,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-item[open] {
|
||||
border-color: #4e4a46;
|
||||
@@ -3548,6 +3911,9 @@
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-select-all,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-option,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-header h2,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-fieldset legend,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-heading,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-item pre,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-panel h2 {
|
||||
@@ -3559,6 +3925,30 @@
|
||||
border-bottom-color: #4e4a46;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-header,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-footer {
|
||||
border-color: #4e4a46;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option:hover {
|
||||
background: #3b3937;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option input {
|
||||
accent-color: #43c4d5;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary {
|
||||
border-color: #00a6be;
|
||||
background: #008aa6;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary:focus-visible {
|
||||
border-color: #43c4d5;
|
||||
background: #006d7a;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-option:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-table tbody tr:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-sort-button:hover,
|
||||
@@ -3826,6 +4216,18 @@
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-dialog {
|
||||
max-height: calc(100vh - 24px);
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-options {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.opportunities-extension-stage-filter-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
2
dist/firefox/manifest.json
vendored
2
dist/firefox/manifest.json
vendored
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Opportunities Extension",
|
||||
"description": "Adiciona um atalho de Opportunities Extension nas paginas Oracle Fusion permitidas.",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"permissions": [
|
||||
"cookies"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "opportunities-extension",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -13,7 +13,7 @@ const baseManifest = {
|
||||
manifest_version: 3,
|
||||
name: "Opportunities Extension",
|
||||
description: "Adiciona um atalho de Opportunities Extension nas paginas Oracle Fusion permitidas.",
|
||||
version: "0.1.0",
|
||||
version: "1.0.0",
|
||||
permissions: [
|
||||
"cookies"
|
||||
],
|
||||
|
||||
412
src/content.js
412
src/content.js
@@ -26,6 +26,8 @@
|
||||
const STAGE_DASHBOARD_ID = "opportunities-extension-stage-dashboard";
|
||||
const FILTER_PREFERENCES_STORAGE_KEY = "opportunities-extension-filter-preferences";
|
||||
const THEME_STORAGE_KEY = "opportunities-extension-theme";
|
||||
const OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY = "opportunities-extension-opportunity-type-preferences";
|
||||
const PREFERENCES_MODAL_ID = "opportunities-extension-preferences-modal";
|
||||
const OPPORTUNITY_DETAIL_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-detail?puid=";
|
||||
const ACCOUNT_DETAIL_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/accounts/accounts-detail?id=";
|
||||
const DEBUG_BODY_LIMIT = 12000;
|
||||
@@ -40,6 +42,24 @@
|
||||
};
|
||||
const STAGE_OPTIONS = ["SQL", "PIPELINE", "UPSIDE", "FORECAST", "WON"];
|
||||
const STATUS_OPTIONS = ["OPEN", "CLOSED", "LOST", "WON"];
|
||||
const OPPORTUNITY_TYPE_OPTIONS = [
|
||||
{ label: "All records I can see", value: "ORA_ALLOPTIES" },
|
||||
{ label: "I am credit receiver", value: "ORA_CREDITRECEIVER_ISME" },
|
||||
{ label: "I am on the team", value: "ORA_MYSALESTEAMOPTIES" },
|
||||
{ label: "I or my subordinate is a credit receiver", value: "ORA_CREDITRECEIVER_MYORG" },
|
||||
{ label: "I own", value: "ORA_MYOPTIES" },
|
||||
{ label: "My subordinates are on the team", value: "ORA_MYSUBORDSSALESTEAMOPTIES" },
|
||||
{ label: "My subordinates own", value: "ORA_MYSUBORDINATESOPTIES" },
|
||||
{ label: "My territory", value: "ORA_MYASSGTERROPTIES" },
|
||||
{ label: "My territory hierarchy", value: "ORA_MYASSGDESCTERROPTIES" },
|
||||
{ label: "My territory membership", value: "ORA_MYASSGMEMBTERROPTIES" },
|
||||
{ label: "My territory membership hierarchy", value: "ORA_MYASSGMEMBDESCTERROPTIES" }
|
||||
];
|
||||
const DEFAULT_OPPORTUNITY_TYPE_VALUES = [
|
||||
"ORA_MYSALESTEAMOPTIES",
|
||||
"ORA_MYASSGTERROPTIES",
|
||||
"ORA_CREDITRECEIVER_ISME"
|
||||
];
|
||||
let tokenRelayRequest = null;
|
||||
let accessToken = "";
|
||||
let periodRequestVersion = 0;
|
||||
@@ -52,6 +72,7 @@
|
||||
let selectedOwnerKeys = new Set();
|
||||
let ownerSearch = "";
|
||||
let tableSearch = "";
|
||||
let selectedOpportunityTypeValues = new Set(DEFAULT_OPPORTUNITY_TYPE_VALUES);
|
||||
let hasSavedCustomerFilter = false;
|
||||
let hasSavedOwnerFilter = false;
|
||||
let customerFilterInitialized = false;
|
||||
@@ -176,6 +197,7 @@
|
||||
periodRequestVersion = 0;
|
||||
authStatus = AUTH_STATUS.idle;
|
||||
resetOpportunitiesTable();
|
||||
selectedOpportunityTypeValues = new Set(getSavedOpportunityTypeValues());
|
||||
overlay.id = MODAL_ID;
|
||||
overlay.className = "opportunities-extension-modal";
|
||||
overlay.setAttribute("data-theme", getSavedTheme());
|
||||
@@ -226,9 +248,17 @@
|
||||
});
|
||||
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, closeButton);
|
||||
headerActions.append(themeButton, preferencesButton, closeButton);
|
||||
|
||||
header.append(titleBlock, headerActions);
|
||||
|
||||
@@ -634,6 +664,15 @@
|
||||
const dateRange = getFiscalPeriodRange(period, new Date());
|
||||
const requestVersion = ++periodRequestVersion;
|
||||
|
||||
if (selectedOpportunityTypeValues.size === 0) {
|
||||
setOpportunitiesTableState({
|
||||
items: [],
|
||||
status: "empty",
|
||||
message: "Select at least one Opportunity Type View in Preferences."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dateRange) {
|
||||
setOpportunitiesTableState({
|
||||
items: [],
|
||||
@@ -1753,10 +1792,11 @@
|
||||
criteria: [
|
||||
{
|
||||
op: "$or",
|
||||
criteria: [
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_MYSALESTEAMOPTIES" },
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_MYASSGTERROPTIES" }
|
||||
]
|
||||
criteria: Array.from(selectedOpportunityTypeValues, (value) => ({
|
||||
op: "$eq",
|
||||
attribute: "RecordSet",
|
||||
value
|
||||
}))
|
||||
},
|
||||
{
|
||||
op: "$wi",
|
||||
@@ -2235,6 +2275,7 @@
|
||||
const modal = document.getElementById(MODAL_ID);
|
||||
|
||||
if (modal) {
|
||||
closePreferencesModal();
|
||||
modal.hidden = true;
|
||||
}
|
||||
|
||||
@@ -2288,6 +2329,185 @@
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createSettingsIcon() {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
const upperControl = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
const lowerControl = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("class", "opportunities-extension-settings-icon");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("focusable", "false");
|
||||
appendPath(svg, "", "M4 7h16M4 17h16");
|
||||
|
||||
[upperControl, lowerControl].forEach((control, index) => {
|
||||
control.setAttribute("cx", index === 0 ? "9" : "15");
|
||||
control.setAttribute("cy", index === 0 ? "7" : "17");
|
||||
control.setAttribute("r", "2.5");
|
||||
control.setAttribute("fill", "currentColor");
|
||||
control.setAttribute("stroke", "currentColor");
|
||||
control.setAttribute("stroke-width", "1.8");
|
||||
});
|
||||
|
||||
svg.querySelector("path").setAttribute("fill", "none");
|
||||
svg.querySelector("path").setAttribute("stroke", "currentColor");
|
||||
svg.querySelector("path").setAttribute("stroke-linecap", "round");
|
||||
svg.querySelector("path").setAttribute("stroke-width", "1.8");
|
||||
svg.append(upperControl, lowerControl);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createCloseIcon() {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("focusable", "false");
|
||||
path.setAttribute("d", "m6 6 12 12M18 6 6 18");
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke", "currentColor");
|
||||
path.setAttribute("stroke-linecap", "round");
|
||||
path.setAttribute("stroke-width", "1.8");
|
||||
svg.append(path);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function getSavedOpportunityTypeValues() {
|
||||
try {
|
||||
const rawPreferences = localStorage.getItem(OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY);
|
||||
|
||||
if (!rawPreferences) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
|
||||
const preferences = JSON.parse(rawPreferences);
|
||||
const values = Array.isArray(preferences.opportunityTypeValues) ? preferences.opportunityTypeValues : null;
|
||||
|
||||
if (!values) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
|
||||
const allowedValues = new Set(OPPORTUNITY_TYPE_OPTIONS.map((option) => option.value));
|
||||
return values.filter((value) => allowedValues.has(value));
|
||||
} catch (error) {
|
||||
return DEFAULT_OPPORTUNITY_TYPE_VALUES;
|
||||
}
|
||||
}
|
||||
|
||||
function openPreferencesModal() {
|
||||
const overlay = document.getElementById(MODAL_ID);
|
||||
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (existingModal) {
|
||||
existingModal.hidden = false;
|
||||
existingModal.querySelector("input")?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const preferencesModal = document.createElement("section");
|
||||
preferencesModal.id = PREFERENCES_MODAL_ID;
|
||||
preferencesModal.className = "opportunities-extension-preferences-modal";
|
||||
preferencesModal.setAttribute("role", "dialog");
|
||||
preferencesModal.setAttribute("aria-modal", "true");
|
||||
preferencesModal.setAttribute("aria-labelledby", "opportunities-extension-preferences-title");
|
||||
|
||||
const dialog = document.createElement("div");
|
||||
dialog.className = "opportunities-extension-preferences-dialog";
|
||||
|
||||
const header = document.createElement("header");
|
||||
header.className = "opportunities-extension-preferences-header";
|
||||
|
||||
const title = document.createElement("h2");
|
||||
title.id = "opportunities-extension-preferences-title";
|
||||
title.textContent = "Preferences";
|
||||
|
||||
const closeButton = document.createElement("button");
|
||||
closeButton.type = "button";
|
||||
closeButton.className = "opportunities-extension-icon-button";
|
||||
closeButton.setAttribute("aria-label", "Fechar preferências");
|
||||
closeButton.title = "Close";
|
||||
closeButton.append(createCloseIcon());
|
||||
closeButton.addEventListener("click", closePreferencesModal);
|
||||
header.append(title, closeButton);
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.className = "opportunities-extension-preferences-content";
|
||||
|
||||
const fieldset = document.createElement("fieldset");
|
||||
fieldset.className = "opportunities-extension-preferences-fieldset";
|
||||
|
||||
const legend = document.createElement("legend");
|
||||
legend.textContent = "Opportunity Type View";
|
||||
fieldset.append(legend);
|
||||
|
||||
const selectedValues = new Set(getSavedOpportunityTypeValues());
|
||||
const options = document.createElement("div");
|
||||
options.className = "opportunities-extension-preferences-options";
|
||||
|
||||
OPPORTUNITY_TYPE_OPTIONS.forEach((option) => {
|
||||
const label = document.createElement("label");
|
||||
label.className = "opportunities-extension-preferences-option";
|
||||
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.name = "opportunityTypeView";
|
||||
checkbox.value = option.value;
|
||||
checkbox.checked = selectedValues.has(option.value);
|
||||
|
||||
const text = document.createElement("span");
|
||||
text.textContent = option.label;
|
||||
label.append(checkbox, text);
|
||||
options.append(label);
|
||||
});
|
||||
|
||||
fieldset.append(options);
|
||||
content.append(fieldset);
|
||||
|
||||
const footer = document.createElement("footer");
|
||||
footer.className = "opportunities-extension-preferences-footer";
|
||||
|
||||
const saveButton = document.createElement("button");
|
||||
saveButton.type = "button";
|
||||
saveButton.className = "opportunities-extension-button opportunities-extension-button-primary";
|
||||
saveButton.textContent = "Save";
|
||||
saveButton.addEventListener("click", () => {
|
||||
const selectedValues = Array.from(preferencesModal.querySelectorAll("input[name='opportunityTypeView']:checked"), (checkbox) => checkbox.value);
|
||||
selectedOpportunityTypeValues = new Set(selectedValues);
|
||||
|
||||
try {
|
||||
localStorage.setItem(OPPORTUNITY_TYPE_PREFERENCES_STORAGE_KEY, JSON.stringify({
|
||||
opportunityTypeValues: selectedValues
|
||||
}));
|
||||
} catch (error) {
|
||||
// Continue with the saved selection for the active modal when storage is unavailable.
|
||||
}
|
||||
|
||||
closePreferencesModal();
|
||||
const periodSelect = overlay.querySelector("select[name='opportunityPeriod']");
|
||||
requestOpportunitiesForPeriod(periodSelect?.value || PERIOD_OPTIONS[0]);
|
||||
});
|
||||
|
||||
footer.append(saveButton);
|
||||
dialog.append(header, content, footer);
|
||||
preferencesModal.append(dialog);
|
||||
overlay.append(preferencesModal);
|
||||
preferencesModal.querySelector("input")?.focus();
|
||||
}
|
||||
|
||||
function closePreferencesModal() {
|
||||
const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (preferencesModal) {
|
||||
preferencesModal.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function ensureEscapeKeyHandler() {
|
||||
if (window[ESCAPE_LISTENER_KEY]) {
|
||||
return;
|
||||
@@ -2309,6 +2529,14 @@
|
||||
}
|
||||
|
||||
if (event.key === "Escape") {
|
||||
const preferencesModal = document.getElementById(PREFERENCES_MODAL_ID);
|
||||
|
||||
if (preferencesModal && !preferencesModal.hidden) {
|
||||
closePreferencesModal();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (closeCustomerFilterPanel()) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
@@ -2485,6 +2713,11 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.opportunities-extension-icon-button .opportunities-extension-settings-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-icon-button:hover,
|
||||
.opportunities-extension-icon-button:focus {
|
||||
border-color: #b8b2ad;
|
||||
@@ -3313,6 +3546,135 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
background: rgba(0, 0, 0, .42);
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-dialog {
|
||||
display: grid;
|
||||
width: min(680px, 100%);
|
||||
max-height: min(720px, calc(100vh - 48px));
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid #8f8a85;
|
||||
border-radius: 4px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, .24);
|
||||
color: #312d2a;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid #dedbd7;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-content {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-fieldset {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-fieldset legend {
|
||||
margin-bottom: 12px;
|
||||
padding: 0;
|
||||
color: #312d2a;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 4px 20px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
padding: 9px 8px;
|
||||
border-radius: 4px;
|
||||
color: #312d2a;
|
||||
font-size: 14px;
|
||||
line-height: 1.35;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option:hover {
|
||||
background: #f0eeeb;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-option input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex: 0 0 18px;
|
||||
margin: 0;
|
||||
accent-color: #00758f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 14px 20px;
|
||||
border-top: 1px solid #dedbd7;
|
||||
}
|
||||
|
||||
.opportunities-extension-button {
|
||||
min-width: 88px;
|
||||
min-height: 36px;
|
||||
padding: 7px 16px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opportunities-extension-button-primary {
|
||||
border-color: #00758f;
|
||||
background: #00758f;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.opportunities-extension-button-primary:hover,
|
||||
.opportunities-extension-button-primary:focus-visible {
|
||||
border-color: #005e73;
|
||||
background: #005e73;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.opportunities-extension-debug-panel {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
@@ -3511,6 +3873,7 @@
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-stage-dashboard-card,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-table-surface,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-panel,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-dialog,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-panel,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-item[open] {
|
||||
border-color: #4e4a46;
|
||||
@@ -3548,6 +3911,9 @@
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-select-all,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-option,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-header h2,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-fieldset legend,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-heading,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-item pre,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-debug-panel h2 {
|
||||
@@ -3559,6 +3925,30 @@
|
||||
border-bottom-color: #4e4a46;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-header,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-footer {
|
||||
border-color: #4e4a46;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option:hover {
|
||||
background: #3b3937;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-preferences-option input {
|
||||
accent-color: #43c4d5;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary {
|
||||
border-color: #00a6be;
|
||||
background: #008aa6;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-button-primary:focus-visible {
|
||||
border-color: #43c4d5;
|
||||
background: #006d7a;
|
||||
}
|
||||
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-customer-filter-option:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-table tbody tr:hover,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-sort-button:hover,
|
||||
@@ -3826,6 +4216,18 @@
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-modal {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-dialog {
|
||||
max-height: calc(100vh - 24px);
|
||||
}
|
||||
|
||||
.opportunities-extension-preferences-options {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.opportunities-extension-stage-filter-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user