You are on page 1of 18

6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

Adding an E-Commerce Admin


Dashboard to Saleor
Artyom Keydunov Follow
Jun 19 · 9 min read

In this tutorial, we’ll explore how to add analytical features, such as


reporting and dashboarding to an existing e-commerce web application
with Cube.js. We’re going to use Saleor as our e-commerce platform. It
is powered by a GraphQL server running on top of Python 3 and a
Django 2 framework. Both the storefront and the admin dashboard are
React applications written in TypeScript and powered by Apollo
GraphQL. We’ll add dashboarding and reporting capabilities to the
admin dashboard with the Cube.js.

Installing Saleor
Saleor server and the admin dashboard are located in a single repo ,
and the storefront is in the separate repo. For the purpose of the
tutorial, we need only the server and the admin dashboard. It comes
with very good documentation and detailed installation guide, which

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 1/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

you can find here. Please follow the instructions on installation and
them come back here.

Saleor server and the admin dashboard are located in a single repo
here. It comes with very good documentation and detailed installation
guide, which you can find here. Please follow the installation
instructions and them come back here.

Once, it is installed, let’s populate our store with example products and
orders, as well as the admin account. Run the following command in
your terminal.

$ python manage.py populatedb --createsuperuser

Next, start the local server.

$ python manage.py runserver

Open http://localhost:8000 in your browser and it will show you your


storefront. Since in previous step we loaded some example data, it will
show you some categories and products inside. In this tutorial we’re
going to work with the admin dashboard, which could be found here
http://localhost:8000/dashboard/next. Notice, next in the url -
Saleor is updating the admin UI and we’re going to work the latest one.
To access admin dashboard enter admin@example.com for email and
admin for password.

This how it should look like after you log in.

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 2/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

Installing Cube.js
Now, as we have Saleor up and running we can install Cube.js to start
adding analytical features to our e-commerce dashboard. The Cube.js
backend is a Node app, which could be run as a separate microservice
or in a serverless mode. Let’s create our Cube.js backend instance.

Run the following commands in your terminal

$ npm install -g cubejs-cli


$ cubejs create saleor-analytics -d postgres

The above commands should create a new Cube.js application. Since


we are using Postgres with Saleor we set it as a database type (-d).
Cube.js use ‘dotenv’ to manage credentials, so we need to update .env
file with our database’s credentials.

# Update database credentials in the .env file

CUBEJS_DB_TYPE=postgres
CUBEJS_DB_NAME=saleor
CUBEJS_DB_USER=saleor

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 3/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

The next step is to tell Cube.js how to convert raw data in the database
into meaningful analytics insights. This process is sometimes called
data modeling. It is a way to translate business level definitions, such as
customer lifetime value into SQL. In Cube.js it’s called schema and
schema files are located in ‘schema’ folder.

Cube.js CLI generates an example schema file — schema/Orders.js .


Change it’s content to the following.

cube(`Orders`, {
sql: `select * from order_order`,

measures: {
count: {
type: `count`
}
},

dimensions: {
created: {
type: `time`,
sql: `created`
}
}
});

Cube.js uses schema to generate a SQL code, which will run in your
database and return a result back. A schema is also used to manage pre-
aggregations and cache, you can learn more about it here.

That is all we need to start our first analytics server. In the project
folder run:

$ npm run dev

It will spin up the development server at http://localhost:4000. If you


open it in the browser it’ll show the codesandbox with an example on
how to use an API with React.

Now, as we have Cube.js server up and running the next step is to add
analytics section to our Saleor admin dashboard.

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 4/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

Add Analytics Section to Saleor Dashboard


We’re going to add analytics section to to the Saleor dashboard. The
admin dashboard is a React app written in Typescript, which is located
in saleor/static/dashboard-next folder. Within this folder create a
new folder - analytics . it is going to contain all our code for analytics
sections. First, we’ll add an analytics page, which is going to be a
container for all of our charts.

// Create analytics/index.tsx with the following content

import * as React from "react";

import DashboardPage from "./views/DashboardPage";

const Component = () => <DashboardPage />;

export default Component;

// Create analytics/views/DashboardPage.tsx with the


following content
import * as React from "react";

import Container from "../../components/Container";


import PageHeader from "../../components/PageHeader";
import i18n from "../../i18n";

const DashboardPage = () => (


<Container width="md">
<PageHeader title={i18n.t("Analytics")} />
</Container>
);

DashboardPage.displayName = "DashboardPage";
export default DashboardPage;

// Add analytics route to index.tsx

import AnalyticsPage from "./analytics";

// Add analytics SectionRoute just after Home

<SectionRoute path="/analytics" component={AnalyticsPage} />

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 5/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

That should be enough to render our new blank analytics page at


http://localhost:8000/dashboard/next/analytics. You can try to open
it in your browser. Next, let’s add Analytics item to the left menu.

First, will add new icon component and then edit AppRoot.tsx to add
menu item.

// Create icons/Analytics.tsx with the following content

import createSvgIcon from "@material-


ui/icons/utils/createSvgIcon";
import * as React from "react";

