## 邀請好友，賺 e幣

分享你的邀請碼，你得 500 e幣，朋友得 100 e幣。

### ① 我的邀請碼

ABCD-23
https://eclawbot.com/invite/ABCD23

### ② 邀請儀表板

3
累計邀請
1,500
獲得 e幣
12
今日使用
33%
轉換率
62 / 100

#### 過去 30 天使用量

### ③ 獎勵說明

你獲得+500 e幣
好友獲得+100 e幣
朋友首次儲值獎勵（你）+500 e幣

### ④ 階級與里程碑

目前階級
青銅邀請者
🥉
青銅（1 位好友）
✓ 解鎖
白銀（5 位好友）
+1000 e幣
黃金（10 位好友）
+3000 e幣

### ⑤ 兌換邀請碼

#### 整體排版

`max-width: 600px` 單欄置中；每個 section 都是 `var(--card)` 底 + 1px border + 圓角 `var(--radius)=12px`，垂直堆疊，`margin-bottom: 16px`。手機同樣版型（不變欄數）。

#### Hero banner (①上方)

深藍 `#1e3a8a` → 紫 `#6d28d9` 135° 漸層，白字，32px padding 置中。全站唯一走漸層 banner 的頁面。

#### ① 邀請碼框

`.inv-code`：24px 特大、800 weight、monospace、`letter-spacing: 4px`、2px 虛線邊框、紫色字。右邊 `.inv-copy-btn` 是實色紫鈕，兩者高度用 padding 對齊。

#### ② 儀表板 KPI

`.inv-kpi-grid`：`repeat(auto-fit, minmax(130px, 1fr))`，螢幕寬 4 格一列、手機自動換 2 格。數字用 `tabular-nums` 等寬對齊。下方 usage bar 是 綠→紫橫條，timeline 是 紫→藍直條。

#### ③ 獎勵 3 行

左標題 secondary 灰 / 右數字 `#10b981` 綠色強調，中間用 1px border-bottom 分隔。

#### ④ Tier 階級

目前階級用 奶油黃漸層 + 棕字 `#78350f` 當 award 感。已解鎖 row 用 amber `#f59e0b` border + 淺黃背景，未解鎖維持 card 灰底。

#### ⑤ 兌換

input + button 平排；input 有 `text-transform: uppercase` + `letter-spacing: 2px`，打字會自動變大寫寬字。

#### 🐞 發現的 bug — 簡繁混雜

頁面 `i18n.lang = "zh-CN"`，但 zh-CN 字典**大量殘留繁體**，甚至單字串內簡繁混寫。看右下詳表。

## 🔧 這次 PR 動到的 UI — before / after

為了修 invite 0/8 轉換率，後端加了 `/invite/:code` redirect（原本 404）+ 前端在 invite.html DOMContentLoaded 加 5 行 JS 把 `?redeem=CODE` 自動填入 input。UI 視覺本身完全沒改，只有行為改了。

BEFORE

#### 1. 分享連結行為

https://eclawbot.com/invite/ABCD23
💀
Cannot GET /invite/ABCD23

Express 預設 404 — 零路由，所有分享出去的碼都是死的

#### 2. 兌換框（直接進 portal 的話）

### 有邀請碼？

↑ 空白，使用者要手動從訊息裡抄

AFTER

#### 1. 分享連結行為

https://eclawbot.com/invite/ABCD23
1. `302 Redirect` (+ 寫 click log)
↓
2. `/portal/invite.html?redeem=ABCD23`
↓
✓ 落地頁正常渲染 + 兌換框已預填

#### 2. 兌換框（從分享連結進來）

### 有邀請碼？

↑ 自動填入，使用者一按兌換就完成

展開：動到的 code（完整 diff）

    + // index.js (新增路由)
    + app.get('/invite/:code', async (req, res) => {
    +     const raw = String(req.params.code || '').toUpperCase();
    +     const code = /^[A-Z0-9]{4,12}$/.test(raw) ? raw : null;
    +     if (!code) return res.redirect(302, '/portal/invite.html');
    +     // best-effort click log (PII-hashed IP, 不擋 redirect)
    +     try { await db.logInviteClick({ code, ipHash, userAgent, referer }); } catch {}
    +     return res.redirect(302, `/portal/invite.html?redeem=${encodeURIComponent(code)}`);
    + });

      // public/portal/invite.html DOMContentLoaded 裡面加 5 行：
      window.addEventListener('DOMContentLoaded', async () => {
          const _params = new URLSearchParams(window.location.search);
          const _isEmbed = _params.get('embed') === '1' || window.self !== window.top;
          if (_isEmbed) document.body.classList.add('embed-mode');
    +     const _redeemParam = (_params.get('redeem') || '').toUpperCase()
    +         .replace(/[^A-Z0-9]/g, '').slice(0, 12);
    +     if (_redeemParam) {
    +         const _input = document.getElementById('redeemInput');
    +         if (_input) _input.value = _redeemParam;
    +     }
          renderNav('settings');
          ...
      });

      // invite.html 的 HTML (input 本身完全沒動)：
      <div class="inv-redeem">
          <input id="redeemInput" placeholder="ABCDEF" maxlength="8">
          <button onclick="redeemCode()">兌換</button>
      </div>

## 🐞 zh-CN 字典 bug — 簡繁混雜範例（實測頁面看到的）

下面是今天從線上頁面抓下來的真實渲染字串，這不是邀請頁獨有，而是 `i18n.zh-CN` 全站字典的問題。

#### 繁體直接當簡體用（應該翻成簡體）

key: inv_dashboard 儀表板
key: inv_invite_friends 邀請好友
key: inv_copy 複製
key: inv_qr_gen_cta 產生分享圖
key: invite_stats_kpi_conversion 轉換率
key: inv_you_get 你獲得
key: topup_btn 儲值
key: footer_privacy 隱私權政策

#### 🚨 單字串內簡繁混寫（最糟）

key: inv_earn_coin 赚取 e幣
（赚=簡，幣=繁）應：賺取 e幣 (繁) / 赚取 e币 (簡)
key: topbar_info 资訊
（资=簡，訊=繁）應：資訊 (繁) / 资讯 (簡)
key: invite_stats_timeline_title 过去 30 天
（过=簡，其他繁）應：過去 30 天 (繁)
key: inv_rewards_title 獎勵说明
（獎勵=繁，说=簡）應：獎勵說明 (繁) / 奖励说明 (簡)
key: inv_tier_title 阶級与里程碑
（阶/与=簡，級=繁）應：階級與里程碑 (繁)
key: faq_title 常見問题
（見=繁，题=簡）應：常見問題 (繁) / 常见问题 (簡)

推測成因：zh-CN 字典是從 zh-TW 字典複製過來後只翻了一部分 key（或機翻工具吃到半個字串就漏字）。影響範圍：全站，不是只有邀請頁。
建議：另開一張 P2 卡做 zh-CN 字典全面清洗（or 直接用 OpenCC zh-TW → zh-CN 自動轉一次當 baseline）。