Override product module styling

Override UI elements of product modules in the InStore colleague app to match brand, workflow, and localization requirements.

Product modules in modular_store share a default UI theme that is configured in the InStore Center on the Theme tab of the environment. You can override the theme and other presentation aspects for the following product modules that run inside the InStore colleague app:
  • InStore_Payment
  • InStore_Refund
  • InStore_DeviceManagement
  • InStore_CashManagement

You must call the relevant API before these modules are called. You can override the following:

Override theme

Product modules adopt the colors defined on the Theme tab in the InStore Center unless you explicitly override them. To override the theme, follow these steps:
  1. Import the useStore hook from InStore_State. This was previously called /store.
  2. Access configuration from useStore in your top-level layout (for example, InStore_Shell/src/views/layout.tsx).
  3. Call configuration.setOverrideCoreTheme(true) before any product module mounts.
  4. Provide custom themeOptions through the theme provider (for example, InStore_Shell/src/providers/theme.tsx).
Import store hooktsx
import { useStore } from "InStore_State/store";
Access configurationtsx
const Layout = () => {
  const { configuration } = useStore();
  // ...
};
Enable core theme overridetsx
useEffect(() => {
  configuration.setOverrideCoreTheme(true);
}, []);
Provide custom themetsx
import React, { PropsWithChildren } from 'react';
import { ThemeProvider as TP } from 'InStore_State/theme';
import customTheme from '../theme/customTheme';

const ThemeProvider = ({ children }: PropsWithChildren) => (
  <TP rootElementId="app" themeOptions={customTheme}>
    {children}
  </TP>
);
  • MUI base v. 5.0.0-beta.34
  • MUI icons-material v. 5.8.0
  • MUI material v. 5.8.1
  • MUI system v. 5.15.0

The following examples show how to configure the elements' size and color, but not the text.

The action button bar background color is not part of Material UI. Set it with the Base Background control on the Environment > Theme tab in the InStore Center.

Payment types structure

Payment types layouttsx
<Grid className="instore-payment-entry-container">
  <Grid item xs={12} md={6} className="instore-payment-amount-section">
    <Grid item xs={12}>
      <Typography className="instore-payment-amount-header" />
    </Grid>
    <Grid item xs={12}>
      <KeypadEntry className="instore-payment-amount-keypad" />
    </Grid>
  </Grid>
  <Grid item xs={12} md={6} className="instore-payment-types-section">
    <Grid item xs={12}>
      <Typography className="instore-payment-types-header" />
    </Grid>
    <Grid item xs={12}>
      <Grid>
        <TenderButton className="instore-payment-types-button-${type}" />
        {/* {type} is cash, credit, paybylink, etc. */}
      </Grid>
    </Grid>
  </Grid>
</Grid>
InStore payment types override

Cash structure

Cash layouttsx
<Grid className="instore-payment-entry-container">
  <Grid item xs={12} md={6} className="instore-payment-amount-section">
    <Grid item xs={12}>
      <Typography className="instore-payment-amount-header" />
    </Grid>
    <Grid item xs={12}>
      <KeypadEntry className="instore-payment-amount-keypad" />
    </Grid>
  </Grid>
  <Grid item xs={12} md={6} className="instore-cash-section">
    <Grid item xs={12}>
      <Typography className="instore-cash-header" />
    </Grid>
    <Grid item xs={12}>
      <Grid>
        <Button className="instore-cash-button-${value}" /> {/* List of buttons */}
      </Grid>
    </Grid>
  </Grid>
</Grid>
InStore cash override

Cash drawer structure

Cash drawer layouttsx
<Grid>
  <Grid item xs={12} md={6}>
    <Grid item xs={12}>
      <Typography className="instore-cash-promp-drawer" />
    </Grid>
  </Grid>
</Grid>
InStore cash drawer override

Change due structure

