Next.jsを企業向けに最適化！2025年のアプリ構築術 | エピソード4: 管理ダッシュボードの構築

## 目標
- Next.jsで管理ダッシュボードを作成する。
- Chart.jsを使ってデータを視覚化する。
- 管理者権限を設定し、ダッシュボードを保護する。

---

## 1. 企業向けアプリにおけるダッシュボードの役割
企業向けアプリでは、管理者がデータを監視・管理するためのダッシュボードが重要です。このエピソードでは、ブログに管理者専用のダッシュボードを追加し、投稿数やユーザー統計を視覚化します。

---

## 2. 必要なライブラリのインストール
視覚化のためにChart.jsを導入します。

### 手順：
ターミナルで以下を実行：
```
npm install chart.js react-chartjs-2
```

---

## 3. ダッシュボードページの作成
管理者向けのダッシュボードを実装します。

### `app/admin/dashboard/page.tsx`の作成：
```tsx
'use client';

import { useSession } from 'next-auth/react';
import { redirect } from 'next/navigation';
import { supabase } from '../../../lib/supabase';
import { Bar } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);

export default function Dashboard() {
  const { data: session } = useSession();

  if (!session || session.user?.email !== 'admin@example.com') {
    redirect('/'); // 管理者以外はリダイレクト
  }

  const fetchStats = async () => {
    const { data: posts } = await supabase.from('posts').select('created_at');
    const { data: users } = await supabase.from('users').select('id');
    return { posts: posts || [], users: users || [] };
  };

  const stats = await fetchStats();

  const postDates = stats.posts.reduce((acc, post) => {
    const date = new Date(post.created_at).toLocaleDateString('ja-JP');
    acc[date] = (acc[date] || 0) + 1;
    return acc;
  }, {});

  const chartData = {
    labels: Object.keys(postDates),
    datasets: [
      {
        label: '投稿数',
        data: Object.values(postDates),
        backgroundColor: 'rgba(54, 162, 235, 0.6)',
      },
    ],
  };

  return (
    <div className="max-w-4xl mx-auto p-6">
      <h1 className="text-3xl font-bold text-blue-600 mb-6">管理ダッシュボード</h1>
      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        <div className="bg-white p-4 rounded shadow">
          <h2 className="text-xl font-semibold mb-4">投稿統計</h2>
          <Bar data={chartData} options={{ responsive: true }} />
        </div>
        <div className="bg-white p-4 rounded shadow">
          <h2 className="text-xl font-semibold mb-4">ユーザー情報</h2>
          <p>総ユーザー数: {stats.users.length}</p>
        </div>
      </div>
    </div>
  );
}
```

### ナビゲーションに追加：
`app/layout.tsx`を編集：
```tsx
import '../globals.css';
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import { i18n } from 'next-i18next';
import { auth } from 'next-auth';
import { signIn, signOut } from 'next-auth/react';

export const metadata = {
  title: 'Next.js 2025ブログ',
  description: '2025年の最新技術で作られたブログサイト',
};

export default async function RootLayout({ children }) {
  const { t } = useTranslation('common');
  const session = await auth();

  return (
    <html lang={i18n?.language}>
      <body className="font-sans antialiased">
        <header className="bg-blue-800 text-white p-4">
          <nav className="max-w-4xl mx-auto flex justify-between items-center">
            <Link href="/" className="text-xl font-bold">{t('home')}</Link>
            <div className="space-x-4">
              <Link href="/about" className="hover:underline">{t('about')}</Link>
              <Link href="/add-post" className="hover:underline">{t('addPost')}</Link>
              {session?.user?.email === 'admin@example.com' && (
                <Link href="/admin/dashboard" className="hover:underline">ダッシュボード</Link>
              )}
              <Link href="/" locale="ja" className="hover:underline">日本語</Link>
              <Link href="/" locale="en" className="hover:underline">English</Link>
              {session ? (
                <>
                  <span>{session.user?.name}</span>
                  <button onClick={() => signOut()} className="hover:underline">ログアウト</button>
                </>
              ) : (
                <button onClick={() => signIn('google')} className="hover:underline">Googleでログイン</button>
              )}
            </div>
          </nav>
        </header>
        <main>{children}</main>
        <footer className="bg-gray-200 p-4 text-center text-gray-600">
          © 2025 Next.js学習シリーズ
        </footer>
      </body>
    </html>
  );
}
```

---

## 実践：管理者ダッシュボード
- **統計**：投稿数の棒グラフを表示。
- **ユーザー情報**：登録ユーザー数を表示。
- **保護**：管理者（例：`admin@example.com`）のみアクセス可能。

**確認**：
- `npm run dev`で起動し、`http://localhost:3000/admin/dashboard`で管理者ログイン後に表示を確認。

---

## エピソード4の終了
- 管理ダッシュボードを作成し、データを視覚化しました。
- 管理者権限でアクセスを保護しました。

**次回のエピソード**：Redisでスケーラビリティを強化し、大規模運用に対応します。

---

この記事が役に立ったと思ったら、ぜひ「いいね」を押して、ストックしていただければ嬉しいです！次回のエピソードもお楽しみに！