mcpField
B
build-dashboard

build-dashboard

github.com/anthropicsデータ・データベース33.6k9.2k 回インストール1 日前に更新
リポジトリ

build-dashboard について

build-dashboardは、データや要件の説明から、自己完結型のインタラクティブなHTMLダッシュボードを作るスキルです。分析やレポート向けに、グラフ、フィルター、KPIカード、テーブルをまとめ、ブラウザーでオフライン表示できるファイルを生成します。

主な機能

  1. 01自己完結型HTMLダッシュボードの生成
  2. 02グラフとKPIカードの作成
  3. 03フィルターと日付範囲の追加
  4. 04ソート可能なデータテーブル
  5. 05レスポンシブレイアウトと印刷用スタイル
  6. 06オフライン用データの埋め込み

活用シーン

  1. 01売上トレンドと地域別実績の分析
  2. 02チケットの優先度と解決時間の確認
  3. 03サンプルデータを使ったSaaS指標ページの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/anthropics/knowledge-work-plugins --skill build-dashboard

インストール後、Claude Code で/build-dashboardを入力して読み込まれたか確認します。

詳細

/build-dashboard - インタラクティブなダッシュボードを作成する

見慣れないプレースホルダーがある場合や、接続されているツールを確認する必要がある場合は、CONNECTORS.md を参照してください。

グラフ、フィルター、テーブル、プロフェッショナルなスタイルを備えた、自己完結型のインタラクティブな HTML ダッシュボードを作成します。ブラウザーで直接開くことができ、サーバーや依存関係は不要です。

使用方法

/build-dashboard <description of dashboard> [data source]

ワークフロー

1. ダッシュボードの要件を把握する

以下を確認します。

  • 目的:経営層向けの概要、運用状況のモニタリング、詳細分析、チームのレポート
  • 対象ユーザー:このダッシュボードを利用するのは誰ですか?
  • 主要指標:最も重要な数値は何ですか?
  • ディメンション:ユーザーがフィルターや切り分けに使えるようにする項目は何ですか?
  • データソース:ライブクエリ、貼り付けたデータ、CSV ファイル、サンプルデータ
2. データを収集する

データウェアハウスが接続されている場合:

  1. 必要なデータをクエリします
  2. 結果を JSON として HTML ファイル内に埋め込みます

データが貼り付けられている、またはアップロードされている場合:

  1. データを解析してクリーンアップします
  2. ダッシュボードに JSON として埋め込みます

データがなく、説明のみをもとに作業する場合:

  1. 説明されたスキーマに合致する、現実的なサンプルデータセットを作成します
  2. サンプルデータを使用していることをダッシュボードに明記します
  3. 実データに差し替える手順を提示します
3. ダッシュボードのレイアウトを設計する

標準的なダッシュボードのレイアウトパターンに従います。

┌──────────────────────────────────────────────────┐
│  Dashboard Title                    [Filters ▼]  │
├────────────┬────────────┬────────────┬───────────┤
│  KPI Card  │  KPI Card  │  KPI Card  │ KPI Card  │
├────────────┴────────────┼────────────┴───────────┤
│                         │                        │
│    Primary Chart        │   Secondary Chart      │
│    (largest area)       │                        │
│                         │                        │
├─────────────────────────┴────────────────────────┤
│                                                  │
│    Detail Table (sortable, scrollable)           │
│                                                  │
└──────────────────────────────────────────────────┘

コンテンツに合わせてレイアウトを調整します:

  • 上部に主要な数値を示す KPI カードを 2~4 個配置します
  • 中央のセクションに、推移や内訳を示すグラフを 1~3 個配置します
  • 必要に応じて、下部に詳細データのドリルダウン用テーブルを配置します
  • 複雑さに応じて、ヘッダーまたはサイドバーにフィルターを配置します
4. HTML ダッシュボードを作成する

以下のベーステンプレートを使用して、自己完結型の HTML ファイルを 1 つ生成します。ファイルには以下が含まれます。

