Skip to content

ダッシュボードに数字を並べる

件数・合計のカード、並べ方、絞り込み、押したときの遷移。

ダッシュボードは items にカードを並べる。カード1枚は「小さな読み取りクエリ」と「その結果の見せ方」の組。

yaml
page:
  type: dashboard
  id: sales_dashboard
  title: 売上ダッシュボード
  repository: orderRepository      # カードが省略したときの既定
  layout: { columns: 4 }
  search:
    filters:
      - { field: orderDate, label: 受注日, type: date, operator: between }
  items:
    - { id: orderCount, title: 受注件数 }
    - { id: total, title: 受注金額, value: { aggregate: sum, field: amount },
        format: currency, config: { symbol: "¥" } }
    - { id: pending, title: 未出荷, filters: { status: 未出荷 } }
    - { id: recent, type: table, title: 直近の受注, span: 2, limit: 5,
        sort: { field: orderDate, ascending: false },
        columns: [ { field: orderNo, label: 受注番号 } ] }

3種類のカード

type で決める。省略すると metric(数字1つ)。

type何が出るか
metric数字1つ。件数、合計、平均
table小さな一覧。直近◯件、上位◯件
chartグラフ(「ダッシュボードにグラフを出す」参照)

value を省くと件数になる

metric カードで value を書かないと件数が出る。合計や平均が欲しいなら valueaggregatefield を書く。

yaml
- { id: orderCount, title: 受注件数 }                                    # 件数
- { id: total, title: 受注金額, value: { aggregate: sum, field: amount } } # 合計

count 以外は field が必須。書き忘れると落ちずに件数のまま出るので、「合計を出したのに件数が出ている」ときはここを見る(下の「よくある間違い」参照)。

集計しているのは Framework ではない

集計クエリは投げていない。 Repository が返した行を、その場で畳み込んでいるだけ。だから

  • カードが読む行数は limit(既定 100)で決まる。100 件を超えるデータの合計は正しくない
  • 大きなデータの合計が要るなら、集計済みの値を返すエンドポイントを Repository 側に用意して、limit を小さくしたカードで受ける

ここは業務で一番効く落とし穴なので、金額の合計を出すカードを作ったら limit を必ず確認する。

カードごとの絞り込み

filters に固定値を書くと、そのカードだけの条件になる。画面上部の検索条件(search)は全カードに効くので、両方を組み合わせて「今月の・未出荷の件数」のようなカードが作れる。

yaml
- { id: pending, title: 未出荷, filters: { status: 未出荷 } }

並べ方

layout.columns がグリッドの幅(既定 2。業務画面では 4 が使いやすい)。span で1枚のカードが占める列数を指定する(既定 1)。表やグラフは span: 2 以上にしないと潰れる。

押したら別の画面へ

action にページアクションの id を書くと、カードを押したときにそれが走る。件数のカードから、その条件の一覧へ飛ばすのが定番。

yaml
items:
  - { id: orderCount, title: 受注件数, action: openOrders }
actions:
  - { id: openOrders, type: navigate, label: 受注照会, page: order_search }

repository はカードごとに変えられる

ページの repository はカードが省略したときの既定でしかない。売上と在庫のように別のデータを1画面に並べるなら、カードごとに書く。

ダッシュボードは1件のレコードを扱わないので key は書かない。

書けるキー

キー書く場所必須既定値有効なページ種別説明
itemsdashboardPagearraydashboardItem必須dashboardCards, in declaration order.
itemsmenuItemarraymenuItem任意すべて
spandashboardIteminteger任意1dashboardGrid columns this card occupies.
valueconditionany任意crud dashboard detail form master report search wizard
valuedashboardItemobjectdashboardValue任意dashboardReduction for a metric card. Omitted = count.
valueoption`stringnumberbooleannull`任意
valueoptionsSourcestring任意"code"crud dashboard detail form master report search wizardField of a row to store.
aggregatechartstringcount / sum / avg / min / max ほか)任意dashboardAggregate applied per label. Omitted = one point per row.
aggregatedashboardValuestringcount / sum / avg / min / max ほか)任意"count"dashboardAggregate operation. Open string; built-ins below.
aggregatereportTotalstringcount / sum / avg / min / max ほか)任意"sum"reportAggregate operation (same vocabulary as a dashboard's).
limitdashboardIteminteger任意100dashboardRows to fetch (the query's pageSize).
limitoptionsSourceinteger任意200crud dashboard detail form master report search wizardRows to fetch (a select is not a list screen).
limitreportinteger任意1000reportRows read for one run (a report is printed, not paged).
sortdashboardItemobjectdashboardItem.sort任意dashboardSort passed to the repository.
sortreportobjectreport.sort任意reportPrint order, passed to the repository. Groups are control breaks, so the rows must arrive in this order.
ascendingdashboardItem.sortboolean任意truedashboard
ascendingreport.sortboolean任意truereport
filtersdashboardItemobjectdashboardItem.filters任意dashboardFixed filter values merged into the query.
filterssearcharrayfilter任意crud dashboard master report search
actiondashboardItemstring任意dashboardId of a page action to run when the card is tapped.

この表は spec/reference.json から生成している(JSON Schema が正)。手元では npx hatake reference <キー名> で同じものが引ける。

近い例

例は丸ごと写して直すのが一番速い。以下は CI で検証済み(そのまま動く形)。

ファイル種別画面どういうときに使うか
customer_master.yamlcrud顧客マスタ検索して一覧に出して、その場で登録・修正・削除まで面倒を見る画面が欲しい
product_search.yamlsearch商品照会検索して一覧を見るだけ(登録も更新もさせない)画面が欲しい
sales_dashboard.yamldashboard売上ダッシュボード件数・金額・グラフのカードを並べて、まず数字を見せたい
sales_report.yamlreport売上明細表一覧の印刷版が欲しい。顧客ごとに小計を出して、CSV も落としたい、紙にも刷りたい
sales_app.yamlapp販売管理複数の画面をメニューで束ねて、1つのアプリにしたい。会社の色にもしたい
roles_app.yamlapp人事管理見せる相手を役割で変えたい(画面ごと隠す・列や項目だけ隠す・押せる人を絞る)

よくある間違い

ページ直下に filters を書く

なぜ駄目か 検索条件は search の持ち物。ページ直下の filters は無い(ダッシュボードのカードの filters は「固定の絞り込み値」で別物)。

こう直す search.filters に入れる。

yaml
page:
  type: search
  id: order_search
  title: 受注照会
  repository: orderRepository
  filters:
    - { field: status, label: 状態 }
yaml
page:
  type: search
  id: order_search
  title: 受注照会
  repository: orderRepository
  search:
    filters:
      - { field: status, label: 状態 }

合計を出したいのに value を省く

なぜ駄目か value を省いた metric カードは 件数(count)。金額を足したいのに件数が出る、という間違いは画面を見ても気づきにくい。

こう直す value: { aggregate: sum, field: amount } のように、畳み込み方と対象項目を書く。count 以外は field が必須。

yaml
page:
  type: dashboard
  id: sales_dashboard
  title: 売上ダッシュボード
  repository: orderRepository
  items:
    - { id: orderCount, title: 受注件数 }
    - id: total
      title: 受注金額
      value: { aggregate: sum, field: amount }
      format: currency

spec/pitfalls.json から生成。各項目は CI で検証済み(間違いは本当に落ち、正しい方は本当に通る)。手元では npx hatake pitfalls <キー名>

実物を見る

デモアプリの「売上ダッシュボード」がこれを使っている。 デモを開く