Lattice Grid Buy a licence

demo D223

A sales dashboard in Angular

Top reps by grouping, most-sold products by unnesting the order lines, and five tiles, one ignoring the filter

groupBy · unnest · scope: 'all' · createStat

This is a sales dashboard built from one filtered source: top reps by grouping, most-sold products by unnesting the order lines, and five figure tiles, one of which deliberately ignores the filter. It shows how a single feed can drive a whole screen of related views that all stay in step.

Building…
Loading a live grid…

This is the Angular version. A standalone component takes the whole configuration through one config input, surfaces grid events as outputs, and exposes the live grid on a getter for anything the inputs do not cover.

The configuration

import { Component, ViewChild, ElementRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGrid, createStat } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { LatticeGridComponent, provideLattice } from '@toclocoinc/lattice-grid/angular';

const USD = { style: 'currency', currency: 'USD', decimals: 0 };
const bookColumns = [
  { field: 'rep', title: 'Sales rep' },
  { field: 'region', title: 'Region', filter: { type: 'set' } },
  { field: 'amount', title: 'Deal value', type: 'number', format: USD, total: 'sum' },
];
const repColumns = [
  { field: 'rep', title: 'Rep' },
  { field: 'revenue', title: 'Revenue', type: 'number', format: USD },
];
const skuColumns = [
  { field: 'sku', title: 'SKU' },
  { field: 'product', title: 'Product' },
  { field: 'units', title: 'Units', type: 'number' },
];

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, LatticeGridComponent],
  template:
    '<lattice-grid #book [config]="bookGrid" (grid-ready)="onGridReady()" style="display:block;height:320px"></lattice-grid>' +
    '<div #tiles style="margin:16px 0"></div>' +
    '<lattice-grid *ngIf="reps" [config]="reps" style="display:block;margin-top:16px"></lattice-grid>' +
    '<lattice-grid *ngIf="skus" [config]="skus" style="display:block;margin-top:16px"></lattice-grid>',
})
export class AppComponent {
  @ViewChild('book') bookRef!: LatticeGridComponent;
  @ViewChild('tiles') tilesEl!: ElementRef<HTMLElement>;
  reps: any = null;
  skus: any = null;

  bookGrid = { rowKey: 'id', toolPanel: { side: 'left', panels: ['filters', 'columns'] }, columns: bookColumns, rows: [/* each sale carries a lines: [{ sku, product, qty, value }] array */] };

  // Both roll-ups derive from the live book and follow its filter, so a region
  // narrows the ranking and the SKU league at once. The grid is created once
  // the view has rendered, so the live instance arrives on grid-ready rather
  // than in ngAfterViewInit.
  onGridReady() {
    const book = this.bookRef.grid;
    // Group and rank: revenue per rep, top five.
    this.reps = { autoHeight: true, columns: repColumns, source: { mode: 'derived', from: book, follow: 'filtered', refresh: 'live', groupBy: 'rep',
      select: { revenue: { of: 'amount', fn: 'sum' }, deals: { fn: 'count' }, biggest: { of: 'amount', fn: 'max' } },
      sort: [{ col: 'revenue', dir: 'desc' }], limit: 5 } };
    // Unnest: the SKUs live in a lines array, so one sale becomes several rows
    // before grouping. groupBy reads the dotted path into the unnested element.
    this.skus = { autoHeight: true, columns: skuColumns, source: { mode: 'derived', from: book, follow: 'filtered', refresh: 'live', unnest: 'lines', groupBy: 'lines.sku',
      select: { product: { of: 'lines.product', fn: 'first' }, units: { of: 'lines.qty', fn: 'sum' } },
      sort: [{ col: 'units', dir: 'desc' }], limit: 5 } };
    // A tile over the source. scope: 'all' holds the company total while the grid
    // and every roll-up follow the filter.
    const tile = this.tilesEl.nativeElement.appendChild(document.createElement('div'));
    createStat({ grid: book, container: tile, title: 'Company total', of: 'amount', fn: 'sum', scope: 'all' });
  }
}

bootstrapApplication(AppComponent, {
  providers: [provideLattice({ createGrid })],
});