kaggle-create-dashboard-page

star 0

ダッシュボードに新しいページを追加する。FastAPI ルーターと htmx テンプレートを作成。

Prgckwb By Prgckwb schedule Updated 3/2/2026

name: kaggle:create-dashboard-page description: ダッシュボードに新しいページを追加する。FastAPI ルーターと htmx テンプレートを作成。 argument-hint: [page-name] disable-model-invocation: true allowed-tools: Read, Write, Edit, Glob

ダッシュボードにページを追加

$ARGUMENTS をページ名として新しいダッシュボードページを作成する。

レイアウト仕様

全ページは以下のデザインシステムに従う:

  • 構成: 左サイドバー(260px 固定)+ メインコンテンツエリアの2カラムレイアウト
  • アクセントカラー: 緑(#22c55e
  • テンプレート継承: {% extends "base.html" %} を使用し、CSS・レイアウト・サイドバーを共有する
  • コンポーネント: .card(カード)、.grid-2 / .grid-3(グリッド)、.btn-primary(緑ボタン)、.page-header(ページヘッダー)

手順

1. base.html の存在確認

app/templates/base.html を Read で読む。

  • ファイルが存在する場合 → 手順3に進む
  • ファイルが存在しない場合 → 手順2に進む

2. base.html を作成(初回のみ)

app/templates/base.html を以下の内容で Write する:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Dashboard{% endblock %} - Kaggle Dashboard</title>
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
    <style>
        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
        :root {
            --color-accent: #22c55e;
            --color-accent-hover: #16a34a;
            --color-accent-light: #dcfce7;
            --color-bg: #f8f9fa;
            --color-bg-sidebar: #ffffff;
            --color-bg-card: #ffffff;
            --color-text: #1a1a2e;
            --color-text-secondary: #6b7280;
            --color-text-muted: #9ca3af;
            --color-border: #e5e7eb;
            --sidebar-width: 260px;
            --radius: 12px;
            --radius-sm: 8px;
            --shadow: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.1);
            --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            --font-mono: 'SF Mono', 'Fira Code', monospace;
        }
        body { font-family: var(--font-sans); line-height: 1.6; color: var(--color-text); background: var(--color-bg); }
        .layout { display: flex; min-height: 100vh; }
        .sidebar {
            width: var(--sidebar-width); background: var(--color-bg-sidebar);
            border-right: 1px solid var(--color-border); padding: 1.5rem 1rem;
            position: fixed; top: 0; left: 0; bottom: 0;
            display: flex; flex-direction: column; overflow-y: auto;
        }
        .sidebar-brand { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.75rem; margin-bottom: 1.5rem; font-weight: 700; font-size: 1rem; }
        .brand-icon { font-size: 1.4rem; }
        .sidebar-nav { display: flex; flex-direction: column; gap: 0.25rem; flex: 1; }
        .nav-item {
            display: flex; align-items: center; gap: 0.75rem;
            padding: 0.625rem 0.75rem; border-radius: var(--radius-sm);
            color: var(--color-text-secondary); text-decoration: none;
            font-size: 0.9rem; transition: background 0.15s ease, color 0.15s ease;
        }
        .nav-item:hover { background: var(--color-bg); color: var(--color-text); }
        .nav-item.active { background: var(--color-accent-light); color: var(--color-accent); font-weight: 600; }
        .sidebar-footer { margin-top: auto; padding-top: 1rem; border-top: 1px solid var(--color-border); }
        .main { margin-left: var(--sidebar-width); flex: 1; padding: 2rem 3rem; max-width: calc(100% - var(--sidebar-width)); }
        .page-header { margin-bottom: 2rem; }
        .page-header h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 0.25rem; }
        .page-subtitle { color: var(--color-text-secondary); font-size: 0.95rem; }
        .card { background: var(--color-bg-card); border-radius: var(--radius); padding: 1.5rem; margin-bottom: 1rem; box-shadow: var(--shadow); border: 1px solid var(--color-border); }
        .card h2 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.75rem; }
        .card p { color: var(--color-text-secondary); font-size: 0.95rem; }
        .grid { display: grid; gap: 1rem; }
        .grid-2 { grid-template-columns: repeat(2, 1fr); }
        .grid-3 { grid-template-columns: repeat(3, 1fr); }
        .btn { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.625rem 1.25rem; border-radius: var(--radius-sm); font-size: 0.9rem; font-weight: 500; cursor: pointer; border: none; transition: background 0.15s ease; text-decoration: none; font-family: var(--font-sans); }
        .btn-primary { background: var(--color-accent); color: white; }
        .btn-primary:hover { background: var(--color-accent-hover); }
        .btn-outline { background: transparent; border: 1px solid var(--color-border); color: var(--color-text); }
        .btn-outline:hover { background: var(--color-bg); }
        code { background: var(--color-bg); padding: 0.2em 0.4em; border-radius: 4px; font-family: var(--font-mono); font-size: 0.875em; color: var(--color-accent-hover); }
        table { width: 100%; border-collapse: collapse; }
        th, td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid var(--color-border); }
        th { font-weight: 600; color: var(--color-text-secondary); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; }
        td { font-size: 0.95rem; }
        .text-muted { color: var(--color-text-muted); font-size: 0.85rem; }
        .accent { color: var(--color-accent); }
        .mt-1 { margin-top: 0.5rem; }
        .mt-2 { margin-top: 1rem; }
        .mt-4 { margin-top: 2rem; }
    </style>
    {% block head %}{% endblock %}