構造(HTML):

  • セマンティックな HTML5 レイアウト
  • CSS Grid または Flexbox を使用したレスポンシブグリッド
  • フィルターコントロール(ドロップダウン、日付選択、切り替え)
  • 値とラベルを含む KPI カード
  • グラフのコンテナー
  • ヘッダーで並べ替え可能なデータテーブル

スタイル(CSS):

  • プロフェッショナルな配色(すっきりとした白とグレーを基調に、データにはアクセントカラーを使用)
  • 控えめな影を付けたカードベースのレイアウト
  • 一貫したタイポグラフィ(読み込みを高速化するシステムフォント)
  • さまざまな画面サイズで動作するレスポンシブデザイン
  • 印刷に適したスタイル

インタラクティブ機能(JavaScript):

  • インタラクティブなグラフには Chart.js を使用します(CDN 経由で読み込みます)
  • すべてのグラフとテーブルを同時に更新するフィルタードロップダウン
  • 並べ替え可能なテーブル列
  • グラフのホバーツールチップ
  • 数値の書式設定(桁区切り、通貨、パーセンテージ)

データ(埋め込み JSON):

  • すべてのデータを JavaScript 変数として HTML に直接埋め込みます
  • 外部からのデータ取得は不要です
  • ダッシュボードは完全にオフラインで動作します
5. グラフの種類を実装する

すべてのグラフに Chart.js を使用します。ダッシュボードでよく使われるグラフのパターンは以下のとおりです。

  • 折れ線グラフ:時系列の推移
  • 棒グラフ:カテゴリーの比較
  • ドーナツグラフ:構成比(カテゴリーが 6 未満の場合)
  • 積み上げ棒グラフ:時系列での構成比
  • 複合グラフ(棒+折れ線):数量と率を重ねて表示

グラフの種類ごとに、以下の Chart.js 統合パターンを使用します。

6. インタラクティブ機能を追加する

以下のフィルターおよびインタラクティブ機能の実装パターンを使用して、ドロップダウンフィルター、日付範囲フィルター、複合フィルターロジック、並べ替え可能なテーブル、グラフの更新を実装します。

7. 保存して開く
  1. 説明的な名前(例:sales_dashboard.html)を付けて、ダッシュボードを HTML ファイルとして保存します
  2. ユーザーの既定のブラウザーで開きます
  3. 正しく表示されることを確認します
  4. データの更新方法やカスタマイズ方法を案内します

ベーステンプレート

すべてのダッシュボードは、以下の構造に従います。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard Title</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]" integrity="sha384-jb8JQMbMoBUzgWatfe6COACi2ljcDdZQ2OxczGA3bGNeWe+6DChMTBJemed7ZnvJ" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]" integrity="sha384-cVMg8E3QFwTvGCDuK+ET4PD341jF3W8nO1auiXfuZNQkzbUUiBGLsIQUE+b1mxws" crossorigin="anonymous"></script>
    <style>
        /* Dashboard styles go here */
    </style>
</head>
<body>
    <div class="dashboard-container">
        <header class="dashboard-header">
            <h1>Dashboard Title</h1>
            <div class="filters">
                
            </div>
        </header>

        <section class="kpi-row">
            
        </section>

        <section class="chart-row">
            
        </section>

        <section class="table-section">
            
        </section>

        <footer class="dashboard-footer">
            <span>Data as of: <span id="data-date"></span></span>
        </footer>
    </div>

    <script>
        // Embedded data
        const DATA = [];

        // Dashboard logic
        class Dashboard {
            constructor(data) {
                this.rawData = data;
                this.filteredData = data;
                this.charts = {};
                this.init();
            }

            init() {
                this.setupFilters();
                this.renderKPIs();
                this.renderCharts();
                this.renderTable();
            }

            applyFilters() {
                // Filter logic
                this.filteredData = this.rawData.filter(row => {
                    // Apply each active filter
                    return true; // placeholder
                });
                this.renderKPIs();
                this.updateCharts();
                this.renderTable();
            }

            // ... methods for each section
        }

        const dashboard = new Dashboard(DATA);
    </script>
