# Steam セール情報ダッシュボード

Steamの現在のセール・割引情報をリアルタイムで取得・表示するWebアプリケーション。

## 概要

- Steam公式の検索APIをスクレイピングし、最大500件のセール中ゲーム情報を取得
- 割引率ランキング、テキスト検索、プラットフォームフィルタ、ソート、ページネーション機能
- 5分間の自動キャッシュでAPI負荷を軽減
- 全UI日本語、日本ストア（JPY）対応

## 技術スタック

| 技術 | バージョン | 用途 |
|---|---|---|
| Next.js | 16.1.6 | フレームワーク (App Router) |
| React | 19.2.3 | UIライブラリ |
| TypeScript | 5.x | 型安全な開発 |
| Tailwind CSS | 4.x | スタイリング (PostCSS統合) |
| ESLint | 9.x | コード品質 |

## ディレクトリ構造

```
steam/
├── package.json
├── tsconfig.json
├── next.config.ts          # Steam CDN画像ホワイトリスト
├── postcss.config.mjs
├── eslint.config.mjs
├── public/
└── src/
    ├── app/
    │   ├── layout.tsx      # ルートレイアウト (lang="ja", Navigation配置)
    │   ├── page.tsx        # / → /deals リダイレクト
    │   ├── error.tsx       # エラーバウンダリ (再試行ボタン)
    │   ├── globals.css     # Steamカラーテーマ定義
    │   ├── deals/
    │   │   ├── page.tsx    # セール一覧ページ
    │   │   └── loading.tsx # スケルトンローディング
    │   └── ranking/
    │       ├── page.tsx    # 割引率ランキングページ
    │       └── loading.tsx # スケルトンローディング
    ├── components/
    │   ├── navigation.tsx      # 粘着ナビゲーション
    │   ├── game-list.tsx       # メイン一覧 (検索・フィルタ・ソート・ページネーション)
    │   ├── game-card.tsx       # ゲームカード (画像・名前・価格・割引率)
    │   ├── search-filter.tsx   # 検索・フィルターUI
    │   ├── sort-select.tsx     # ソート選択UI
    │   ├── discount-badge.tsx  # 割引率バッジ (色分け)
    │   ├── price-display.tsx   # 価格表示 (元価格取消線 + 割引後)
    │   ├── platform-icons.tsx  # Windows/Mac/Linux SVGアイコン
    │   ├── countdown-timer.tsx # セール残り時間 (1分更新)
    │   └── empty-state.tsx     # 検索結果なし表示
    ├── lib/
    │   ├── steam-api.ts    # Steam検索APIスクレイピング (5分キャッシュ, 並列取得)
    │   └── format.ts       # 価格フォーマット・日時変換ユーティリティ
    └── types/
        └── steam.ts        # TypeScript型定義・型ガード
```

## ページ構成

| パス | 説明 | コンポーネント |
|---|---|---|
| `/` | `/deals` にリダイレクト | — |
| `/deals` | セール中ゲーム一覧 | SearchFilter, SortSelect, GameList |
| `/ranking` | 割引率ランキング | SearchFilter, SortSelect, GameList (showRank) |

## データソース

Steam公式検索API（HTMLスクレイピング）:

```
https://store.steampowered.com/search/results/?query=&start={offset}&count=100&specials=1&cc=JP&l=japanese
```

- 100件/ページ × 5ページ = 最大500件を並列取得 (`Promise.all`)
- Next.js `revalidate: 300`（5分キャッシュ）
- 重複排除: `Set<number>` で appId チェック

### 取得データ

```typescript
interface SteamDealItem {
  id: number;                    // Steam App ID
  name: string;                  // ゲーム名
  discount_percent: number;      // 割引率 (%)
  original_price: number | null; // 元価格 (JPYセント単位)
  final_price: number;           // 割引後価格 (JPYセント単位)
  header_image: string;          // ゲーム画像 (460x215)
  windows_available: boolean;
  mac_available: boolean;
  linux_available: boolean;
  discount_expiration: number;   // セール終了時刻 (Unixタイムスタンプ, 0=期限未定)
}
```

## カラーテーマ

Steam公式カラースキームを採用:

```css
--color-steam-darker:   #171a21  /* 最暗背景 */
--color-steam-dark:     #1b2838  /* カード背景 */
--color-steam-medium:   #2a475e  /* ボーダー */
--color-steam-blue:     #1a9fff  /* アクティブ/ホバー */
--color-steam-highlight:#67c1f5  /* リンク強調 */
--color-steam-light:    #c7d5e0  /* 本文テキスト */
--color-steam-sale:     #a4d007  /* セール価格 (黄緑) */
--color-steam-red:      #d94126  /* エラー/高割引 */
```

割引率バッジの色分け:
- 80%以上: 赤 (`steam-red`)
- 60-80%: 緑600
- 30-60%: 緑700
- 30%未満: 緑800

## セットアップ

```bash
cd steam
npm install
npm run dev
# → http://localhost:3000
```

### コマンド一覧

| コマンド | 説明 |
|---|---|
| `npm run dev` | 開発サーバー起動 (ホットリロード) |
| `npm run build` | 本番ビルド |
| `npm start` | 本番サーバー起動 |
| `npm run lint` | ESLint実行 |

### 環境要件

- Node.js 18+
- npm

### 外部依存

- Steam CDN画像 (next.config.ts でホワイトリスト設定済み)
  - `cdn.akamai.steamstatic.com`
  - `shared.akamai.steamstatic.com`
  - `store.akamai.steamstatic.com`
  - `cdn.cloudflare.steamstatic.com`
  - `shared.fastly.steamstatic.com`

## アーキテクチャ補足

- **Server Components**: ページ (deals/page.tsx, ranking/page.tsx) でデータ取得
- **Client Components**: GameList, SearchFilter, SortSelect, Navigation, CountdownTimer で対話的UI
- **URL駆動状態管理**: `useSearchParams()` でフィルタ/ソート/ページを URLクエリパラメータに同期
- **パフォーマンス**: `useMemo()` でフィルタ/ソート結果をメモ化