export const Analytics = createSvgIcon(


<>
<g id="Group" transform="translate(-188.000000,
-237.000000)">
<path d="M204,240 L208,240 L208,256 L204,256 Z
M198,246 L202,246 L202,256 L198,256 Z M192,250 L196,250
L196,256 L192,256 Z M192,250" />
</g>
</>
);
Analytics.displayName = "Analytics";
export default Analytics;

// Import icon and add new element to menuStructure array in


the AppRoot.tsx

import Analytics from "./icons/Analytics";

// ...

const menuStructure: IMenuItem[] = [


// ...
{
ariaLabel: "analytics",
icon: <Analytics />,
label: i18n.t("Analytics", { context: "Menu label" }),
url: "/analytics"
}
]

That’s it! Open admin dashboard in your browser and you should see
the new menu item. Now, let’s add some charts to our Analytics Page.

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 6/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

Create First Char


Let’s create a ChartCard React Component in
analytics/components/ChartCard.tsx . It will receive a query as an
input and will encapsulate getting data from Cube.js API and then
rendering the visualization.

We need to install Cube.js javascript client with React wrapper and


Bizcharts for visualizations. Bizcharts is easy to use visualization library
for React apps based on G2. It comes with pretty nice default styles.
Run the following commands in the Saleor root directory:

$ npm install @cubejs-client/core @cubejs-client/react


bizcharts moment numeral --save

Our Cube.js server is running on http://localhost:4000 and API


endpoint is located at http://localhost:4000/cubejs-api/v1, so we need
to specify it in the apiUrl parameter when initializing Cube.js client
instance. It also expects an API key, which you can copy from Cube.js
server console output when you start it.

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 7/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

// Create analytics/components/ChartCard.tsx with the


following content

import * as cubejs from '@cubejs-client/core';


import { QueryRenderer } from '@cubejs-client/react';
import Card from "@material-ui/core/Card";
import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import * as moment from 'moment';
import * as React from "react";

import CardTitle from "../../../components/CardTitle";


import Skeleton from "../../../components/Skeleton";

const CHART_HEIGHT = 400;


const API_KEY = 'YOUR-CUBE-JS-API-KEY';
const API_URL = 'http://localhost:4000/cubejs-api/v1';
const cubejsApi = cubejs(API_KEY, { apiUrl: API_URL });

const renderChart = (resultSet) => (


<Chart
scale={{ category: { tickCount: 8 } }}
height={CHART_HEIGHT}
data={resultSet.chartPivot()}
forceFit
>
<Axis name="category" label={{ formatter: val =>
moment(val).format("MMM DD") }} />
{resultSet.seriesNames().map(s => (<Axis name={s.key}
/>))}
<Tooltip crosshairs={{type : 'y'}} />
{resultSet.seriesNames().map(s => (<Geom type="line"
position={`category*${s.key}`} size={2} />))}
</Chart>
);

const ChartCard = ({ title, query }) => (


<Card>
<CardTitle title={title} />
<QueryRenderer
query={query}
cubejsApi={cubejsApi}
render={ ({ resultSet }) => {
if (!resultSet) {
return (
<div style={{ padding: "10px" }}>
<Skeleton />
</div>
)
}

return renderChart(resultSet);
}}
/>
</Card>
);

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 8/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

ChartCard.displayName = "ChartCard";
export default ChartCard;

Now, let’s add our first chart in DashboardPage using ChartCard


component.

// Replace the content of analytics/views/DashboardPage.tsx


with the following

import * as React from "react";

import CardSpacer from "../../components/CardSpacer";


import Container from "../../components/Container";
import PageHeader from "../../components/PageHeader";
import i18n from "../../i18n";

import ChartCard from "./../components/ChartCard";

const DashboardPage = () => (


<Container width="md">
<PageHeader title={i18n.t("Analytics")} />
<CardSpacer />
<ChartCard
title={i18n.t("Orders Last 30 Days")}
query={{
measures: [ "Orders.count" ],
timeDimensions: [{
dateRange: ['2019-01-01', '2019-01-31'],
dimension: 'Orders.created',
granularity: 'day'
}]
}}
/>
</Container>
);

DashboardPage.displayName = "DashboardPage";
export default DashboardPage;

That will render our first chart at


http://localhost:8000/dashboard/next/analytics.

Add More Charts

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 9/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

First, let’s add support for other visualizations, such as a pie chart. The
query to Cube.js will remain the same, we just need to support the pie
chart in the renderChart methods. Also, we’ll add support for
currency formatting using numeraljs.

// Replace the content of analytics/components/ChartCard.tsx


with the following

import * as cubejs from '@cubejs-client/core';


import { QueryRenderer } from '@cubejs-client/react';
import Card from "@material-ui/core/Card";
import { Axis, Chart, Coord, Geom, Legend, Tooltip } from
'bizcharts';
import * as moment from 'moment';
import * as numeral from 'numeral';
import * as React from "react";

import CardTitle from "../../../components/CardTitle";


import Skeleton from "../../../components/Skeleton";

const CHART_HEIGHT = 400;