</body>
</html>

KPI カードのパターン

<div class="kpi-card">
    <div class="kpi-label">Total Revenue</div>
    <div class="kpi-value" id="kpi-revenue">$0</div>
    <div class="kpi-change positive" id="kpi-revenue-change">+0%</div>
</div>
function renderKPI(elementId, value, previousValue, format = 'number') {
    const el = document.getElementById(elementId);
    const changeEl = document.getElementById(elementId + '-change');

    // Format the value
    el.textContent = formatValue(value, format);

    // Calculate and display change
    if (previousValue && previousValue !== 0) {
        const pctChange = ((value - previousValue) / previousValue) * 100;
        const sign = pctChange >= 0 ? '+' : '';
        changeEl.textContent = `${sign}${pctChange.toFixed(1)}% vs prior period`;
        changeEl.className = `kpi-change ${pctChange >= 0 ? 'positive' : 'negative'}`;
    }
}

function formatValue(value, format) {
    switch (format) {
        case 'currency':
            if (value >= 1e6) return `$${(value / 1e6).toFixed(1)}M`;
            if (value >= 1e3) return `$${(value / 1e3).toFixed(1)}K`;
            return `$${value.toFixed(0)}`;
        case 'percent':
            return `${value.toFixed(1)}%`;
        case 'number':
            if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
            if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
            return value.toLocaleString();
        default:
            return value.toString();
    }
}

Chart.js の統合

グラフコンテナーのパターン
<div class="chart-container">
    <h3 class="chart-title">Monthly Revenue Trend</h3>
    <canvas id="revenue-chart"></canvas>
</div>
折れ線グラフ
function createLineChart(canvasId, labels, datasets) {
    const ctx = document.getElementById(canvasId).getContext('2d');
    return new Chart(ctx, {
        type: 'line',
        data: {
            labels: labels,
            datasets: datasets.map((ds, i) => ({
                label: ds.label,
                data: ds.data,
                borderColor: COLORS[i % COLORS.length],
                backgroundColor: COLORS[i % COLORS.length] + '20',
                borderWidth: 2,
                fill: ds.fill || false,
                tension: 0.3,
                pointRadius: 3,
                pointHoverRadius: 6,
            }))
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            interaction: {
                mode: 'index',
                intersect: false,
            },
            plugins: {
                legend: {
                    position: 'top',
                    labels: { usePointStyle: true, padding: 20 }
                },
                tooltip: {
                    callbacks: {
                        label: function(context) {
                            return `${context.dataset.label}: ${formatValue(context.parsed.y, 'currency')}`;
                        }
                    }
                }
            },
            scales: {
                x: {
                    grid: { display: false }
                },
                y: {
                    beginAtZero: true,
                    ticks: {
                        callback: function(value) {
                            return formatValue(value, 'currency');
                        }
                    }
                }
            }
        }
    });
}
棒グラフ
function createBarChart(canvasId, labels, data, options = {}) {
    const ctx = document.getElementById(canvasId).getContext('2d');
    const isHorizontal = options.horizontal || labels.length > 8;

    return new Chart(ctx, {
        type: 'bar',
        data: {
            labels: labels,
            datasets: [{
                label: options.label || 'Value',
                data: data,
                backgroundColor: options.colors || COLORS.map(c => c + 'CC'),
                borderColor: options.colors || COLORS,
                borderWidth: 1,
                borderRadius: 4,
            }]
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            indexAxis: isHorizontal ? 'y' : 'x',
            plugins: {
                legend: { display: false },
                tooltip: {
                    callbacks: {
                        label: function(context) {
                            return formatValue(context.parsed[isHorizontal ? 'x' : 'y'], options.format || 'number');
                        }
                    }
                }
            },
            scales: {
                x: {
                    beginAtZero: true,
                    grid: { display: isHorizontal },
                    ticks: isHorizontal ? {
                        callback: function(value) {
                            return formatValue(value, options.format || 'number');
                        }
                    } : {}
                },
                y: {
                    beginAtZero: !isHorizontal,
                    grid: { display: !isHorizontal },
                    ticks: !isHorizontal ? {
                        callback: function(value) {
                            return formatValue(value, options.format || 'number');
                        }
                    } : {}
                }
            }
        }
    });
}
ドーナツグラフ
function createDoughnutChart(canvasId, labels, data) {
    const ctx = document.getElementById(canvasId).getContext('2d');
    return new Chart(ctx, {
        type: 'doughnut',
        data: {
            labels: labels,
            datasets: [{
                data: data,
                backgroundColor: COLORS.map(c => c + 'CC'),
                borderColor: '#ffffff',
                borderWidth: 2,
            }]
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            cutout: '60%',
            plugins: {
                legend: {
                    position: 'right',
                    labels: { usePointStyle: true, padding: 15 }
                },
                tooltip: {
                    callbacks: {
                        label: function(context) {
                            const total = context.dataset.data.reduce((a, b) => a + b, 0);
                            const pct = ((context.parsed / total) * 100).toFixed(1);
                            return `${context.label}: ${formatValue(context.parsed, 'number')} (${pct}%)`;
                        }
                    }
                }
            }
        }
    });
}
フィルター変更時のグラフ更新
function updateChart(chart, newLabels, newData) {
    chart.data.labels = newLabels;

    if (Array.isArray(newData[0])) {
        // Multiple datasets
        newData.forEach((data, i) => {
            chart.data.datasets[i].data = data;
        });
    } else {
        chart.data.datasets[0].data = newData;
    }

    chart.update('none'); // 'none' disables animation for instant update
}

