How to create Moderation Views in Forest Admin

In this tutorial, we'll take a look at Moderation Views, what they are, and how you can create them. Check it out!

How to create Moderation Views in Forest Admin

Depending on your app or business, there might come a time when you’re in need of a system to moderate and validate things such as your products, orders, or other changes to your everyday operations.

With Forest Admin, creating such a system is a breeze: in this article, we’ll help you create your own custom Moderation View in only a few easy steps. Let’s take a look at it, shall we?

Getting started

In order to fully understand what Moderation Views are, we first have to mention the two features that make it work: Smart Actions and Smart Views.

Smart Actions allow you to code in your own business logic and implement new functions that you can use at the push of a button: for example, here, we’ll take a look at executing acting without a dropdown menu, and easy record grouping. You can find a detailed tutorial how to get started with them in our Smart Action documentation.


The other half of the picture are Smart Views, which allow you to take data visualization to a next level, and code your view using JS, HTML, and CSS. With Smart Views, you can create views such as maps, calendars, galleries, and so on. For a full tutorial on creating one, check out our Smart View documentation.

So, how will these help us? Well, in order to build a Moderation View like the one above, we need to have:

  • Fast access to actions to moderate records. By default, Forest Admin has a dropdown you need to click on to display your actions. When dealing with multiple records in a Moderation View such as ours, this can quickly become tedious, so we want to simplify this process with a Smart Action that takes the dropdown aspect out of this equation.
  • Easy record grouping to perform actions on. We don't want to perform actions on every of our records one by one. We want to be able to select a pull of records and to perform the same actions only once for every record of the selection — which can be done easily with another Smart Action.
  • Last but not least, a clean and fast overview of the records we want to moderate. In this example, we work with a lot of pictures, and we want to have a full view of what matters without going into individual record details — this is going to be our Smart View, which we’ll place everything else upon.

Let’s take a look at these elements individually, and see how we can actually create them.

Fast access to actions to moderate records

Actions are, by default, displayed when clicking on an action dropdown. Using a Moderation View, we are most likely to be performing these actions repetitively, so we want to eliminate the need for a dropdown. To do soe, we will simply create two buttons and trigger their related actions upon clicking them. Triggering Smart Actions from Smart Views is natively supported by Forest.

For the sake of this tutorial, let's assume we have already created two smart actions called Approve and Reject — of course, when building your own Moderation View, these can be whatever you need them to be, from marking a product live to paying the VAT for a transaction. Here is how we can code the Smart Action buttons into our template.

