# UTAGE → Monaka 変換仕様メモ

**このファイルはUTAGE取り込み関連の開発前に必ず読むこと。**

---

## 🚨 絶対ルール（最優先・例外なし）

### コンバート処理は「全UTAGEページ対応」で作ること

**特定のURLや特定のページだけに効く修正は一切禁止。**

UTAGEからの取り込みロジック（`UtagePageImportService.php`）およびプレビューCSS（`preview.blade.php`）への修正は、**あらゆるUTAGEページに対して汎用的に動作する**形で実装すること。

#### NG（やってはいけない）
```php
// ❌ 特定ページIDや特定URLへのハードコード
if ($pageUrl === 'https://utage-system.com/p/xxxxx') {
    // このページだけの処理
}

// ❌ 特定の要素内容に依存した条件分岐
if ($n['text'] === '佐藤茂夫') {
    // この名前のときだけ左寄せ
}
```

#### OK（正しいアプローチ）
```php
// ✅ 要素の属性・構造に基づいた汎用ロジック
if (($n['float'] ?? '') === 'left') {
    // float=left のすべての要素を左寄せ
}
```

#### CSS修正も同様
```css
/* ✅ 全ページ・全要素に効くCSS */
.el-text p { line-height: 1.6; word-break: break-all; }
.el-subhead { font-family: 'Noto Sans JP', sans-serif; }

/* ❌ 特定ページのIDやクラスに依存したCSS */
#page-xxxxx .el-text { ... }
```

**「このページで見た問題」を修正するときも、必ずその問題が発生する根本原因をUTAGEの仕様から理解し、同じ条件を持つ全ページで正しく動くよう実装すること。**

---

## ⚠️ 作業前チェック

UTAGE取り込みに関する修正・追加を行う前に、このファイルの全内容を確認すること。

---

## 1. 基本構造

| UTAGE | Monaka | ファイル |
|-------|--------|---------|
| `section` | `section` / `columns2` / `columns3` / `columns4` | `UtagePageImportService.php` |
| `row`（section内） | Monakaセクション1件に対応 | 同上 |
| `col`（row内） | columnsセクションの各列要素 | 同上 |

- UTAGEの `section > row > col > element` 構造をMonakaの `section > elements[]` にフラット化する
- rowが複数 → Monakaセクションが複数生成される
- colが複数 → `columns2/3/4` セクションになる

---

## 2. セクション間スペース（重要・過去に何度もバグになった）

### 問題
UTAGEのJSONには `padding_top` / `padding_bottom` / `margin_top` / `margin_bottom` が**ほぼ存在しない（N/A）**。
スペースはUTAGEのVue.jsコンポーネントのCSSデフォルトから来ており、JSONに書かれていない。

### 対処
取り込み時に `pb=0`（値なし）の場合は `margin_bottom: '20px'` をデフォルト設定する。

### 実装箇所（`UtagePageImportService.php`）
3箇所すべてに同じルールを適用すること：

1. **`emitNormalSection` クロージャ（単一カラムセクション）**
```php
'margin_bottom' => ($bgCss === '' && $bgImg === '') && $pb > 0 ? $pb.'px' : ($pb > 0 ? '0px' : '20px'),
```

2. **`colSection`（複数カラムセクション）**
```php
'margin_bottom' => ($bgCss === '' && $bgImg === '') && $rowPb > 0 ? $rowPb.'px' : ($rowPb > 0 ? '0px' : '20px'),
```

3. **`buildPutageSectionsFromUtageRow`（ベース）**
```php
'margin_bottom' => $bgColor === '' && $pb > 0 ? $pb.'px' : ($pb > 0 ? '0px' : '20px'),
```

### ルール
- `pb > 0` かつ背景なし → `pb`をpxに変換してmargin_bottomに使う
- `pb > 0` かつ背景あり → `0px`（背景内のpadding_bottomで処理）
- `pb = 0`（値なし） → `20px`デフォルト（背景あり・なし問わず）

---

## 3. セクション背景と余白の関係

| 条件 | margin_top/bottom | padding_top/bottom |
|------|-------------------|--------------------|
| 背景色/背景画像あり | 外側透明余白（section間のスペース） | 内側着色余白（背景が塗られる範囲） |
| 背景なし | 外側透明余白として使用 | margin_topに変換して使用 |