フィルターとインタラクティブ機能の実装

ドロップダウンフィルター
<div class="filter-group">
    <label for="filter-region">Region</label>
    <select id="filter-region" onchange="dashboard.applyFilters()">
        <option value="all">All Regions</option>
    </select>
</div>
function populateFilter(selectId, data, field) {
    const select = document.getElementById(selectId);
    const values = [...new Set(data.map(d => d[field]))].sort();

    // Keep the "All" option, add unique values
    values.forEach(val => {
        const option = document.createElement('option');
        option.value = val;
        option.textContent = val;
        select.appendChild(option);
    });
}

function getFilterValue(selectId) {
    const val = document.getElementById(selectId).value;
    return val === 'all' ? null : val;
}
日付範囲フィルター
<div class="filter-group">
    <label>Date Range</label>
    <input type="date" id="filter-date-start" onchange="dashboard.applyFilters()">
    <span>to</span>
    <input type="date" id="filter-date-end" onchange="dashboard.applyFilters()">
</div>
function filterByDateRange(data, dateField, startDate, endDate) {
    return data.filter(row => {
        const rowDate = new Date(row[dateField]);
        if (startDate && rowDate < new Date(startDate)) return false;
        if (endDate && rowDate > new Date(endDate)) return false;
        return true;
    });
}
複合フィルターロジック
applyFilters() {
    const region = getFilterValue('filter-region');
    const category = getFilterValue('filter-category');
    const startDate = document.getElementById('filter-date-start').value;
    const endDate = document.getElementById('filter-date-end').value;

    this.filteredData = this.rawData.filter(row => {
        if (region && row.region !== region) return false;
        if (category && row.category !== category) return false;
        if (startDate && row.date < startDate) return false;
        if (endDate && row.date > endDate) return false;
        return true;
    });

    this.renderKPIs();
    this.updateCharts();
    this.renderTable();
}
並べ替え可能なテーブル
function renderTable(containerId, data, columns) {
    const container = document.getElementById(containerId);
    let sortCol = null;
    let sortDir = 'desc';

    function render(sortedData) {
        let html = '<table class="data-table">';

        // Header
        html += '<thead><tr>';
        columns.forEach(col => {
            const arrow = sortCol === col.field
                ? (sortDir === 'asc' ? ' ▲' : ' ▼')
                : '';
            html += `<th onclick="sortTable('${col.field}')" style="cursor:pointer">${col.label}${arrow}</th>`;
        });
        html += '</tr></thead>';

        // Body
        html += '<tbody>';
        sortedData.forEach(row => {
            html += '<tr>';
            columns.forEach(col => {
                const value = col.format ? formatValue(row[col.field], col.format) : row[col.field];
                html += `<td>${value}</td>`;
            });
            html += '</tr>';
        });
        html += '</tbody></table>';

        container.innerHTML = html;
    }

    window.sortTable = function(field) {
        if (sortCol === field) {
            sortDir = sortDir === 'asc' ? 'desc' : 'asc';
        } else {
            sortCol = field;
            sortDir = 'desc';
        }
        const sorted = [...data].sort((a, b) => {
            const aVal = a[field], bVal = b[field];
            const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
            return sortDir === 'asc' ? cmp : -cmp;
        });
        render(sorted);
    };

    render(data);
}

