Customising Shopfront

Customising Creation Wizards (Page Rules)

Multiple creation wizards are highly customisable in Shopfront by injecting your own JavaScript code.

NOTE: This is a technical article, some programming experience is recommended before continuing.

What are Page Rules?

Page Rules allow you to run custom JavaScript code to customise the creation wizard. The page rules are run in a sandboxed environment that has no access to the rest of the POS system. You are able to run any JavaScript code that is supported by the latest version of Chrome.

The Page Rules work by returning a question in response to a provided context (see Function Signature for more information).

 

Accessing the Page Rules

  1. Open the Menu
  2. Navigate to Setup > Page Rules
  3. Select the Page Rule you wish to customise

 

Function Signature

You must provide a function named entry which will serve as the entry point for the script. This takes one argument (a context). The entry function must return an object that contains the question to ask. The entry function can also optionally return a promise that resolves into the question.

The function has the following signature (TypeScript typed):

interface User {
name: string;
username: string;
role: null | {
id: string | null;
};
permissions: Array<string>;
}

interface Answer {
question: string; // The question asked
elements: Array<{
field: string; // The name of the field
value: string; // The value of the field
}>;
metaData: unknown; // Your specified meta data
}

interface Context {
user: User;
answers: Array<Answer>;
currentQuestion: number; // The index of the question
currentLocation: {
outlet: null | {
id: null | string;
name: null | string;
};
register: null | {
id: null | string;
name: null | string;
};
};
};

interface CustomElementEvent {
type: string; // The name of the event (e.g. click)
target: EventTarget;
currentTarget: EventTarget;
};

interface CustomElement {
type: string; // The DOM element type
textNode?: string; // Any text to display on the element
attributes?: {
[attribute: string]: any; // An object containing the keys as the attribute name and the values as the attribute values
},
events?: Array<string>; // An array of events to listen to (see supported events below)
onEvent?: (CustomElementEvent) => { // Only applicable to the first parent, all children onEvent's will be ignored
data: any; // Must return the value to set for the field
next?: boolean; // Whether to continue to the next page
};
children?: Array<CustomElement>; // Any children to add to the custom element
};

interface QuestionElement {
type: 'text' | 'number' | 'currency' | 'percentage' | 'toggle' | 'calendar' | 'select' | 'custom';
field: string; // The name of the field
placeholder?: string; // The placeholder - not used if custom
default?: string; // The default value - not used if custom
required?: string; // Whether a value is required
multiple?: boolean; // Whether multiple options can be selected - only for type 'select'
options?: Array<{ // Only used with type 'select'
label: string; // The visible value of the option
value: string; // The internal value of the option
}> | string; // If a string is specified, it uses an internal database (see supported databases below)
creatable?: boolean; // Whether new elements can be created - only used with type 'select'
time?: boolean; // Whether you can specify the time - only used with type 'calendar'
end?: boolean; // Whether you need an end date as well as a start date - only used with type 'calendar'
isClearable?: boolean; // Defaults to true - only used with type 'select'
customElement?: CustomElement; // The custom element to use - only for custom
};

interface Question {
question: string; // The text of the question to ask,
title?: string; // The title for the question (displayed at the top of the page)
questionsLeft: number; // An approximate number of questions left,
metaData?: unknown; // Any custom meta data you wish to add to the question
elements: Array<QuestionElement>;
};

/**
* The entry for the page rule
* @param {Context} The current context
* @returns {Question | Promise<Question>} The question to ask
*/
function entry(context) {}

You can specify other functions within the Page Rule to be called from the entry function.

Because the entry function returns a promise you can use it to do things like load external resources, make server-side calls and more.

You are in control of the permissions for the creation wizard. If the user doesn't have permission to answer one of the questions, you must specify that - Shopfront will not automatically remove questions that are affected by a user's permissions.

 

Supported Events

Please note, these are case sensitive.

  • change
  • click

Please contact us if you need another event type.

 

Supported Databases

Please note, these are case sensitive.

  • brands,
  • categories,
  • families,
  • tags,
  • taxRates,
  • suppliers,
  • paymentMethods,
  • outlets,
  • registers,
  • priceSets,
  • priceLists,
  • customerGroups

 

Available APIs

The Page Rule is run directly in the browser so nearly all current JavaScript APIs are available. We've also created an additional API that contains a number of functions that we've found ourselves constantly using.

The Shopfront API

You can access the Shopfront API by using the global variable ShopfrontAPI, e.g.

function entry() {
const API = ShopfrontAPI;
}

The Shopfront API is a singleton so you do not need to use the new keyword.

storeVariable

This allows you to store data between questions (as an alternative to using the metaData object on the element). This overrides any currently stored variable of the same name.

Arguments

  • name: string - the name of the variable,
  • data: mixed - the data to store

Returns

  • void

hasVariable

Check whether a variable has been stored.

Arguments

  • name: string - the name of the variable

Returns

  • boolean - whether the variable exists or not

getVariable

Retrieve a variable from the data store. Throws a TypeError when the variable is not found.

Arguments

  • name: string - the name of the variable

Returns

  • mixed - the data that was stored

queryGraphQL

Query the GraphQL API without having to deal with authentication (uses the current user's permissions). Throws an error upon receiving an error from the server. See https://developer.pos.dev/documentation#Introduction for more details.

Arguments

  • query: string - the query to make to the server,
  • variables (optional): object - the variables to send with the request

Returns

  • object - the data in the same format as the request

redirect

Redirect the page to the provided address

Arguments

  • address: string - the address to redirect the user to

Returns

  • void

setField

Set a field for the current creation wizard to an arbitrary value.

Arguments

  • field: string - the field to update
  • value: mixed - the value to set

Returns

  • void

back

Go back to the previous page

Returns

  • void

next

Go to the next page

Returns

  • void

finish

Finish the creation wizard

Returns

  • void

toast

Go back to the previous page

Arguments

  • toastType: 'success' | 'error' | 'warning' | 'information' - the type of toast to show
  • toastMessage: string - the message to show

Returns

  • void

 

Final Notes

Once you've finished creating your Page Rule, press the Save button and we will run a quick test to check for any syntax errors and whether the Page Rule is valid (such as having an entry function).

If the Page Rule is not valid, the creation wizard will be skipped and a full edit page will be presented instead.

If you ever want to return to the default Page Rule, press the Reset Wizard button and then press the Save button when editing a page rule.