Change due layouttsx
<Grid className="instore-change-due-container">
  <Grid item xs={12}>
    <Typography className="instore-cash-change-row-0" />
    <Typography className="instore-cash-change-row-1" />
    <Typography className="instore-cash-change-row-2" />
    <Typography className="instore-cash-change-row-3" />
  </Grid>
</Grid>
InStore change due override

Credit processing structure

Credit processing layouttsx
<Grid className="instore-payment-entry-container">
  <Grid item xs={12}>
    <Typography className="instore-credit-process-label" />
    <Typography className="instore-credit-process-amount" />
  </Grid>
</Grid>
InStore credit processing override

Gift card entry structure

Gift card entry layouttsx
<Grid>
  <Grid item xs={12} md={6}>
    <Grid className="instore-gift-cart-header">
      <Grid item xs={12}>
        <Button className="instore-gift-cart-camera-button" />
        <Typography className="instore-gift-cart-label" />
      </Grid>
      <Grid item xs={12}>
        <KeypadEntry className="instore-gift-cart-keypad" />
      </Grid>
    </Grid>
  </Grid>
</Grid>
InStore gift card override

HTML structures for Pay On Account

Use this wrapper to introduce the POA component:

<Paper elevation={0} className='instore-POA-paper-background'>
  <Container className='instore-POA-confirmation-container'>

HTML structure for POA Company Name label and field

<>
  <Grid item xs={12} sm={6}>
    <Typography
      className='instore-POA-company-name-label'
    >
      {t('POA.companyName')}
    </Typography>
  </Grid>
  <Grid
    item
    xs={12}
    sm={6}
    className='instore-POA-company-name-field'
    >
    <Typography
      variant='subtitle1'
      id='customer-company-name'
    >
      {customer.company}
    </Typography>
  </Grid>
</>

HTML structure for POA Authorized Purchaser label and field

<>
  <Grid item xs={12} sm={6}>
    <Typography
      variant='h6'
      id='instore-POA-authorised-purchaser-label'
    >
      {t('POA.authorizedPurchaser')}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <Typography
      variant='subtitle1'
      className='instore-customer-authorised-purchaser-field'
    >
      {customer.first_name + ' ' + customer.last_name}
    </Typography>
  </Grid>
</>

HTML structure for POA Customer Number label and field

<>
  <Grid item xs={12} sm={6}>
    <Typography
      variant='h6'
      className='instore-customer-number-label'
    >
      {t('POA.customerNumber')}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <Typography
      variant='subtitle1'
      className='instore-customer-number-field'
    >
      {customer.customer_number}
    </Typography>
  </Grid>
</>

HTML structure for POA Customer Email label and field

<>
  <Grid item xs={12} sm={6}>
    <Typography variant='h6' className='instore-customer-email-label'
    >
      {t('POA.customerEmail')}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <Typography variant='subtitle1' className='instore-customer-email-field'
    >
      {customer.email}
    </Typography>
  </Grid>
</>

HTML structure for POA Account IDs label and drop-down

<>
  <Grid item xs={12} sm={6}>
    <Typography variant='h6' className='instore-POA-account-id-label'
    >
      {t('POA.accountID')}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <Typography variant='subtitle1' className='instore-customer-account-number'
    >
      {accountNumber}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <FormControl className='instore-POA-account-dropdown'
    >
      <Select
      >
        <MenuItem
        >
          {account}
        </MenuItem>
      </Select>
    </FormControl>
  </Grid>
</>

HTML structure for POA page divider

  <Grid item xs={12} sm={6}>
    <Divider variant='middle' className='instore-POA-divider' />
  </Grid>

HTML structure for POA Payment Amount label and field

<>
  <Grid item xs={12} sm={6}>
    <Typography variant='h6' className='instore-POA-payment-amount-label'
    >
      {t('POA.paymentAmount')}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <Typography variant='subtitle1' className='instore-poa-payment-amount'>
      {currentTenderAmount.toFormat(currencyInstance.currencyFormat)}
    </Typography>
  </Grid>
