Skip to main content

 

 

Coupa Success Portal

Create an Embedded Panel App

Introduction

Panel Apps allow customers to display data from external sources within a UI panel on a given Coupa page. This data can be context-specific and can be automatically or manually refreshed. For example, when a Supplier page loads in Coupa, a Panel App on that page can automatically get data from an external source via API that pertains to that particular Supplier and display the data in Coupa.

Create a Panel App by going to Setup > Platform > Installed Apps and click the Create button and then the option: Create New Panel App. The key to creating and configuring a Panel App is through it's Descriptor, a JSON format set of parameters for your App.

Here's an example Panel App on the Coupa Homepage.

Example panel app on the Coupa homepage

You can create a Panel App by going to Setup > Platform > Installed Apps. Click the Create button and select Create New Panel App.

Requirements

Starting in R29, Panel Apps require at least TLS v1.2 to make any outbound connections to 3rd party APIs.

Panel App basics

Panel Apps are built using an app descriptor that uses JSON format. The descriptor consists of two main properties: data and blocks. Data is processed using JQ v1.6.

Property Description
slot

Specifies the page type where the panel will be rendered. Current options are:

  • suppliers.show (supplier details page: /suppliers/:id/record)
  • contracts.show (contract details page: /contracts/show/:id)
  • user.home (Coupa homepage: / and /user/home)
  • requisition_headers.edit (Coupa shopping cart: /requisition_headers/{id}/edit)
{
   "slot": "user.home"
 }

Only one slot can be specified per panel app. To have the app appear on multiple pages, create the app again with the desired slot value.

data

The data attribute describes how to fetch the desired data that is rendered in the panel.  A JSON object that describes what data to fetch to render the panel. The values in this object will be URLs that Coupa will send a request to in order to retrieve data. The responses from all of these data requests will be merged into one Hash, aliased under the key name given to them in this object.

{
  "data": {
    "study_queue": "https://www.wanikani.com/api/user/{user_ID}/study-queue",
    "level_progression": "https://www.wanikani.com/api/user/{user_ID}/level-progression"
  }
}
blocks

The blocks attribute describes how to render data in the panel. It is an array of JSON objects ("blocks") of various types that each describe a separate visualization. Blocks have access to the data that was previously sent and use it to create visualizations.

error_blocks The error blocks attribute works similarly to the blocks attribute but is used to create error messages when there's a problem getting or rendering the data.

Panel App block types

Panel App renders that data on an existing Coupa page using different types of UI elements known as blocks. Coupa supports the following block types:

  • Rich text (including images)
  • Fields (essentially key-value pairs)
  • Number
  • Money
  • Table
  • Pie chart
  • Line/Bar/Column graph

App configuration

Attribute Description

type

Identifies the type of block. For example, fields, text, or bars. This will determine how the block is rendered in the panel on the UI, and also the required format for the data.

data

This JSON object will have different properties depending on strategy.

JQ

  • type must equal "jq"

  • jq is a string that contains a JQ script. The JQ script is fed the merged data structure from the data attribute as its input and must produce a JSON structure that fits the required format for the block type.

Processing data with JQ

For example, the following JQ script could be used to extract several specific pieces of data and repackage them in another JSON object:

{
   "username": ".study_queue.user_information.username",
   "radicals": ".level_progression.requested_information.radicals_progress",
   "kanji": ".level_progression.requested_information.kanji_progress"
}

This would result in the following JSON object being produced:

{
   "username": "example_user",
   "radicals": 0,
   "kanji": 0
}

More information:

other properties

Different blocks types may have other properties specific to that block type.

Panel App block types

Text block

Attribute Description

type

Must equal text

template

The text block type uses a Liquid Markdown template. The template is first parsed as Liquid to interpolate the data that's returned from the requests. Then, the template is parsed again as Markdown to generate the HTML.

Markdown provides a safe method of generating HTML, and for security reasons, Coupa blocks inline HTML in the template. There's no character limit other than the max limit of the data column that stores the block's configuration.

Markdown allows for headers, inline emphasis formatting, lists, images, links, and blockquotes. Coupa also supports syntax highlighting and tables. 

For detailed info about working with Liquid and Markdown, see Liquid template language and Mastering Markdown.

data

The data strategy must produce a single JSON object. All of the keys in the object will be available to the Liquid template as variables.

{
   "foo": "bar",
   "fizz": {
      "buzz": "buzz"
   }
}

Fields block 

Attribute Description

type

Must equal field

data

The data strategy must produce an array of JSON objects with label and value attributes. When rendering, the label will be used as the field label and the value will be used as the field value.

[
   {
      "label": "Foobar",
      "value": "fizzbuzz"
   },
   {
      "label": "Lorem Ipsum",
      "value": 42
   }
]
title An optional title that will appear above the list of fields.

Table block

Attribute Description

type

Must equal table

data

The data strategy must produce an array of arrays.