ダッシュボードの CSS スタイル

カラーシステム
:root {
    /* Background layers */
    --bg-primary: #f8f9fa;
    --bg-card: #ffffff;
    --bg-header: #1a1a2e;

    /* Text */
    --text-primary: #212529;
    --text-secondary: #6c757d;
    --text-on-dark: #ffffff;

    /* Accent colors for data */
    --color-1: #4C72B0;
    --color-2: #DD8452;
    --color-3: #55A868;
    --color-4: #C44E52;
    --color-5: #8172B3;
    --color-6: #937860;

    /* Status colors */
    --positive: #28a745;
    --negative: #dc3545;
    --neutral: #6c757d;

    /* Spacing */
    --gap: 16px;
    --radius: 8px;
}
レイアウト
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.5;
}

.dashboard-container {
    max-width: 1400px;
    margin: 0 auto;
    padding: var(--gap);
}

.dashboard-header {
    background: var(--bg-header);
    color: var(--text-on-dark);
    padding: 20px 24px;
    border-radius: var(--radius);
    margin-bottom: var(--gap);
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
}

.dashboard-header h1 {
    font-size: 20px;
    font-weight: 600;
}
KPI カード
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--gap);
    margin-bottom: var(--gap);
}

.kpi-card {
    background: var(--bg-card);
    border-radius: var(--radius);
    padding: 20px 24px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

.kpi-label {
    font-size: 13px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 4px;
}

.kpi-value {
    font-size: 28px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 4px;
}

.kpi-change {
    font-size: 13px;
    font-weight: 500;
}

.kpi-change.positive { color: var(--positive); }
.kpi-change.negative { color: var(--negative); }
チャートコンテナー
.chart-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
    gap: var(--gap);
    margin-bottom: var(--gap);
}

.chart-container {
    background: var(--bg-card);
    border-radius: var(--radius);
    padding: 20px 24px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

.chart-container h3 {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 16px;
}

.chart-container canvas {
    max-height: 300px;
}
フィルター
.filters {
    display: flex;
    gap: 12px;
    align-items: center;
    flex-wrap: wrap;
}

.filter-group {
    display: flex;
    align-items: center;
    gap: 6px;
}

.filter-group label {
    font-size: 12px;
    color: rgba(255, 255, 255, 0.7);
}

.filter-group select,
.filter-group input[type="date"] {
    padding: 6px 10px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.1);
    color: var(--text-on-dark);
    font-size: 13px;
}