プレビュー（`_preview_sections.blade.php`）のロジック：
- `margin_top/bottom` が0pxの場合のみ `padding_top/bottom` を参照（fallback）
- 背景ありセクション: padding → `padding-top/bottom`（着色）
- 背景なしセクション: padding → `margin-top/bottom`（透明）

---

## 4. 画像幅の変換

UTAGEの画像幅（例: `width:410`）はピクセル値。
**絶対に `410%` に変換しないこと。**

変換ルール：
- セクション幅・カラム幅をコンテナ幅として `width%` に換算する
- 実装: `repairImportedImageWidthsInPageContent` で既存ページも修復可能

---

## 5. `inner` 行の扱い（design=inner）

- `design=inner` のrowはMonakaで独立したセクション/カラムとして出力
- 行幅・角丸・行背景をMonakaセクション/カラムとして反映
- **カードまとめ込みは取り込み時は行わない**（編集デグレ対策）

---

## 6. フォーム系要素

| UTAGEタイプ | Monakaの扱い |
|------------|------------|
| `form` | `skipped_types`でスキップ → `import_utage_form_placeholder` |
| `webinar-form` | `mapUtageWebinarForm()` → `import_utage_form_placeholder` |

- フォーム系はすべて `import_utage_form_placeholder` として出力
- エディタ/プレビュー/公開で「登録フォームはMonakaのページエディタから設置してください」を表示
- **注意**: `webinar-form` は `text` フィールドにボタンラベルを持つが、これをそのままHTMLに出力しないこと（生テキストとして表示されてしまう）

---

## 7. `image-text` 要素

UTAGEの `image-text` は「顔写真 + プロフィールテキスト」の横並びブロック。

**UTAGE の構造：**
- `img_src`: 画像URL
- `text`: HTMLテキスト（プロフィール本文）
- `float`: "left"（画像を左配置、テキストが右に回り込む）
- `align`: "center"

**正しい変換（`mapUtageImageText`）：**
```php
// コンテナ幅の約30%を画像幅とする（最小100px、最大250px）
$imgWidth = max(100, min(250, (int) round($containerPx * 0.30)));

// float:left で左配置（flexboxではなくfloatを使う）
$content = '<div style="overflow:hidden;">'
    . '<img src="..." style="float:left;width:' . $imgWidth . 'px;margin-right:16px;margin-bottom:8px;object-fit:cover;" />'
    . '<div>' . $text . '</div>'
    . '</div>';
```

**NG パターン（やってはいけない）：**
- `border-radius:50%` → 円形になり講師写真がアイコン化してしまう
- `width:80px;height:80px` → 小さすぎる
- `display:flex` → UTAGEのfloatレイアウトと異なる

---

## 8. よくあるミス

1. **「取り込み済みデータを直接DB更新しても意味がない」** → 必ず取り込みサービスを修正し再取り込みして確認すること
2. **「UTAGEのJSONに値がないからMonakaでも0でいい」は間違い** → UTAGEはVue.jsのCSSデフォルトでスタイルを持つ。値がなくてもデフォルト値を補完する
3. **取り込みは汎用対応** → 特定ページへのハードコード対応は禁止。すべてのUTAGEページに適用されるロジックで修正する

---

## 8. 検証手順

修正後は必ず以下を行う：

1. `UtagePageImportServiceTest` を実行（既存テスト全件パス確認）
2. テストページ `https://utage-system.com/p/vHtj0xiHJMnh` を再取り込み
3. `https://tools.monaka-app.com/p/RpFzGyFIInQXng` でビジュアル確認
4. UTAGEオリジナルと並べて比較

---

## 9. 主要ファイル

| ファイル | 役割 |
|---------|------|
| `app/Services/UtagePageImportService.php` | 取り込みロジック本体 |
| `app/Services/FunnelPageUrlImportService.php` | URL取り込みの入口 |
| `resources/views/funnel/page/_preview_sections.blade.php` | セクション描画（公開・プレビュー） |
| `resources/views/funnel/page/editor.blade.php` | エディタキャンバス描画 |
| `tests/Unit/UtagePageImportServiceTest.php` | テスト |