<Button::BetaButton
  @type="primary"
  @text="Approve"
  @size="tiny"
  @action={{fn this.triggerSmartAction @collection 'Approve' this.selectedRecords}}
  @disabled={{this.disableButtons}
/>
<Button::BetaButton
  @type="danger"
  @text="Reject"
  @size="tiny"
  @action={{fn this.triggerSmartAction @collection 'Reject' this.selectedRecords}}
  @disabled={{this.disableButtons}}
/>

The key in this code snippet is the @action statement. We simply call the triggerSmartAction helper provided by default. The parameters used are the following:

  • @collection is the collection in which we want to trigger the Smart action, which is automatically provided in your Smart View context
  • Approve or Reject are the smart action names we want to trigger
  • this.selectedRecords are the records we want to manipulate with the actions, which we'll look at in the next section

Easy record grouping to perform actions on

Smart Actions can be single, bulk, or global, depending on how many records you want to affect with them. In our case, we might want to trigger the same Smart Actions on multiple records (which is a bulk action), since we’re dealing with a large number of records, and we want to save as much time as possible. To create a bulk action, we need to implement a selection logic to be able to select records. The following code is the part responsible for this.

export default class GalleryView extends Component {
  @tracked allSelected = false;

  ...

  // This function adds the selection mechanism on our records
  @computed('args.records', 'args.records.@each._selected')
  get formatedRecords() {
    if (!this.args.records) return [];

    return this.args.records.map((r) => { r._selected = r._selected || false; return r; })
  }

  // This is the action responsible for selecting a record (eg. adding a record to the selected record pull)
  @action
  selectRecord(selected) {
    if (!selected) {
      this.allSelected = false;
    }

    if (selected && !this.formatedRecords.find((r) => !r._selected)) {
      this.allSelected = true;
    }
  }

  // This action is an additional UX improvement allowing operators to select / unselect every records
  @action
  selectAll(selected) {
    this.formatedRecords.forEach((r) => { r.set('_selected', selected);})
  }

  // This the property that will be used to know which record are selected
  @computed('formatedRecords')
  get selectedRecords() {
    return this.formatedRecords.filter((r) => r._selected);
  }
}

A clean and fast overview of the records you want to moderate

By default, Forest Admin provides you with a view called the table view. The goal of this component is to display your data in an organized manner, but, being a default view aimed at general usage, it is not specific enough for our Moderation View. In order for our view to work perfectly, we need to display records in a table, but at the same time have every piece of  information be visible without having to scroll or to reach records details.

We will, of course, try to reuse as much code as possible from this default table view. This step basically consists of creating a UI that fits our requirements in terms of UX, and including every JS logic we have implemented so far. Here is the exhaustive list of what should be present:

  • The two actions we want to operate with
  • A global checkbox for selecting / deselecting every record on the page
  • Checkbox next to every record for selecting / deselecting it
  • The record data, in such a way that we neither need to scroll or to reach record details to see relevant information

Now that we have everything ready, let’s take a look at how this is going to fit into place.

Displaying actions

Our two actions will be present on the right side, in the headers of the table, using Forest’s pre-built buttons.

...
<th scope="col" class="c-table-frame__header c-table-column-header">
  <span class="c-table-column-header__content">
    <span class="c-table-column-header__display-name" role="button">
      <Button::BetaButton
        @type="primary"
        @text="Approve"
        @size="tiny"
        @action={{fn this.triggerSmartAction @collection 'Approve' this.selectedRecords}}
        @disabled={{this.disableButtons}}
        @class="no-margin"
      />
      <Button::BetaButton
        @type="danger"
        @text="Reject"
        @size="tiny"
        @disabled={{this.disableButtons}}
        @action={{fn this.triggerSmartAction @collection 'Reject' this.selectedRecords}}
        @class="no-margin"
      />
    </span>
  </span>
</th>
...

Global checkbox

The global checkbox will be present on the left side, in the headers of the table, using Forest’s default checkbox.

<th scope="col" role="button" class="c-table-frame__header c-table-frame__header--select-all">
  <div class="c-table-frame__checkbox-select-all">
    <div class="l-table-frame-checkbox-select-all">
      <BetaCheckbox
        @value={{this.allSelected}}
        @small={{true}}
        @disabled={{false}}
        @onChange={{fn this.selectAll}}
      />
    </div>
  </div>
</th>

Record checkbox

The record dedicated checkbox will be present on the left side within the record row, to mimic the default Forest behaviour.

 <td class="align-top first-column" role="">
  <BetaCheckbox
    @value={{record._selected}}
    @small={{true}}
    @disabled={{false}}
    @onChange={{fn this.selectRecord}}
  />
</td>

Display images:

Last but not least, as we mentioned before, we want to display every record's pictures in a static manner, without a carousel or any complex UI disposition.

An easy way to do so is to use the File Viewer to display pictures next to each other.

<td class="second-column">
  {{#each record.forest-imagesSF as |image|}}
    <Widgets::Display::FileViewer::WidgetLayout
      @value={{image}}
      @field={{this.pictureField}}
    />
  {{/each}}
</td>

And that’s it! Once everything is in place, you should have a fully working Moderation View that you can use or further customize based on your business needs. Of course, we have a compilation of the full HTML and CSS needed to replicate our example Moderation View down to the last detail, as well — check it out in our Woodshop, if you're in need of a bit of inspiration!

If you’ve enjoyed this article and would like to see more detailed explanations of some of our more advanced features, don’t forget to follow us on Twitter, Facebook, and LinkedIn. Until next time!