![[新機能]AIファーストな新しいSnowflakeのダッシュボード「Dashboards 2.0」を試してみた](https://devio2024-media.developers.io/image/upload/f_auto,q_auto,w_3840/f_auto,q_auto/v1790872527/eyecatch/cxntufvdsz1bwbpuiokz.png)
[新機能]AIファーストな新しいSnowflakeのダッシュボード「Dashboards 2.0」を試してみた
さがらです。
Snowflakeの新機能として、Dashboards 2.0が利用できるようになっています。Snowsight Workspacesで.dashファイルとしてダッシュボードを作成し、ロールに共有して、CoWorkまたはSnowsightから閲覧できるAIファーストなBI成果物です。
共有まわりの詳細は、こちらの公式Docに記載されています。
今回は、サンプルデータのTPC-Hに対してSemantic Viewを定義し、そのSemantic Viewを使ってDashboards 2.0で可視化するところまでを試します。あわせて、作成された.dashファイルの中身の簡単な説明と、ロールによる権限付与も行ってみます。
機能概要
Dashboards 2.0は、Snowsight Workspacesで作成するSQLベースの複数タイル構成のダッシュボードです。アナリストがCortex Code(CoCo)と対話しながら作成し、ロールに共有します。閲覧者はCoWorkまたはSnowsightで開き、グラフをクリックして探索できます。CoWorkで開いた場合は、タイルに対する追加質問も可能です。
ダッシュボードは、主に以下の要素で構成されます。
| 要素 | 概要 |
|---|---|
| Dashboard | レイアウト、フィルター、タイル群を含む本体(.dashファイル) |
| Tile | SQL・可視化種別(chart / table / markdown)を持つ1つの表示単位。12列グリッド上に配置 |
| Tab | タイルをまとめる単位。最大20個で、フィルターは全タブ共通 |
| Filter | ダッシュボード単位で定義。タイルのSQLに{{ filter('name') }}で接続 |
| Hierarchy | ドリルダウン用の階層定義 |
| Chart action | グラフのクリック/ドラッグで他タイルをフィルター/ハイライト |
| Artifact | .dashの読み取り専用コピー。ロールへの共有の単位 |
タイルのクエリは**閲覧者自身の権限(caller's rights)**で実行され、データ抽出は行われません。
また、閲覧者自身の権限で実行されるとはいえ、タイルがSemantic Viewを参照する場合に閲覧者へ付与が必要なのはSemantic ViewへのSELECTで、基底テーブルの参照権限まで付与する必要はありません。Semantic Viewのクエリに必要な権限は、公式Docの以下のページに記載されています。
従来のDashboardsとの違い
公式Docでは、Dashboards 2.0は従来版のdrop-in置換ではないと明記されています。主な違いは以下です。
| 項目 | 従来版 | Dashboards 2.0 |
|---|---|---|
| 追加質問 | 非対応 | CoWorkで対話可能 |
| 作成方法 | ワークシートUI | CoCo+手動 |
| アクセス制御 | UBAC | RBAC |
| フィルターの範囲 | アカウント全体 | ダッシュボード単位 |
| クエリキャッシュ | ロール内で共有 | ユーザー単位(閲覧者単位の結果再利用) |
従来版の:filter_name構文は2.0では認識されず、{{ filter('name') }}に置き換える必要があります(アップグレードツールで自動変換も可能です)。
参考記事
Dashboards 2.0については、Snowflake Data Superheroesのぺいさんが「Snowflake Dashboards 2.0が最高な理由と、これからBIに求められること」という記事で、特徴とBIの今後を整理されています。本記事の機能概要を書くにあたり、大変参考にさせていただきました。
併せてご覧ください。
制限事項
公式Docの「Limitations (public preview)」から、今回の検証に関係しそうなものを抜粋します。
- 共有されたアーティファクトの閲覧画面(CoWorkなど)では、タイルの追加やグラフ種別の変更はできません。これらは元の
.dashをWorkspacesで編集して行います。なお、Can edit(WRITE権限)を付与されたユーザーは、変更をアーティファクトへ公開(publish)できます。「WRITEがある」ことと「閲覧画面で直接編集できる」ことは同義ではありません - アーティファクトは元の
.dashとは別に保持されます。元ファイルを削除してもアーティファクトは残りますが、Open source fileの参照先は存在しないパスを指すようになります。名前変更や移動では、元ファイルとの関連付けが古くなったり失われたりし、再度アーティファクトを作成すると別アーティファクト・別リンクになる場合があります - チャートアクションはCoCo経由でのみ追加でき、同じテーブル/ビューを参照するタイル間でのみ機能します
{{ hierarchy_key(...) }}を使うクエリでは、{{ hierarchy_rows(...) }}も必須です- アーティファクトの管理用SQL(
SHOW ARTIFACTS、DESCRIBE ARTIFACTなど)は、2026年11月13日時点では「Coming soon」とされています
事前準備
検証用のロール・スキーマ・ウェアハウスを作成
検証用に、作成者ロールと閲覧者ロールを用意します。
USE ROLE ACCOUNTADMIN;
-- 作成者用ロールと閲覧者用ロール
CREATE ROLE IF NOT EXISTS DASH_AUTHOR;
CREATE ROLE IF NOT EXISTS DASH_VIEWER;
-- 検証用のDB・スキーマ・ウェアハウス
CREATE DATABASE IF NOT EXISTS DASH_TEST_DB;
CREATE SCHEMA IF NOT EXISTS DASH_TEST_DB.SV;
CREATE WAREHOUSE IF NOT EXISTS DASH_TEST_WH WAREHOUSE_SIZE = XSMALL AUTO_SUSPEND = 60;
-- サンプルデータベースを作成(SNOWFLAKE_SAMPLE_DATAがアカウントに存在しない場合のみ実行)
CREATE DATABASE SNOWFLAKE_SAMPLE_DATA FROM SHARE SFC_SAMPLES.SAMPLE_DATA;
続いて、作成者ロールDASH_AUTHORに必要な権限を付与します。
-- サンプルデータ(共有DB)の参照権限(作成者のみ。閲覧者には付与しません)
GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE_SAMPLE_DATA TO ROLE DASH_AUTHOR;
-- 作成者ロールの権限(必要なものに絞って付与)
GRANT USAGE ON DATABASE DASH_TEST_DB TO ROLE DASH_AUTHOR;
GRANT USAGE ON SCHEMA DASH_TEST_DB.SV TO ROLE DASH_AUTHOR;
GRANT CREATE SEMANTIC VIEW ON SCHEMA DASH_TEST_DB.SV TO ROLE DASH_AUTHOR;
GRANT USAGE ON WAREHOUSE DASH_TEST_WH TO ROLE DASH_AUTHOR;
-- アーティファクトの共有権限(PUBLICへの既定付与に依存しない)
GRANT MANAGE ACCESS ON DASHBOARD ARTIFACTS
ON ACCOUNT TO ROLE DASH_AUTHOR;
-- 自分のユーザーに作成者ロールを付与
GRANT ROLE DASH_AUTHOR TO USER <your_user>;
Semantic Viewを作成
TPC-HのORDERS、CUSTOMER、LINEITEMを使って、Semantic Viewを定義します。
Semantic ViewのDDL(クリックで展開)
USE ROLE DASH_AUTHOR;
USE WAREHOUSE DASH_TEST_WH;
USE SCHEMA DASH_TEST_DB.SV;
CREATE OR REPLACE SEMANTIC VIEW TPCH_SALES_SV
TABLES (
customers AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER
PRIMARY KEY (c_custkey)
COMMENT = '顧客',
orders AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS
PRIMARY KEY (o_orderkey)
COMMENT = '注文',
line_items AS SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.LINEITEM
PRIMARY KEY (l_orderkey, l_linenumber)
COMMENT = '注文明細'
)
RELATIONSHIPS (
orders_to_customers AS orders (o_custkey) REFERENCES customers,
line_items_to_orders AS line_items (l_orderkey) REFERENCES orders
)
FACTS (
line_items.net_price AS l_extendedprice * (1 - l_discount)
)
DIMENSIONS (
customers.customer_name AS c_name,
customers.market_segment AS c_mktsegment,
orders.order_date AS o_orderdate,
orders.order_year AS YEAR(o_orderdate),
orders.order_priority AS o_orderpriority
)
METRICS (
orders.order_count AS COUNT(o_orderkey),
line_items.total_net_sales AS SUM(line_items.net_price)
)
COMMENT = 'TPC-Hの売上分析用Semantic View';
DDLの構文は、こちらの公式Docを参考にしています。
作成できたか確認するため、実際にSemantic Viewを問い合わせます(DASH_AUTHORロールで実行します)。
-- 年別の売上合計
SELECT *
FROM SEMANTIC_VIEW(
DASH_TEST_DB.SV.TPCH_SALES_SV
DIMENSIONS orders.order_year
METRICS line_items.total_net_sales
)
ORDER BY order_year;

-- 注文優先度別の注文件数
SELECT *
FROM SEMANTIC_VIEW(
DASH_TEST_DB.SV.TPCH_SALES_SV
DIMENSIONS orders.order_priority
METRICS orders.order_count
)
ORDER BY order_priority;

それぞれ、年ごと・優先度ごとの行が返ってくればOKです。SEMANTIC_VIEW句の書き方は、こちらの公式Docを参考にしています。
なお、売上指標total_net_salesは「明細の拡張価格(l_extendedprice)から割引を差し引いた金額の合計」です。税を含む金額や、ORDERS.O_TOTALPRICEの合計とは一致しない点にご注意ください。
試してみた
1. CoCoでDashboardを作成する
SnowsightのWorkspacesを開き、Workspace上でロールをDASH_AUTHOR、ウェアハウスをDASH_TEST_WHに切り替えます。
次に、Add new→Dashboardを選択します。

Workspace右側にCoCoのチャット画面が開くので、CoCoへのプロンプトで、使用するSemantic View名を指定します。未指定の場合はCoCoが関連するSemantic Viewを探しますが、今回は明示的に指定します。あわせて、基底テーブルを直接参照したり、指標を独自に再定義したりしないよう指示します。閲覧者に基底テーブルの権限を付与しない手順との整合性を取るためです。
/dashboard DASH_TEST_DB.SV.TPCH_SALES_SV を使って、TPC-Hの売上ダッシュボードを作ってください。
- 年別の売上合計の推移(折れ線)
- 市場セグメント別の売上合計(棒グラフ)
- 注文優先度別の注文件数(テーブル)
- フィルター: 注文年
すべてのタイルSQLと、注文年フィルターの候補取得にSQLを使う場合の候補取得SQLは、DASH_TEST_DB.SV.TPCH_SALES_SV を参照してください。
SNOWFLAKE_SAMPLE_DATAの基底テーブルは直接参照しないでください。
売上はline_items.total_net_sales、注文件数はorders.order_countを使用し、指標を独自に再定義しないでください。

下図のようにCoCo内で表示され、まずは実装計画を作ります。なぜか英語で作られたので、日本語で回答するよう指示します。

日本語で実装計画が表示されました。問題ないため、作成を依頼します。

少し待つと、.dashファイルがWorkspaces内に作成されて開き、タイルにグラフが表示されました。

2. 対話で修正する
続けて、チャートアクションの追加をCoCoに依頼します。チャートアクションはタイルエディターからは追加できず、CoCo経由でのみ追加できます。
CoCoから、以下の内容で依頼します。
市場セグメント別売上の棒をクリックしたときに、
選択した市場セグメントで年別売上と注文優先度別注文件数を
絞り込むチャートアクションを追加してください。
この結果、下図のように「市場セグメント別 売上合計」で棒をクリックすると、他のタイルの内容も変更されるチャートアクションが追加されました。


ちなみに、追加されたフィルターの定義を見ると、下図のようになっていました。
(フィルターはGUIベースで手動でも追加できるのですが、Semantic ViewのディメンションをGUIから選択することはできなかったため、Semantic Viewに対してディメンションを1つ抽出するSELECT文が記述されています。)


3. 作成された.dashファイルの中身を見る
作成された.dashファイルを開いて、中身を確認します。
ダッシュボード右上から、View JSONを押して確認可能です。

中身はJSON形式でした。今回生成されたファイルは、大きく以下のブロックで構成されていました。(全文は、このステップの最後に折りたたんで載せています)
| キー | 内容 |
|---|---|
version / spec_version / spec_revision |
形式のバージョン情報(今回は2 / 2 / 17) |
title |
ダッシュボードのタイトル |
filters |
フィルターの定義(order_year、market_segment) |
datasources |
タイルやフィルター候補が使うSQLの定義 |
tabs |
タブと、その中のタイル(グラフ・テーブル)の定義 |
layout |
12列グリッド上の各タイルの位置とサイズ |
scopes |
チャートアクションの連動範囲の定義 |
フィルター(filters)
注文年はsingle_select、市場セグメントはmulti_selectで定義されていました。選択肢は"source": "query"で、候補取得用のdatasource(ds-order_year-optionsなど)を参照する形です。
なお、公式Docのフィルターのモードはmulti-selectのようにハイフン区切りで書かれていますが、実際のファイルではmulti_selectとアンダースコア区切りでした。
データソース(datasources)
各datasourceは"type": "sql"で、SQLが文字列として埋め込まれています。たとえば年別売上のSQLは、以下のような内容でした。
SELECT
order_year,
AGG(total_net_sales) AS TOTAL_NET_SALES
FROM DASH_TEST_DB.SV.TPCH_SALES_SV
WHERE
{{ filter('order_year') }}
AND {{ filter('market_segment') }}
GROUP BY
order_year
ORDER BY
order_year
ポイントは以下です。
- Semantic Viewを直接
FROMに指定し、指標をAGG()で集計する書き方になっていました。どのdatasourceもTPCH_SALES_SVのみを参照しており、SNOWFLAKE_SAMPLE_DATAの基底テーブルは直接参照していませんでした。また、売上はtotal_net_sales、注文件数はorder_countが使われ、指標の再定義もされていませんでした。ステップ1で依頼した内容が反映されています - フィルターは、SQL内の
{{ filter('order_year') }}と、filter_bindingsのfilter_idで接続されています - 市場セグメント別売上(
ds-sales-by-segment)は、order_yearのみがバインドされていて、market_segmentのフィルターは接続されていません。このタイルがチャートアクションの操作元になるためと考えられます(推測です) - 候補取得用のdatasource(
ds-order_year-optionsなど)は、Semantic Viewに対してディメンションを1つGROUP BYで抽出するSQLになっています。ステップ2で触れたとおりです
タイルとチャートアクション(tabs)
タイルにはchartとtableの2種類があり、chartのspecはmark(line / bar)とencoding(x / yのfield・type・title)で書かれていました。公式Docには、チャートタイルはVega-Liteで描画されると記載されていますが、この書き方もVega-Liteの記法に近い形でした。
ステップ2で追加したチャートアクションは、市場セグメント別売上のタイルのinteractionsとして定義されていました。
"interactions": [
{
"trigger": "select",
"operator": "set_filter",
"fields": ["MARKET_SEGMENT"],
"targets": [
{
"scope": "segment_action",
"fields": ["MARKET_SEGMENT"],
"filter": "market_segment"
}
]
}
]
select(棒の選択)をトリガーに、market_segmentフィルターをセットする(set_filter)という内容です。連動先のタイル(年別売上・注文優先度別注文件数)には"scope": "segment_action"が付与されており、ファイル末尾のscopesでsegment_actionが定義されています。
レイアウト(layout)
layoutでは、タブごとに各タイルのx / y / w / hが指定されていました。年別売上は幅12(全幅)、市場セグメント別売上と注文優先度別注文件数は幅6ずつで、下段に並んでいます。公式Docの「12列グリッド」と一致する内容です。
.dashファイルの全体(クリックで展開)
{
"version": 2,
"title": "TPC-H 売上ダッシュボード",
"filters": {
"order_year": {
"label": "注文年",
"mode": "single_select",
"valueType": "STRING",
"options": {
"source": "query",
"datasources": [
"ds-order_year-options"
]
},
"default_values": []
},
"market_segment": {
"label": "市場セグメント",
"mode": "multi_select",
"valueType": "STRING",
"options": {
"source": "query",
"datasources": [
"ds-market_segment-options"
]
},
"default_values": []
}
},
"datasources": {
"ds-order_year-options": {
"type": "sql",
"sql": "SELECT\n order_year\nFROM DASH_TEST_DB.SV.TPCH_SALES_SV\nGROUP BY\n order_year\nORDER BY\n order_year"
},
"ds-sales-by-year": {
"type": "sql",
"columns": [
{
"name": "ORDER_YEAR",
"type": "NUMBER"
},
{
"name": "TOTAL_NET_SALES",
"type": "NUMBER"
}
],
"sql": "SELECT\n order_year,\n AGG(total_net_sales) AS TOTAL_NET_SALES\nFROM DASH_TEST_DB.SV.TPCH_SALES_SV\nWHERE\n {{ filter('order_year') }}\n AND {{ filter('market_segment') }}\nGROUP BY\n order_year\nORDER BY\n order_year",
"filter_bindings": [
{
"filter_id": "order_year"
},
{
"filter_id": "market_segment"
}
]
},
"ds-sales-by-segment": {
"type": "sql",
"columns": [
{
"name": "MARKET_SEGMENT",
"type": "VARCHAR"
},
{
"name": "TOTAL_NET_SALES",
"type": "NUMBER"
}
],
"sql": "SELECT\n market_segment,\n AGG(total_net_sales) AS TOTAL_NET_SALES\nFROM DASH_TEST_DB.SV.TPCH_SALES_SV\nWHERE\n {{ filter('order_year') }}\nGROUP BY\n market_segment\nORDER BY\n TOTAL_NET_SALES DESC",
"filter_bindings": [
{
"filter_id": "order_year"
}
]
},
"ds-orders-by-priority": {
"type": "sql",
"columns": [
{
"name": "ORDER_PRIORITY",
"type": "VARCHAR"
},
{
"name": "ORDER_COUNT",
"type": "NUMBER"
}
],
"sql": "SELECT\n order_priority,\n AGG(order_count) AS ORDER_COUNT\nFROM DASH_TEST_DB.SV.TPCH_SALES_SV\nWHERE\n {{ filter('order_year') }}\n AND {{ filter('market_segment') }}\nGROUP BY\n order_priority\nORDER BY\n order_priority",
"filter_bindings": [
{
"filter_id": "order_year"
},
{
"filter_id": "market_segment"
}
]
},
"ds-market_segment-options": {
"type": "sql",
"sql": "SELECT\n market_segment\nFROM DASH_TEST_DB.SV.TPCH_SALES_SV\nGROUP BY\n market_segment\nORDER BY\n market_segment"
}
},
"tabs": [
{
"tab_id": "tab-0",
"title": "概要",
"tiles": [
{
"tile_id": "sales-by-year",
"type": "chart",
"title": "年別 売上合計の推移",
"datasources": [
"ds-sales-by-year"
],
"spec": {
"mark": "line",
"encoding": {
"x": {
"field": "ORDER_YEAR",
"type": "ordinal",
"title": "注文年"
},
"y": {
"field": "TOTAL_NET_SALES",
"type": "quantitative",
"title": "売上合計"
}
}
},
"scope": "segment_action"
},
{
"tile_id": "sales-by-segment",
"type": "chart",
"title": "市場セグメント別 売上合計",
"datasources": [
"ds-sales-by-segment"
],
"spec": {
"mark": "bar",
"encoding": {
"x": {
"field": "MARKET_SEGMENT",
"type": "nominal",
"title": "市場セグメント",
"sort": "-y"
},
"y": {
"field": "TOTAL_NET_SALES",
"type": "quantitative",
"title": "売上合計"
}
}
},
"interactions": [
{
"trigger": "select",
"operator": "set_filter",
"fields": [
"MARKET_SEGMENT"
],
"targets": [
{
"scope": "segment_action",
"fields": [
"MARKET_SEGMENT"
],
"filter": "market_segment"
}
]
}
]
},
{
"tile_id": "orders-by-priority",
"type": "table",
"title": "注文優先度別 注文件数",
"datasources": [
"ds-orders-by-priority"
],
"spec": {
"columns": [
{
"field": "ORDER_PRIORITY"
},
{
"field": "ORDER_COUNT"
}
]
},
"scope": "segment_action"
}
]
}
],
"layout": {
"tab-0": {
"meta": {
"default": true
},
"positions": {
"sales-by-year": {
"x": 0,
"y": 0,
"w": 12,
"h": 4
},
"sales-by-segment": {
"x": 0,
"y": 4,
"w": 6,
"h": 4
},
"orders-by-priority": {
"x": 6,
"y": 4,
"w": 6,
"h": 4
}
}
}
},
"spec_version": 2,
"spec_revision": 17,
"scopes": {
"segment_action": {}
}
}
4. アーティファクトをPublishしてロールに共有する
作成したダッシュボードを他のユーザーに見せるには、アーティファクトとして共有します。
まず、管理者として閲覧者ロール向けの権限を整えます。閲覧者のクエリは閲覧者自身の権限で実行されますが、Semantic Viewを参照する場合に必要なのはSemantic ViewへのSELECTで、基底テーブル(SNOWFLAKE_SAMPLE_DATA)の権限は付与しません。
USE ROLE ACCOUNTADMIN;
-- 閲覧者ロールに必要な権限(基底テーブルの権限は付与しない)
GRANT USAGE ON WAREHOUSE DASH_TEST_WH TO ROLE DASH_VIEWER;
GRANT USAGE ON DATABASE DASH_TEST_DB TO ROLE DASH_VIEWER;
GRANT USAGE ON SCHEMA DASH_TEST_DB.SV TO ROLE DASH_VIEWER;
GRANT SELECT ON SEMANTIC VIEW DASH_TEST_DB.SV.TPCH_SALES_SV TO ROLE DASH_VIEWER;
続いて、SnowsightでロールをDASH_AUTHOR、ウェアハウスをDASH_TEST_WHにした状態で、Workspacesの.dashファイルを開き、Publish artifactを選択します。

ダイアログが開くので、以下の順に設定します。
Who has accessでDASH_VIEWERロールを追加- アクセスレベルを
Can viewに設定 Publishを選択

Publishを押すとアーティファクトのリンクがクリップボードにコピーされます。

Publishしたダッシュボードは、SnowsightのWorkspaces→Artifactsから確認可能です。


元の.dashとの関係と更新について
共有アーティファクトは、元の.dashとは別に保持されます。
- 元ファイルを削除しても、アーティファクトは残ります。ただし
Open source fileの参照先は存在しないパスを指すようになります - 元ファイルの名前変更や移動では、アーティファクトとの関連付けが古くなったり失われたりします。移動後に再度アーティファクトを作成すると、別アーティファクト・別リンクになる場合があります
- 元ファイルの編集内容は、自動では反映されません。Workspacesから既存アーティファクトへ公開更新(publish)する必要があります。既存アーティファクトを更新する場合、リンクは維持されます
5. SQLでアーティファクトの権限を付与する
アーティファクトは、UUIDで識別されるsecurable objectです。UUIDはアーティファクトのURLの/artifacts/の後ろに含まれます。

付与できる権限は以下の3種類です。
| 権限 | 内容 |
|---|---|
OWNERSHIP |
削除・所有権移転 |
READ |
内容の閲覧 |
WRITE |
閲覧と変更(変更内容のアーティファクトへの公開など) |
先ほどのUIでDASH_VIEWERにCan viewを付与した場合、下記のSQLは必須の手順ではありません。SQLで付与する場合の例として、管理者(ACCOUNTADMIN)が実行するSQLを紹介します。
USE ROLE ACCOUNTADMIN;
-- 閲覧者ロールにREADを付与する場合の例
GRANT READ
ON ARTIFACT "<アーティファクトのUUID>"
TO ROLE DASH_VIEWER;
なお、SHOW ARTIFACTSやDESCRIBE ARTIFACTなどによる確認用のSQLは、2026年11月13日時点では「Coming soon」とされているため、本記事ではSQLによる権限の確認は行いません。
6. 閲覧ユーザーで開いて確認する
作成したユーザー本人はアーティファクトの所有者のため、閲覧の確認は作成者とは別のユーザーで行います。既存の検証用ユーザーに、管理者が閲覧者ロールを付与します。
USE ROLE ACCOUNTADMIN;
GRANT ROLE DASH_VIEWER TO USER <viewer_user>;
なお、検証用ユーザーにはDASH_AUTHORや管理者ロールは付与していない前提です。
このユーザーでSnowsightにログインし、Workspaces→Artifactsから確認してみると、下図のようにダッシュボードを見ることができました。

最後に
Snowflake Dashboards 2.0をSemantic Viewとの組み合わせで試し、ロールを介した共有まで確認しました。
AIファーストなコードベースのダッシュボードで、とても使いやすい印象を受けました!ぜひご活用ください。









