# 画面設計書 10-What's New画面

## 概要

本ドキュメントは、Ghost管理画面のWhat's New画面の設計内容を記載する。

### 本画面の処理概要

What's New画面は、Ghostの新機能や更新情報を表示する画面である。ghost.orgのChangelogから最新の更新情報を取得し、管理者に対してプロダクトの新機能や改善点を紹介する。

**業務上の目的・背景**：Ghost開発チームからの製品アップデート情報を管理者に伝達する。新機能のリリース、バグ修正、改善点などの情報をタイムリーに提供し、ユーザーが最新の機能を活用できるようサポートする。また、通知購読への誘導リンクを提供する。

**画面へのアクセス方法**：
- サイドナビゲーションまたはユーザーメニューの「What's new」リンクをクリック
- URL直接アクセス（`/ghost/whatsnew`）

**主要な操作・処理内容**：
1. ghost.org/changelog.jsonから更新情報を取得
2. 更新エントリーの一覧表示
3. 最終閲覧日時の記録・更新
4. 通知購読ページへのリンク提供

**画面遷移**：
- 遷移元：サイドナビゲーション、ユーザーメニュー
- 遷移先：外部サイト（ghost.org/changelog）、記事詳細ページ（外部）

**権限による表示制御**：認証済みユーザーのみアクセス可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 62 | 管理者通知 | 主機能 | Ghost新機能・更新情報の表示 |

## 画面種別

表示画面

## URL/ルーティング

- **URL**: `/ghost/whatsnew`
- **Emberルート名**: `whatsnew`
- **ルートファイル**: `ghost/admin/app/routes/whatsnew.js`
- **テンプレートファイル**: `ghost/admin/app/templates/whatsnew.hbs`

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | バリデーション | 備考 |
|--------|--------|-----|------|----------|---------------|------|
| エントリースラグ | entry | string | 任意 | URL Query | - | スクロール位置の指定 |

## 表示項目

| 項目名 | 項目ID | 型 | 備考 |
|--------|--------|-----|------|
| ヘッダー | gh-whats-new-heading | テキスト | ギフトアイコン + "What's new?" |
| 通知登録ボタン | - | リンクボタン | ghost.org/changelog/#/portal/signupへ |
| エントリー一覧 | gh-wn-entry | コンテナ | 更新情報の繰り返し表示 |
| 公開日 | - | テキスト | "DD MMMM YYYY"形式 |
| タイトル | - | テキスト | entry.title |
| フィーチャー画像 | - | 画像 | entry.feature_image（存在する場合） |
| 抜粋/本文 | gh-wn-content | HTML | custom_excerpt または html |
| 全文リンク | - | リンク | "Read the full post" |
| 全更新表示リンク | - | リンク | ghost.org/changelogへ |

### 表示項目詳細

#### エントリー一覧

- **データソース**: ghost.org/changelog.json
- **表示順**: 公開日降順（最新が上）
- **各エントリーの構成**:
  - 公開日（moment-formatで"DD MMMM YYYY"形式）
  - タイトル（h1）
  - フィーチャー画像（存在する場合）
  - 抜粋または本文HTML
  - 全文リンク（抜粋表示の場合）

## イベント仕様

### 1-ページ読み込み時

**トリガー**: `/ghost/whatsnew`へのルート遷移

**処理フロー**:
1. `AuthenticatedRoute.beforeModel`で認証チェック
2. テンプレートレンダリング
3. `did-insert`モディファイアで`updateLastSeen`タスク実行
4. ユーザーのaccessibility設定に最終閲覧日時を保存

### 2-最終閲覧日時の更新（updateLastSeen）

**トリガー**: 画面表示時（did-insert）

**処理フロー**:
1. 最新エントリーの公開日を取得
2. `_whatsNewSettings.lastSeenDate`を更新
3. ユーザーのaccessibility JSONに保存
4. `user.save()`でサーバーに永続化

### 3-エントリーリンククリック

**トリガー**: エントリータイトルまたは画像クリック

**処理フロー**:
1. 外部リンク（entry.url）を新しいタブで開く
2. `rel="noopener noreferrer"`でセキュリティ確保

### 4-通知登録ボタンクリック

**トリガー**: 「Turn on notifications」ボタンクリック

**処理フロー**:
1. ghost.org/changelog/#/portal/signupを新しいタブで開く

### 5-全更新表示リンククリック

**トリガー**: 「View all updates」リンククリック

**処理フロー**:
1. ghost.org/changelogを新しいタブで開く

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | users | UPDATE | accessibility.whatsNew.lastSeenDateを更新 |

### テーブル別更新項目詳細

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | accessibility | JSON（whatsNew.lastSeenDate） | 最新エントリーの公開日をISO形式で保存 |

## メッセージ仕様

本画面は専用のメッセージ表示を持たない。外部から取得したChangelogコンテンツがそのまま表示される。

## 例外処理

| 例外ケース | 対応処理 | 表示内容 |
|-----------|----------|----------|
| 未認証状態でアクセス | サインイン画面へリダイレクト | - |
| Changelog取得失敗 | コンソールにエラー出力 | 空のリスト表示 |
| ユーザー保存失敗 | エラー処理なし（サイレント） | - |

## 備考