</>

HTML structure for POA Purchase Order label and field

<>
  <Grid item xs={12} sm={6}>
    <Typography variant='h6' className='instore-purchase-order-label'
    >
      {t('POA.purchaseOrderLabel')}
    </Typography>
  </Grid>
  <Grid item xs={12} sm={6}>
    <TextField
      value={purchaseOrder}
      className='instore-POA-purchase-order-input'
    />
  </Grid>
</>

HTML structure for POA navigation buttons

<Button
  className='instore-back-btn'
>
  {t('Common.buttons.back')}
</Button>
<Button
  className='instore-next-btn'
>
  {t('Common.buttons.next')}
</Button>

Pay On Account HTML rendered on the UI

InStore POA page

Override strings

InStore retrieves UI strings from predefined language bundles. Override only the keys you want to change. Other keys fall back to defaults.

Follow these steps to override strings:

  1. In the InStore GitHub repository, in the client/public folder, locate and copy the locales language bundles for product modules. You can use the bundles as a reference when specifying strings to override.
  2. Specify overrides as shown in this example. If you have multiple languages, add a section for each language as shown.
  3. Define a payload { ... } for each section based on the main payload in the example. The payload only needs to include the strings to be overridden, you don't need to include strings in the payload if you don’t want to override them.
  4. Get an administrator access token. The credentials used here are the same as the ones you use with all InStore APIs.
  5. Send a PUT request to /:tenantId/translations, and then send the payload. You must include {{login_token}} in the header of your request. For example, in the PUT call, insert the {{login_token}} as the value for the token key that pairs with the header.
  6. The returned response is the updated bundle in JSON format.

Route

PathTypeDescription
/:tenantId/translationsPUTOverride default translations for supported locales.

Request

FieldTypeDescription
translationsobjectContainer for per-locale override objects.

Example

The following example replaces the string Cash Drawer with Till in the en-GB bundle.
Translations override payload (excerpt)json
{
  "translations": {
    "en-CA": { },
    "fr-CA": { },
    "en-GB": {
      "App": {
        "system_error": "Undefined system error",
        "initialization_error": "Unable to initialize session!",
        "invalid_request": "Invalid process requested"
      },
      "PaymentEntry": {
        "promptMessage": "Enter Remaining Balance, then select Payment Type.",
        "promptMessageAdditionalPayment": "Enter additional remaining balance, then select Payment Type.",
        "promptMessageCancel": "Cancelling payment...",
        "userMessageCash": "Partial cash tender added",
        "buttons": {
          "cash": "CASH",
          "card": "CREDIT / DEBIT",
          "gift": "GIFT CARD",
          "wallet": "DIGITAL WALLET"
        }
      }
    }
  }
}

Override tender labels

  1. Log in to the InStore API server with administrator credentials and obtain a token.
  2. Add the token to the request header.
  3. Send PUT /:tenantId/translations with a payload containing updated button labels under the relevant module key (for example, Checkout).
  4. A successful update returns status code 202 and the updated labels.

Route

PathTypeDescription
/:tenantId/translationsPUTOverride default translations for supported locales.

Request

FieldTypeDescription
translationsobjectContainer for per-locale translation overrides.

Example

Tender label overridesjson
{
  "translations": {
    "en-US": {
      "Checkout": {
        "buttons": {
          "cash": "CASH",
          "typeCards": "CREDIT / DEBIT",
          "giftCard": "GIFT CARD",
          "digitalWallet": "DIGITAL WALLET",
          "poa": "PAY-ON-ACCOUNT",
          "payByLink": "PAY BY LINK"
        }
      }
    }
  }
}

Override tender order and icons

  1. Log in to the InStore API server with administrator credentials and obtain a token.
  2. Add the token to the request header.
  3. Send PUT /:tenantId/payment-buttons with a payload describing order and icons.
  4. A successful update returns status code 202 and the updated configuration.

