User profile controls that hide non-sales features from users outside sales

This commit is contained in:
2026-09-24 17:23:50 -03:00
parent f74071e39e
commit 451c27e9d7
22 changed files with 391 additions and 36 deletions

View File

@@ -15,6 +15,7 @@ function createMessageTypes() {
requestInstallBase: "opportunitiesExtension.requestInstallBase",
requestCustomerServiceRequest: "opportunitiesExtension.requestCustomerServiceRequest",
requestWlActions: "opportunitiesExtension.requestWlActions",
requestCurrentUser: "opportunitiesExtension.requestCurrentUser",
requestConsumptionTrend: "opportunitiesExtension.requestConsumptionTrend",
requestConsumptionPlanDetail: "opportunitiesExtension.requestConsumptionPlanDetail",
requestRatecardByPlan: "opportunitiesExtension.requestRatecardByPlan"
@@ -470,6 +471,7 @@ function createContentModal({
resetWorkloadActions,
reopenWorkloadActions,
requestTokenRelayOnce,
ensureCurrentUser,
resetCustomerAndOwnerFiltersForPeriod,
resetOpportunitiesTable,
restoreFilterPreferences,
@@ -496,11 +498,16 @@ function createContentModal({
if (existingModal) {
existingModal.hidden = false;
reopenWorkloadActions();
existingModal.querySelector("select").focus();
document.documentElement.classList.add("opportunities-extension-scroll-lock");
ensureEscapeKeyHandler();
requestTokenRelayOnce().then(token => loadCrmServiceRequests(token, opportunitiesQueryState.periodRequestVersion))
ensureCurrentUser().then(() => {
if (existingModal.hidden) return;
reopenWorkloadActions();
return requestTokenRelayOnce().then(token => {
if (!existingModal.hidden) return loadCrmServiceRequests(token, opportunitiesQueryState.periodRequestVersion);
});
})
.then(() => renderOpportunitiesTable()).catch(() => {});
return;
}
@@ -1186,6 +1193,25 @@ function createAuthContentSession({
return { requestTokenRelayOnce, requestConsumerTokenServiceOnce };
}
// Source: src/auth/current-user.js
function createCurrentUser({ sendRuntimeMessage, appendBackgroundRequestLog, messageTypes }) {
let pending = null;
let allowed = false;
function ensure() {
if (!pending) {
pending = sendRuntimeMessage({ type: messageTypes.requestCurrentUser }).then(response => {
appendBackgroundRequestLog(response?.debugRequests);
const roles = response?.user?.roles;
allowed = response?.ok === true && Array.isArray(roles)
&& roles.every(role => typeof role === "string") && !roles.includes("SALES_REP");
return allowed;
}).catch(() => { allowed = false; return false; });
}
return pending;
}
return { ensure, allowsCrmActivities: () => allowed };
}
// Source: src/auth/sales-common.js
function createAuthSalesCommon({
TOKEN_RELAY_URL,
@@ -1568,11 +1594,14 @@ function createOpportunitiesController({
resetWorkloadActions,
requestOpptyProductsInBackground,
requestTokenRelayOnce,
ensureCurrentUser,
setOpportunitiesTableState
}) {
async function requestOpportunitiesForPeriod(period) {
const dateRange = getFiscalPeriodRange(period, new Date());
const requestVersion = ++opportunitiesQueryState.periodRequestVersion;
await ensureCurrentUser();
if (!isCurrentPeriodRequest(requestVersion)) return;
if (preferencesState.selectedOpportunityTypeValues.size === 0) {
setOpportunitiesTableState({
@@ -1933,14 +1962,14 @@ function createFusionCrmServiceRequests({ isCurrentPeriodRequest,
}
// Source: src/features/opportunities/crm-service-requests.js
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, environment }) {
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, allowsCrmActivities, environment }) {
let status = "idle";
let byOpportunity = new Map();
let pending = null;
let generation = 0;
function reset() { closePopover(); generation += 1; pending = null; status = "idle"; byOpportunity = new Map(); }
function load(token, version) {
if (!isCurrentPeriodRequest(version)) return;
if (!allowsCrmActivities() || !isCurrentPeriodRequest(version)) return;
if (!pending) pending = run(token, version, generation);
return pending;
}
@@ -1965,7 +1994,7 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
}
}
function createBadge(optyNumber) {
if (!optyNumber || status === "idle" || status === "loading") return null;
if (!allowsCrmActivities() || !optyNumber || status === "idle" || status === "loading") return null;
const items = byOpportunity.get(String(optyNumber)) || [];
const badge = environment.document.createElement(items.length && status === "ready" ? "button" : "span");
badge.className = "opportunities-extension-crm-sr-badge";
@@ -2123,7 +2152,7 @@ function createCrmSrPopover({ MODAL_ID, createCopyButton, createCloseIcon, envir
}
// Source: src/features/opportunities/workload-actions.js
function createOpportunityWorkloadActions({ authSessionState, requestConsumerTokenServiceOnce, sendRuntimeMessage,
function createOpportunityWorkloadActions({ authSessionState, allowsCrmActivities, requestConsumerTokenServiceOnce, sendRuntimeMessage,
appendBackgroundRequestLog, queueProductsTableRender, productsContentState, messageTypes, environment }) {
let session = newSession();
function newSession() { return { enabled: true, records: new Map(), queue: [], running: 0 }; }
@@ -2137,7 +2166,7 @@ function createOpportunityWorkloadActions({ authSessionState, requestConsumerTok
return [...new Set((products?.items || []).map(item => String(item?.RevnId ?? "").trim()).filter(Boolean))];
}
function loadProducts(products) {
if (!session.enabled) return;
if (!session.enabled || !allowsCrmActivities()) return;
for (const revnId of idsFor(products)) {
if (session.records.has(revnId)) continue;
const record = { status: "loading", actions: [], hasNext: false };
@@ -2161,9 +2190,9 @@ function createOpportunityWorkloadActions({ authSessionState, requestConsumerTok
async function run(current, { revnId, record }) {
try {
const authorizations = await requestConsumerTokenServiceOnce();
if (!current.enabled || current !== session) return;
if (!current.enabled || current !== session || !allowsCrmActivities()) return;
const result = await sendRuntimeMessage({ type: messageTypes.requestWlActions, revnId, authorization: authorizations?.workloadActions });
if (!current.enabled || current !== session) return;
if (!current.enabled || current !== session || !allowsCrmActivities()) return;
appendBackgroundRequestLog(result?.debugRequests);
if (!result?.ok) throw new Error(result?.error || "Unable to load workload actions.");
record.actions = result.actions;
@@ -2178,7 +2207,7 @@ function createOpportunityWorkloadActions({ authSessionState, requestConsumerTok
}
}
function createBadge(optyNumber) {
if (!session.enabled || !optyNumber) return null;
if (!session.enabled || !allowsCrmActivities() || !optyNumber) return null;
const products = productsContentState.opptyProductsByNumber.get(String(optyNumber));
if (!products) return null;
const ids = idsFor(products);
@@ -6463,7 +6492,7 @@ function createDebugContentView({
let returnFocus = null;
function getRequestCategory(entry) {
if (/^request(?:TokenRelay|ConsumerTokenService|CustomerTokenService)/.test(entry.name)) return "authentication";
if (/^request(?:CurrentUser|TokenRelay|ConsumerTokenService|CustomerTokenService)/.test(entry.name)) return "authentication";
if (entry.name === "requestOpportunities") return "opportunities";
if (entry.name === "requestOpptyProducts") return "products";
if (entry.name === "requestWlActions") return "actions";
@@ -6826,6 +6855,11 @@ function createContentApplication(overrides = {}) {
...overrides
};
const messageTypes = createMessageTypes();
const currentUser = createCurrentUser({
messageTypes,
sendRuntimeMessage: (...args) => platformContentMessages.sendRuntimeMessage(...args),
appendBackgroundRequestLog: (...args) => debugContentLog.appendBackgroundRequestLog(...args)
});
const contracts = createContentContracts();
const authSessionState = createAuthSessionState(contracts);
const authSalesCommonState = createAuthSalesCommonState();
@@ -6857,6 +6891,7 @@ function createContentApplication(overrides = {}) {
environment
});
const contentModal = createContentModal({
ensureCurrentUser: () => currentUser.ensure(),
ACCOUNT_PLANS_MODAL_ID: contracts.ACCOUNT_PLANS_MODAL_ID,
AUTH_STATUS: contracts.AUTH_STATUS,
CUSTOMER_FILTER_BUTTON_ID: contracts.CUSTOMER_FILTER_BUTTON_ID,
@@ -6975,6 +7010,7 @@ function createContentApplication(overrides = {}) {
environment
});
const opportunitiesController = createOpportunitiesController({
ensureCurrentUser: () => currentUser.ensure(),
resetWorkloadActions: (...args) => opportunityWorkloadActions.reset(...args),
STAGE_OPTIONS: contracts.STAGE_OPTIONS,
STATUS_OPTIONS: contracts.STATUS_OPTIONS,
@@ -7023,6 +7059,7 @@ function createContentApplication(overrides = {}) {
createCloseIcon: (...args) => contentIcons.createCloseIcon(...args), environment
});
const opportunityCrmServiceRequests = createOpportunityCrmServiceRequests({
allowsCrmActivities: () => currentUser.allowsCrmActivities(),
bindPopover: (...args) => crmSrPopover.bind(...args),
closePopover: (...args) => crmSrPopover.close(...args),
requestCrmServiceRequest: (...args) => fusionCrmServiceRequests.requestCrmServiceRequest(...args),
@@ -7143,6 +7180,7 @@ function createContentApplication(overrides = {}) {
environment
});
const opportunityWorkloadActions = createOpportunityWorkloadActions({
allowsCrmActivities: () => currentUser.allowsCrmActivities(),
authSessionState,
requestConsumerTokenServiceOnce: (...args) => authContentSession.requestConsumerTokenServiceOnce(...args),
sendRuntimeMessage: (...args) => platformContentMessages.sendRuntimeMessage(...args),
@@ -7335,6 +7373,7 @@ function createContentApplication(overrides = {}) {
contentIcons,
contentStyles,
authContentSession,
currentUser,
authSalesCommon,
authTokenRelay,
authXsrfContent,