.filter-group select option {
    background: var(--bg-header);
    color: var(--text-on-dark);
}
データテーブル
.table-section {
    background: var(--bg-card);
    border-radius: var(--radius);
    padding: 20px 24px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    overflow-x: auto;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.data-table thead th {
    text-align: left;
    padding: 10px 12px;
    border-bottom: 2px solid #dee2e6;
    color: var(--text-secondary);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
    user-select: none;
}

.data-table thead th:hover {
    color: var(--text-primary);
    background: #f8f9fa;
}

.data-table tbody td {
    padding: 10px 12px;
    border-bottom: 1px solid #f0f0f0;
}

.data-table tbody tr:hover {
    background: #f8f9fa;
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}
レスポンシブデザイン
@media (max-width: 768px) {
    .dashboard-header {
        flex-direction: column;
        align-items: flex-start;
    }

    .kpi-row {
        grid-template-columns: repeat(2, 1fr);
    }

    .chart-row {
        grid-template-columns: 1fr;
    }

    .filters {
        flex-direction: column;
        align-items: flex-start;
    }
}

@media print {
    body { background: white; }
    .dashboard-container { max-width: none; }
    .filters { display: none; }
    .chart-container { break-inside: avoid; }
    .kpi-card { border: 1px solid #dee2e6; box-shadow: none; }
}

大規模データセットのパフォーマンスに関する考慮事項

データサイズのガイドライン
データサイズアプローチ
<1,000 rowsHTML に直接埋め込みます。完全なインタラクティブ機能を利用できます。
1,000 - 10,000 rowsHTML に埋め込みます。グラフ用に事前集計が必要になる場合があります。
10,000 - 100,000 rowsサーバー側で事前集計します。集計済みデータのみを埋め込みます。
>100,000 rowsクライアント側ダッシュボードには適していません。BI ツールを使用するか、ページネーションを実装してください。
事前集計パターン

生データを埋め込んでブラウザーで集計する代わりに、次のようにします。

// DON'T: embed 50,000 raw rows
const RAW_DATA = [/* 50,000 rows */];

// DO: pre-aggregate before embedding
const CHART_DATA = {
    monthly_revenue: [
        { month: '2024-01', revenue: 150000, orders: 1200 },
        { month: '2024-02', revenue: 165000, orders: 1350 },
        // ... 12 rows instead of 50,000
    ],
    top_products: [
        { product: 'Widget A', revenue: 45000 },
        // ... 10 rows
    ],
    kpis: {
        total_revenue: 1980000,
        total_orders: 15600,
        avg_order_value: 127,
    }
};
グラフのパフォーマンス
  • 折れ線グラフは系列あたりのデータポイントを 500 未満に制限します(必要に応じてダウンサンプリングします)
  • 棒グラフのカテゴリ数は 50 未満に制限します
  • 散布図は 1,000 ポイントを上限とします(より大きなデータセットではサンプリングを使用します)
  • 多数のグラフがあるダッシュボードではアニメーションを無効にします。Chart.js のオプションで animation: false を指定します
  • フィルターによる更新には Chart.update() ではなく Chart.update('none') を使用します
DOM のパフォーマンス
  • データテーブルの表示行数は 100~200 行に制限します。それ以上の場合はページネーションを追加します。
  • グラフの更新を同期させるには requestAnimationFrame を使用します
  • フィルターの変更時に DOM 全体を再構築せず、変更された要素のみを更新します
// Efficient table pagination
function renderTablePage(data, page, pageSize = 50) {
    const start = page * pageSize;
    const end = Math.min(start + pageSize, data.length);
    const pageData = data.slice(start, end);
    // Render only pageData
    // Show pagination controls: "Showing 1-50 of 2,340"
}

例

/build-dashboard Monthly sales dashboard with revenue trend, top products, and regional breakdown. Data is in the orders table.
/build-dashboard Here's our support ticket data [pastes CSV]. Build a dashboard showing volume by priority, response time trends, and resolution rates.
/build-dashboard Create a template executive dashboard for a SaaS company showing MRR, churn, new customers, and NPS. Use sample data.

ヒント

  • ダッシュボードは完全に自己完結した HTML ファイルです。ファイルを送るだけで誰とでも共有できます
  • リアルタイムダッシュボードには、代わりに BI ツールへの接続を検討してください。これらのダッシュボードは特定時点のスナップショットです
  • スタイルを変更するには、「ダークモード」または「プレゼンテーションモード」を指定してください
  • ブランドに合わせた特定の配色を指定できます

コメント