[
   ["col1", "col2", "col3"],
   [1, 2, 3],
   [4, 5, 6]
]
headers A boolean, defaulting to false. If true, the first row of data will be treated as headers for the table and will receive special styling.
title An optional title that will appear above the table.
description An optional description that will appear below the field in smaller text.

Bar/Line block 

Attribute Description

type

Must equal bar or line

data

The data strategy must produce an array of arrays. Unlike the Table Block, the data for bar/line graphs will be column-oriented.

Each array represents a single series of data that will be displayed. The first element is the name of the series, then the remaining elements are the points in the series.

[{
    "name": "Year 1800",
    "data": [107, 31, 635, 203, 2]
 }, {
    "name": "Year 1900",
    "data": [133, 156, 947, 408, 6]
 }, {
    "name": "Year 2000",
    "data": [814, 841, 3714, 727, 31]
 }, {
    "name": "Year 2016",
    "data": [1216, 1001, 4436, 738, 40]
}]
axis Allows configuration of the labels on the x and y axes.
title An optional title that will appear above the graph.
description An options description that will appear below the graph in smaller text.

Pie block

Attribute Description

type

Must equal pie

data

The data strategy must produce a column-oriented array of arrays as described in the Bar/Line Block section.


[{
    name: 'Chrome',
    y: 20.41,
    sliced: true,
    selected: true
  }, {
    name: 'Internet Explorer',
    y: 11.84
  }, {
    name: 'Firefox',
    y: 10.85
  }, {
    name: 'Edge',
    y: 4.67
  }, {
    name: 'Safari',
    y: 4.18
  }, {
    name: 'Sogou Explorer',
    y: 1.64
  }, {
    name: 'Opera',
    y: 2.8
  }, {
    name: 'Other',
    y: 2.61
}]
title An optional title that will appear above the chart.
description An options description that will appear below the chart in smaller text.

Money block

Attribute Description

type

Must equal money

data The data strategy must produce a JSON object with the following structure:
  • Amount a JSON Float to determine the amount of currency.
  • Currency a string containing the ISO 4217 currency code of the currency.
{
  "amount": 42.0,
  "currency": "USD"
}
Title An optional title that will appear above the number.
Description An optional description that will appear below the number in smaller text.

Numbers block

Attribute Description

Type

Must equal numbers

Data

The data strategy must produce a single JSON integer or Float.

42.0
Decimal The number of decimals to display after the decimal point. Defaults to 0.
Title An optional title that will appear above the number.
Description An optional description that will appear below the number in smaller text.

Example Panel Apps

Coupa Cats

This simple app shows a random image and fact related to cats.

The Coupa Cats Panel App

This is the code that drives the app.

{
  "properties": {},
  "slot": "user.home",
  "before_data": {},
  "data": {
    "cat_data": {
      "uri": "https://aws.random.cat/meow"
    },
    "fact_data": {
      "uri": "https://cat-fact.herokuapp.com/facts/random"
    }
  },
  "blocks": [
    {
      "type": "text",
      "data": {
        "type": "jq",
        "jq": ".cat_data"
      },
      "text": "![random cat pic from aws.random.cat]({{ file }}){:height=\"450px\"}"
    },
    {
      "type": "text",
      "data": {
        "type": "jq",
        "jq": ".fact_data"
      },
      "text": "## Here's an interesting fact about cats:\n\n{{ text }}"
    }
  ]
} 

New York Times article search

This app adds the recent news about the supplier from the New York Times on the supplier's page. You'll need to get an NYT API key to make this one work.

nyt-article-search.png

This is the code that drives the app.

{
  "properties": {
    "api_key": {
      "type": "string"
    }
  },
  "slot": "suppliers.show",
  "before_data": {
  },
  "data": {
    "nyt_data": {
      "uri": "https://api.nytimes.com/svc/search/v2/articlesearch.json?q={context.supplier.name}&api-key={properties.api_key}"
    }
  },
  "blocks": [
    {
      "type": "text",
      "data": {
        "type": "jq",
        "jq": "{ \"docs\": .nyt_data | .response | .docs }"
      },
      "text": "![nyt logo](http://www.transervice.com/wp-content/uploads/2018/06/the-new-york-times-4.png){:height=\"100px\"}"
    },
    {
      "type": "text",
      "data": {
        "type": "jq",
        "jq": "{ \"docs\": .nyt_data | .response | .docs }"
      },
      "text": "1. [{{ docs[0].headline.main }}]({{ docs[0].web_url }}) \n2. [{{ docs[1].headline.main }}]({{ docs[1].web_url }})\n3. [{{ docs[2].headline.main }}]({{ docs[2].web_url }})\n4. [{{ docs[3].headline.main }}]({{ docs[2].web_url }})\n5. [{{ docs[2].headline.main }}]({{ docs[4].web_url }})\n"
    }
  ]
} 
  • Was this article helpful?