- What's New画面はghost.org/changelog.jsonから更新情報を取得
- Changelogは一度取得したら再リクエストしない（`_changelog_response`でキャッシュ）
- 新着判定：最新エントリーの公開日 > lastSeenDate
- フィーチャード判定：`entry.featured === 'true'`（文字列）
- 新規ユーザーは現在日時をデフォルトのlastSeenDateとして設定
- accessibility JSON内の構造：`{ whatsNew: { lastSeenDate: "ISO日時文字列" } }`
- 外部リンクはすべて`target="_blank" rel="noopener noreferrer"`で開く

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: ルートとコントローラーを理解する

What's Newルートの実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | whatsnew.js | `ghost/admin/app/routes/whatsnew.js` | 認証チェックとページタイトル設定 |
| 1-2 | whatsnew.js | `ghost/admin/app/controllers/whatsnew.js` | whatsNewサービスの注入 |

**主要処理フロー**:
- **routes/whatsnew.js 3行目**: AuthenticatedRouteを継承
- **routes/whatsnew.js 4-7行目**: titleTokenで「What's new?」を設定
- **controllers/whatsnew.js 5行目**: whatsNewサービスを注入

#### Step 2: テンプレートの構造を理解する

画面のレイアウトと表示内容を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | whatsnew.hbs | `ghost/admin/app/templates/whatsnew.hbs` | ヘッダー、エントリー一覧、リンク構成 |

**主要処理フロー**:
- **3行目**: ギフトアイコン付きヘッダー
- **4行目**: 通知登録ボタン（外部リンク）
- **8行目**: `did-insert`でupdateLastSeen実行
- **9-27行目**: `#each`でエントリー一覧を表示
- **10行目**: `scroll-to`でエントリースラグ一致時にスクロール
- **12行目**: 公開日をmoment-formatでフォーマット
- **14-16行目**: フィーチャー画像の条件付き表示
- **19-24行目**: 抜粋または本文HTMLの表示

#### Step 3: whatsNewサービスを理解する

データ取得と状態管理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | whats-new.js | `ghost/admin/app/services/whats-new.js` | fetchLatest、updateLastSeen、hasNew |

**主要処理フロー**:
- **12行目**: changelogUrl定義（https://ghost.org/blog/）
- **22-29行目**: init()でデフォルトのlastSeenDate設定
- **31-41行目**: hasNewでの新着判定（computed property）
- **65-101行目**: fetchLatestタスクでChangelog取得
- **89行目**: https://ghost.org/changelog.jsonからfetch
- **103-122行目**: updateLastSeenタスクで最終閲覧日時更新
- **117-121行目**: ユーザーのaccessibilityに保存

### プログラム呼び出し階層図

```
WhatsnewRoute
    |
    +-- beforeModel (AuthenticatedRoute)
    |       |
    |       +-- session.requireAuthentication
    |              |
    |              +-- [未認証] --> signin画面へリダイレクト
    |
    +-- buildRouteInfoMetadata
            |
            +-- titleToken: "What's new?"

WhatsNewController
    |
    +-- @service whatsNew

Template (whatsnew.hbs)
    |
    +-- did-insert
    |       |
    |       +-- whatsNew.updateLastSeen.perform()
    |              |
    |              +-- entries[0].published_at取得
    |              |
    |              +-- _whatsNewSettings.lastSeenDate更新
    |              |
    |              +-- user.accessibility更新
    |              |
    |              +-- user.save()
    |                     |
    |                     +-- PUT /users/{id}
    |
    +-- #each whatsNew.entries
            |
            +-- entry.url, entry.title, entry.published_at
            |
            +-- entry.feature_image (if exists)
            |
            +-- entry.custom_excerpt or entry.html
```

### データフロー図

```
[外部データ]                     [処理]                           [出力]

ghost.org/changelog.json ────▶ whatsNew.fetchLatest ─────────▶ entries配列
                                    |
                                    v
                            [_changelog_response]
                              (キャッシュ保存)


[画面表示]                      [処理]                           [出力]

/ghost/whatsnew ────────────▶ whatsnew.hbs描画 ──────────────▶ UI表示
        |                           |
        |                           v
        |                   did-insert実行
        |                           |
        |                           v
        |                   updateLastSeen.perform()
        |                           |
        v                           v
[entryパラメータ]           users.accessibility更新
        |                           |
        v                           v
   scroll-to実行              PUT /users/{id}


[状態管理]

entries[0].published_at ────────────────▶ lastSeenDate比較
        |                                        |
        v                                        v
_whatsNewSettings.lastSeenDate ◀─────────── hasNew判定
        |                                   (true/false)
        v
users.accessibility.whatsNew
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| whatsnew.js | `ghost/admin/app/routes/whatsnew.js` | ルート | What's New画面ルーティング |
| whatsnew.js | `ghost/admin/app/controllers/whatsnew.js` | コントローラー | whatsNewサービス注入 |
| whatsnew.hbs | `ghost/admin/app/templates/whatsnew.hbs` | テンプレート | 画面レイアウト |
| whats-new.js | `ghost/admin/app/services/whats-new.js` | サービス | Changelog取得・状態管理 |
| authenticated.js | `ghost/admin/app/routes/authenticated.js` | ルート（親） | 認証済みルートの基底クラス |