const API_KEY = 'YOUR-CUBE-JS-API-KEY';
const API_URL = 'http://localhost:4000/cubejs-api/v1';
const cubejsApi = cubejs(API_KEY, { apiUrl: API_URL });

const formatters = {
currency: (val) => numeral(val).format('$0,0'),
date: (val) => moment(val).format("MMM DD"),
undefined: (val) => val
}
const renderLine = (resultSet) => (
<Chart
scale={{ category: { tickCount: 8 } }}
height={CHART_HEIGHT}
data={resultSet.chartPivot()}
forceFit
>
<Axis name="category" label={{ formatter:
formatters.date }} />
{resultSet.seriesNames().map(s => (<Axis name={s.key}
label={{ formatter:
formatters[resultSet.loadResponse.annotation.measures[s.key]
.format] }} />))}
<Tooltip crosshairs={{type : 'y'}} />
{resultSet.seriesNames().map(s => (<Geom type="line"
position={`category*${s.key}`} size={2} />))}
</Chart>
);

const renderPie = (resultSet) => (


<Chart height={CHART_HEIGHT} data={resultSet.chartPivot()}
forceFit>
<Coord type="theta" radius={0.75} />

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 10/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

{resultSet.seriesNames().map(s => (<Axis name={s.key}


/>))}
<Legend position="right" name="category" />
<Tooltip showTitle={false} />
{resultSet.seriesNames().map(s => (<Geom
type="intervalStack" position={s.key} color="x" />))}
</Chart>
);

const renderChart = (resultSet, visualizationType) => (


{
'line': renderLine,
'pie': renderPie
}[visualizationType](resultSet)
);

const ChartCard = ({ title, query, visualizationType }) => (


<Card>
<CardTitle title={title} />
<QueryRenderer
query={query}
cubejsApi={cubejsApi}
render={ ({ resultSet }) => {
if (!resultSet) {
return (
<div style={{ padding: "10px" }}>
<Skeleton />
</div>
)
}

return renderChart(resultSet, visualizationType);


}}
/>
</Card>
);

ChartCard.displayName = "ChartCard";
export default ChartCard;

Now, let’s define more measures and dimensions in the Cube.js


schema. Open your Cube.js Service project and edit the Orders.js file in
the schema folder.

// Replace the content of schema/Orders.js with the


following:

cube(`Orders`, {
sql: `select * from order_order`,

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 11/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

measures: {
count: {
type: `count`
},

totalNet: {
sql: `total_net`,
type: `sum`,
format: `currency`
},

averageValue: {
sql: `total_net`,
type: `avg`,
format: `currency`
}
},

dimensions: {
created: {
sql: `created`,
type: `time`
},

status: {
sql: `status`,
type: `string`
}
}
});

We have added two new measures — total sales and average sales. And
one new dimension — status.

Now, let’s define all new queries in the DashboardPage component. We


will display the line charts for total orders, total sales, and average
order value (per day), and pie chart for the breakdown of order count
by status. The default date range is set to last 30 days.

// Replace the content of analytics/views/DashboardPage.tsx


with the following
import * as moment from 'moment';
import * as React from "react";

import CardSpacer from "../../components/CardSpacer";


import Container from "../../components/Container";
import PageHeader from "../../components/PageHeader";
import i18n from "../../i18n";

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 12/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

import ChartCard from "./../components/ChartCard";

const cardContainerStyles = {
display: "grid",
gridColumnGap: "24px",
gridTemplateColumns: "1fr 1fr",
rowGap: "24px"
}

const timeDimensions = [{
dateRange: [
moment().subtract(30, 'days').format("YYYY-MM-DD"),
moment().format("YYYY-MM-DD")
],
dimension: 'Orders.created',
granularity: 'day'
}];
const queries = [
{
query: {
measures: ["Orders.count"],
timeDimensions
},
title: i18n.t("Total Orders"),
visualizationType: 'line',
},
{
query: {
measures: ["Orders.totalNet"],
timeDimensions
},
title: i18n.t("Total Sales"),
visualizationType: 'line'
},
{
query: {
measures: ["Orders.averageValue"],
timeDimensions
},
title: i18n.t("Average Order Value"),
visualizationType: 'line'
},
{
query: {
dimensions: ["Orders.status"],
measures: ["Orders.count"]
},
title: i18n.t("Orders by Status"),
visualizationType: 'pie'
}
];

const DashboardPage = () => (


<Container width="md">
<PageHeader title={i18n.t("Analytics")} />
<CardSpacer />
<div style={cardContainerStyles}>
{queries.map((query, index) => (

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 13/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

<ChartCard key={index} {...query} />


))}
</div>
</Container>
);

DashboardPage.displayName = "DashboardPage";
export default DashboardPage;

This will give us a fully working dashboard with four basic metrics. In
the next tutorial, we’ll show you how to add dashboard filters and a
query builder to let users build custom reports.

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 14/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 15/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 16/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 17/18
6/20/2019 Adding an E-Commerce Admin Dashboard to Saleor – Stats and Bots

https://blog.statsbot.co/adding-an-e-commerce-admin-dashboard-to-saleor-da87be14c40e 18/18

You might also like