Route

PathTypeDescription
/:tenantId/payment-buttonsPUTUpdate tender display configuration and return the tenant document.

Request fields

FieldTypeDescriptionExample
paymentButtonsarrayArray of button configuration objects.See example
paymentButtons[].keystringIdentifier of the payment button to update.creditCard
paymentButtons[].iconstringMaterial UI icon component name.CreditCard
paymentButtons[].orderintegerDisplay order (1–10).1

Example

Tender order and icon overridesjson
{
  "paymentButtons": [
    { "key": "creditCard", "icon": "CreditCard", "order": 1 },
    { "key": "cash", "icon": "Money1", "order": 2 }
  ]
}
To optimize performance, InStore loads only a subset of MaterialUI icons. If you want to use an icon that is not included in the preloaded set, contact the InStore support team.

Override print options

You can override print options from any module in modular_store. For example, you can omit a receipt option on the cart if certain conditions on the customer record are not met.
  1. Import useStore from InStore_State/store.
  2. Access configuration from useStore.
  3. Call configuration.setOverridePrintOptions([...]) to set allowed print options for Checkout or Refund in the InStore_Core module.
Override print optionstsx
import { useStore } from 'InStore_State/store';
  1. Call setOverridePrintOptions in any part of the component to override print options on Checkout or Refund in the InStore core application.
    configuration.setOverridePrintOptions(['Paper']);
    //Options: Paper, Email, Text, and None
    
Make sure that the value of setOverridePrintOptions is either an array of strings or null. If this override consists of null, then InStore uses the values that were set by the Receipt_Print_Options administration parameter in the InStore Center.

An array will replace the values that were configured by the administration parameter.

For more information, see how to set an event type for capturing the choice of receipt.

Add payment details to the Totals box

You can add your own specific information to display to your colleagues at the top of the Totals box during checkout. For example, you can display loyalty benefits, customer entitlements, or applied discount information to help the colleague to explain the amount due for a customer.
The additional information is added within a flexibly sized component called the MoneyBoxInjection. You can call the InStore moneyBoxInjection property at render time.
To display payment details within the Totals box, do the following:
  1. Follow the steps for overriding the theme and add the following to the configuration:
    Injector.inject({
      tenantId: "SF-29299292",
      moneyBoxInjection: {
        items: [
          { label: "Promo: Buy 1 Get 1 Free!", alignment: "center" },
          { type: "divider" },
          { label: "Offer ends 31/12", alignment: "right" },
          { type: "divider" },
          { label: "Order Total", value: "£180.50", alignment: "left" },
        ]
      }
    });
    
  2. For each item in the items array, assign one of the following options:
 {
   "label": "Promo: Buy 1 Get 1 Free!",
   "value": "-£1.00",
   "alignment": "center"
 }
  1. Implement a divider (a horizontal line) by inserting the following text:

    { "type": "divider" }
    

Use theming described elsewhere in this topic to style line thickness, color, and spacing.

  1. Use theming overrides to style the MoneyBoxInjection component. For example:
    MoneyBoxInjection: {
      styleOverrides: {
        root: {
          "&.instore-moneybox-injection": {
            textAlign: themeOptions.defaultTextAlign || "left",
            margin: themeOptions.moneyBoxMargin || "4px 0",
            fontSize: themeOptions.moneyBoxFontSize || "14px",
            color: themeOptions.moneyBoxTextColor || "#000",
            "& .instore-moneybox-item-left": { textAlign: "left" },
            "& .instore-moneybox-item-center": { textAlign: "center" },
            "& .instore-moneybox-item-right": { textAlign: "right" },
            "& .instore-moneybox-separator": {
              borderTop: `1px solid ${themeOptions.moneyBoxSeparatorColor || "rgba(0,0,0,0.3)"}`,
              margin: themeOptions.moneyBoxSeparatorSpacing || "4px 0",
            },
          }
        }
      }
    }