\n \n Click here\n to view the Console User's Guide.\n
\n\n \n Click here\n to view the latest release notes.\n
\n {expirationDate && (\nYour subscription contract expiration date is {isValidDate ? `${expirationDate}.` : 'invalid. Contact Channel Software Support for assistance.'}
\n )}\nNeed additional support? Contact us at
Sign In to the Admin Console
\nCatalog: {catalog}
\n )}\nAn error occurred. Check your browser console for more information.
\n )\n}\n","import { useSelector } from 'react-redux';\nimport { createSelector } from '@reduxjs/toolkit';\nimport { getPermissions, getFeatureFlags } from '../redux/selectors';\nimport createFeatureDecisions from '../auth/featureDecisions'\n\n// useFeature is a custom hook that enables feature flags to respond to store changes in components\n// example use case: Resource Center UI visibility is dictated by the value of the feature flag exposeResourceCenter.\n// if the user changes the value of this flag, the store must be able to set the new flag value\n// in memory immediately to avoid timing issues that may result in Resource Center UI being shown/hidden\n// out of sync w/ the flag's value\n\n// create a memoized selector for feature decisions\n// this seems to be the most efficient way of keeping featureDecisions up to date w latest store data\n// avoids calling createFeatureDecisions unless a store dependency has changed\nconst featureDecisionSelector = createSelector(\n // 'input' selector - extracts values from arguments\n getPermissions,\n // 'input' selector\n getFeatureFlags,\n // 'input' selector\n // get latest analytics flag. could abstract this selector, like the others\n state => state.analytics,\n // 'output' selector - receives extracted values and returns a derived value. See featureDecisions.ts.\n // will only be called if extracted values change - for example, if the user's permissions change or a\n // feature flag value is updated via the UI\n createFeatureDecisions\n);\n\n// called in the app as 'useFeature' hook\nexport default function(featureFunction) {\n // use memoized selector created above to get an up-to-date featureDecision obj\n const featureDecisions = useSelector(featureDecisionSelector);\n // then call the feature function with the up-to-date obj\n return featureFunction(featureDecisions)\n}\n","import { getPermissions, getFeatureFlags } from '../redux/selectors';\nimport { FeatureFlagSettings, StoreData } from \"../models/App\";\n\n/**\n * Translates permissions and other feature dependencies (like catalog data)\n * into booleans for use in UI rendering/API endpoint protection\n *\n * @param {string[]} _permissions - a list of the current user's permissions/roles extracted\n * from auth0's ID token supplied at login. Implements role-based access on a per-user basis.\n * @param {FeatureFlagSettings} _featureFlags - current state of internal feature flags, fetched from API on login.\n * Hides/exposes various UI feature elements.\n * @param {boolean} _showAnalytics - boolean indicating whether this console instance has supplied\n * a valid google analytics config. Determines whether to show components displaying GA data.\n */\nfunction createFeatureDecisions (_permissions: string[], _featureFlags: FeatureFlagSettings, _showAnalytics = false) {\n const permissions = _permissions ? _permissions : [];\n const featureFlags = _featureFlags ? _featureFlags : {} as FeatureFlagSettings;\n const showAnalytics = _showAnalytics !== null ? _showAnalytics : false;\n\n return {\n showExperimentalFeatures() {\n return permissions.includes('Experimental Features')\n },\n // permissions are extracted from the user's ID token in App.tsx\n // they are assigned via a user's roles on the auth0 dashboard\n // the Channel Admin role allows users to see admin-only UI - feature flags and WebSetup\n exposeAdminUI() {\n return permissions.includes('Channel Admin')\n },\n exposeContentGroups() {\n return !!featureFlags.exposeContentGroups?.enabled\n },\n // indicates whether a valid GA config has been saved to the API.\n // controls whether GA widgets appear on dashboard, and whether add'l links\n // appear in sidebar under Google Analytics\n showAnalytics() {\n return !!featureFlags.GoogleAnalyticsVersion?.enabled && showAnalytics\n },\n // indicates whether the GoogleAnalyticsVersion flag is enabled.\n // controls whether Google Analytics item appears in sidebar\n exposeAnalytics() {\n return !!featureFlags.GoogleAnalyticsVersion?.enabled\n },\n // indicates value of GoogleAnalyticsVersion flag. used to route GA endpoints\n // correctly and to modify UI to show old UA vs new GA4 widgets/data\n exposeGoogleAnalytics4() {\n return featureFlags.GoogleAnalyticsVersion?.value === 'GA4'\n },\n exposeResourceCenter() {\n return !!featureFlags.exposeResourceCenter?.enabled\n },\n // exposes fields used by clients that are not integrated with an ERP,\n // such as the custom field 'Price\"\n nonIntegratedClientFlag() {\n return !!featureFlags.nonIntegratedClient?.enabled\n },\n exposeSalesRep() {\n return !!featureFlags.exposeSalesRep?.enabled\n },\n exposeACHReports() {\n return !!featureFlags.exposeACHReports?.enabled\n },\n exposeCoupons() {\n return !!featureFlags.exposeCoupons?.enabled\n },\n exposeShipAddresses() {\n return !!featureFlags.exposeShipAddresses?.enabled\n },\n exposeShipRates() {\n return !!featureFlags.exposeShipRates?.enabled\n },\n enableCostCenters() {\n return !!featureFlags.enableCostCenters?.enabled\n },\n exposeApprovalGroups() {\n return !!featureFlags.exposeApprovalGroups?.enabled\n },\n exposeProductDocuments() {\n return !!featureFlags.exposeProductDocuments?.enabled\n },\n exposeProductInventoryMessage() {\n return !!featureFlags.exposeProductInventoryMessage?.enabled\n },\n exposeOrderHistory() {\n return !!featureFlags.exposeOrderHistory?.enabled\n },\n exposeApprovalBudget() {\n return !!featureFlags.exposeApprovalBudget?.enabled\n },\n exposeLoginPricing() {\n return !!featureFlags.showPricing?.enabled\n },\n exposeERPCompanyId() {\n return !!featureFlags.exposeERPCompanyId?.enabled\n }\n }\n}\nexport default createFeatureDecisions\n\n/**\n * Returns a list of all feature decision functions. Used to determine whether the user\n * has the correct permissions to call an endpoint.\n * @param {StoreData} store - current state of data in redux store. contains a list of:\n * permissions (roles) taken from the user's id token on login;\n * catalog data fetched from the API;\n * whether the client has a google analytics account linked to this console instance;\n * and the state of the frontend feature flags, fetched from the API on login\n */\nexport function createFeatureDecisionsFromStore(store: StoreData) {\n const permissions = getPermissions(store);\n const featureFlags = getFeatureFlags(store)\n const showAnalytics = store.analytics;\n\n return createFeatureDecisions(permissions, featureFlags, showAnalytics)\n}\n","import { createSlice } from '@reduxjs/toolkit';\n\nconst savedCatalog = localStorage.getItem('catalog');\n\nconst catalogSlice = createSlice({\n name: 'catalog',\n initialState: savedCatalog ? savedCatalog : '',\n reducers: {\n setCatalog(state, action) {\n return action.payload\n }\n }\n})\n\n// Extract the action creators object and the reducer\nconst { actions, reducer } = catalogSlice\n// Extract and export each action creator by name\nexport const { setCatalog } = actions\n// Export the reducer, either as a default or named export\nexport default reducer\n","import { useState, useEffect } from 'react';\nimport { ApiResponse, AxiosReqStatus } from \"../models/App\";\n\n// axiosRequest is the function indicating which API endpoint should be called\n// other params are dependencies to trigger useEffect/refetch\n\n/**\n * Custom hook handling API requests. Takes in a function for calling an axios request and any dependencies\n * causing the request to be refetched. Returns an array with the API response payload, an error object\n * (if it exists), a setter for manually setting the request data in state (see ProductForm.tsx),\n * and a flag indicating whether the request is in process.\n * @param {PromiseAn error occurred when fetching catalogs. Contact Channel Software support at
An error occurred when fetching this catalog. Contact Channel Software support at