</head>
<body>
    <div class="layout">
        <aside class="sidebar">
            <div class="sidebar-brand">
                <span class="brand-icon">📊</span>
                <span>Kaggle Dashboard</span>
            </div>
            <nav class="sidebar-nav">
                <a href="/" class="nav-item {% if active_page == 'home' %}active{% endif %}">🏠 Home</a>
            </nav>
            <div class="sidebar-footer">
                <p class="text-muted">Kaggle Competition Dashboard</p>
            </div>
        </aside>
        <main class="main">
            {% block content %}{% endblock %}
        </main>
    </div>
</body>
</html>

次に app/templates/index.htmlWrite で以下の内容に書き換える:

{% extends "base.html" %}

{% block title %}Home{% endblock %}

{% block content %}
<div class="page-header">
    <h1>Kaggle Competition Dashboard</h1>
    <p class="page-subtitle">EDA や実験結果の可視化用ダッシュボードです。</p>
</div>

<div class="grid grid-2">
    <div class="card">
        <h2>Welcome</h2>
        <p>新しいページは <code>app/pages/</code> に追加してください。</p>
        <p class="mt-1">スキル <code>/create-dashboard-page [page-name]</code> で自動生成できます。</p>
    </div>
    <div class="card">
        <h2>Getting Started</h2>
        <p>実験を開始するには <code>src/</code> ディレクトリに実験フォルダを追加してください。</p>
    </div>
</div>
{% endblock %}

次に app/main.pyindex 関数のテンプレートレスポンスのコンテキストに "active_page": "home"Edit で追加する。

3. ルーターを作成

app/pages/{page-name}.pyWrite で作成する。{page-name} はハイフン区切り、{page_name_snake} はスネークケース(例: page-namepage_name)に変換する:

from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from pathlib import Path

router = APIRouter()
templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")


@router.get("/{page-name}", response_class=HTMLResponse)
async def {page_name_snake}(request: Request):
    return templates.TemplateResponse(
        "{page-name}.html",
        {"request": request, "title": "{Page Name}", "active_page": "{page-name}"},
    )

4. テンプレートを作成

app/templates/{page-name}.htmlWrite で作成する:

{% extends "base.html" %}

{% block title %}{{ title }}{% endblock %}

{% block content %}
<div class="page-header">
    <h1>{Page Name}</h1>
    <p class="page-subtitle">ページの説明をここに記載</p>
</div>

<div class="card">
    <!-- コンテンツをここに追加 -->
    <h2>セクション名</h2>
    <p>説明テキスト</p>
</div>
{% endblock %}

5. app/main.py にルーターを登録

app/main.pyRead して現在の内容を確認した後、Edit で以下を追加する:

  • import を追加: from app.pages.{page_name} import router as {page_name}_router
  • app.include_router({page_name}_router) を追加

6. base.html のサイドバーナビゲーションを更新

app/templates/base.htmlEdit で変更する。

<a href="/" class="nav-item ...">🏠 Home</a> の直後に以下を追加する:

                <a href="/{page-name}" class="nav-item {% if active_page == '{page-name}' %}active{% endif %}">{Page Name}</a>

7. 完了報告

  • 作成・変更したファイルの一覧を表示
  • アクセス URL を案内: http://localhost:8000/{page-name}
  • 起動コマンドを案内: uv run uvicorn app.main:app --reload
Install via CLI
npx skills add https://github.com/Prgckwb/icdar-cidpen-2026 --skill kaggle-create-dashboard-page
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator