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 を書かないと件数が出る。合計や平均が欲しいなら value に aggregate と field を書く。

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 は書かない。

書けるキー ​

キー書く場所型必須既定値有効なページ種別説明
itemsdashboardPagearray → dashboardItem必須—dashboardCards, in declaration order.
itemsmenuItemarray → menuItem任意—すべて—
spandashboardIteminteger任意1dashboardGrid columns this card occupies.
valueconditionany任意—crud dashboard detail form master report search wizard—
valuedashboardItemobject → dashboardValue任意—dashboardReduction for a metric card. Omitted = count.
valuefixedConditionany必須—crud dashboard master report searchThe value to match.
valueoption`stringnumberbooleannull`任意
valueoptionsSourcestring任意"code"crud dashboard detail form master report search wizardField of a row to store.
aggregatechartstring (count / sum / avg / min / max ほか)任意—dashboardAggregate applied per label. Omitted = one point per row.
aggregatedashboardValuestring (count / sum / avg / min / max ほか)任意"count"dashboardAggregate operation. Open string; built-ins below.
aggregatereportTotalstring (count / sum / avg / min / max ほか)任意"sum"reportAggregate operation (same vocabulary as a dashboard's).
limitdashboardIteminteger任意100dashboardRows to fetch (the query's pageSize).
limitfield.computedinteger (3 ほか)任意—crud dashboard detail form master report search wizardRow-folding mode only: fold just the first rows, after where and sort. A summary field has a fixed width, so listing every row overflows it. join says how many rows it left out (see overflow).
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).
sortdashboardItemobject → dashboardItem.sort任意—dashboardSort passed to the repository.
sortfield.computedobject → field.computed.sort任意—crud dashboard detail form master report search wizardRow-folding mode only: order the rows before folding, so limit takes the top ones (the three largest amounts). Same shape as the sort of a dashboard card and a report. Values compare as numbers when both read as numbers and as text otherwise (the rule compare uses); rows with no value go last either way, and equal values keep their original order.
sortreportobject → report.sort任意—reportPrint order, passed to the repository. Groups are control breaks, so the rows must arrive in this order.
ascendingdashboardItem.sortboolean任意truedashboard—
ascendingfield.computed.sortboolean任意truecrud dashboard detail form master report search wizard—
ascendingreport.sortboolean任意truereport—
filtersdashboardItemobject → dashboardItem.filters任意—dashboardFixed filter values merged into the query.
filterssearcharray → filter任意—crud dashboard master report search—
actiondashboardItemstring任意—dashboardId of a page action to run when the card is tapped.

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

近い例 ​

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

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

よくある間違い ​

ページ直下に 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 -p @hatake-fw/api hatake pitfalls <キー名>。

実物を見る ​

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