Files
yingfungsolar/docs/superpowers/plans/2026-09-13-admin-dashboard.md
T

11 KiB
Raw Blame History

後台 Dashboard 化 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal:/admin 由綠色純 CSS 表單頁改造成「墨黑 sidebar + 米白內容」嘅真 dashboard/admin 做總覽、文章列表搬去 /admin/posts,所有 admin 頁統一視覺;維持純 Astro + CSS、無 React。

Architecture: AdminLayout.astro 做單一 shellsidebar + CSS variables),保留原有 R2 上傳 script。新增 getDashboardData(db)src/data/content.ts 提供統計,唔改 schema。index.astro 改寫成 dashboard,新增 posts.astro 承接原文章列表。

Tech Stack: Astro 7 SSR、Drizzle/D1、純 CSSCSS variables)。驗證=npm run build(專案冇 testlinttypecheck)。

Spec: docs/superpowers/specs/2026-09-13-admin-dashboard-design.md


File Structure

  • Modify src/layouts/AdminLayout.astro — shell / tokens / nav(保留 upload script
  • Modify src/data/content.ts — 加 getDashboardData
  • Modify src/pages/admin/index.astro — dashboard
  • Create src/pages/admin/posts.astro — 文章列表(由原 index 搬)
  • Modify src/pages/admin/post/[id].astro — redirect+取消 link
  • Modify src/pages/admin/{cases,settings,ai}.astrocontent/[kind].astro — 只改外觀
  • Modify src/pages/admin/login.astro — 重新上色
  • Modify src/AGENTS.md — Admin 段落

Task 1: AdminLayout tokens + sidebar shell

Files: Modify src/layouts/AdminLayout.astro

  • Step 1: 換 :root 同佈局 CSS — 將現有 :rootline 138-144)+ .inner.bar*149-158)換成:
:root {
  --ink:#1A1917; --ink-2:#24221F; --ink-3:#2E2B27;
  --bg:#FAF8F4; --surface:#fff;
  --fg:#1A1917; --fg-muted:#6E6862;
  --border:#E7E2D9;
  --brand:#D97706; --brand-strong:#B45309; --brand-soft:#FEF3C7;
  --ok:#0F6E56; --ok-soft:#E1F5EE;
  --warn:#854F0B; --warn-soft:#FAEEDA;
  --danger:#A32D2D; --danger-soft:#FCEBEB;
  --radius-sm:4px; --radius:8px; --sidebar-w:248px;
  font-family:"Noto Sans HK","PingFang HK","Microsoft JhengHei",system-ui,sans-serif;
  color:var(--fg); background:var(--bg); line-height:1.65; font-size:15px;
}
a{color:var(--brand-strong);text-decoration:none} a:hover{text-decoration:underline}
.shell{display:flex;min-height:100vh}
.sidebar{position:fixed;inset:0 auto 0 0;width:var(--sidebar-w);background:var(--ink);color:#EDEAE4;display:flex;flex-direction:column;padding:22px 14px;z-index:20}
.brand{display:flex;flex-direction:column;gap:2px;padding:0 10px 18px;color:#fff}
.brand-name{font-family:'Noto Serif HK',serif;font-weight:900;font-size:17px;letter-spacing:.02em}
.brand-sub{font-size:11px;color:#9A938A;letter-spacing:.28em}
.nav{display:flex;flex-direction:column;gap:2px;flex:1;overflow:auto}
.nav-group{margin:16px 10px 6px;font-size:10.5px;letter-spacing:.22em;color:#7E776E}
.nav a{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:var(--radius-sm);color:#CFC9C1;font-size:13.5px;border-left:3px solid transparent}
.nav a:hover{background:var(--ink-2);color:#fff;text-decoration:none}
.nav a.active{background:var(--ink-2);color:var(--brand);border-left-color:var(--brand);font-weight:700}
.sidebar-foot{display:flex;flex-direction:column;gap:4px;border-top:1px solid #2E2B27;padding-top:14px}
.sidebar-foot a{padding:8px 10px;color:#9A938A;font-size:13px}
.sidebar-foot a:hover{color:#fff;text-decoration:none}
.content{flex:1;margin-left:var(--sidebar-w);min-width:0}
.main{max-width:1080px;padding:36px 40px 100px;margin:0 auto}
.topbar{display:none}
  • Step 2: 加 mobile CSS(同一 <style> 內):
@media (max-width:900px){
  .sidebar{transform:translateX(-100%);transition:transform .2s ease}
  #nav-toggle:checked ~ .sidebar{transform:translateX(0)}
  .content{margin-left:0}
  .topbar{display:flex;align-items:center;gap:12px;position:sticky;top:0;z-index:15;background:var(--surface);border-bottom:1px solid var(--border);padding:12px 18px}
  .topbar .brand{flex-direction:row;align-items:baseline;gap:8px;padding:0;color:var(--ink)}
  .topbar .brand-sub{color:var(--fg-muted)}
  .hamburger{cursor:pointer;font-size:20px;line-height:1;color:var(--ink);border:1px solid var(--border);border-radius:var(--radius-sm);padding:4px 10px}
  .main{padding:24px 18px 80px}
}
  • Step 3: 換 body markupline 29-51)成:
<body>
  <input type="checkbox" id="nav-toggle" class="nav-toggle" hidden />
  <div class="shell">
    <aside class="sidebar">
      <a class="brand" href="/admin">
        <span class="brand-name">盈豐太陽能</span>
        <span class="brand-sub">後台</span>
      </a>
      <nav class="nav">
        <p class="nav-group">總覽</p>
        <a href="/admin" class={isActive("/admin") ? "active" : ""}>總覽</a>
        <p class="nav-group">內容管理</p>
        <a href="/admin/posts" class={isActive("/admin/posts") ? "active" : ""}>文章</a>
        <a href="/admin/content/service" class={isActive("/admin/content/service") ? "active" : ""}>服務</a>
        <a href="/admin/content/feature" class={isActive("/admin/content/feature") ? "active" : ""}>特色</a>
        <a href="/admin/content/step" class={isActive("/admin/content/step") ? "active" : ""}>流程</a>
        <a href="/admin/content/faq" class={isActive("/admin/content/faq") ? "active" : ""}>常見問題</a>
        <a href="/admin/cases" class={isActive("/admin/cases") ? "active" : ""}>完成案例</a>
        <p class="nav-group">系統</p>
        <a href="/admin/ai" class={isActive("/admin/ai") ? "active" : ""}>AI 生成</a>
        <a href="/admin/settings" class={isActive("/admin/settings") ? "active" : ""}>網站設定</a>
      </nav>
      <div class="sidebar-foot">
        <a href="/" target="_blank">睇網站 ↗</a>
        <a href="/admin/logout">登出</a>
      </div>
    </aside>
    <div class="content">
      <header class="topbar">
        <label for="nav-toggle" class="hamburger" aria-label="開合導航">☰</label>
        <a class="brand" href="/admin">
          <span class="brand-name">盈豐太陽能</span>
          <span class="brand-sub">後台</span>
        </a>
      </header>
      <main class="main"><slot /></main>
    </div>
  </div>
  <script>/* 原 upload script 不變 */</script>
</body>
  • Step 4: 換 formtablecard CSS(現有 164-206)— 保留 class 名(.card .badge .actions .grid2 .image-* .muted .err .field-error .row),色值改用 tokens.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px 22px;margin-bottom:18px}th/tdvar(--border)input/textarea/select 用 var(--border)focus outline:2px solid var(--brand-soft);border-color:var(--brand)button,.btnbackground:var(--ink);border-color:var(--ink);border-radius:var(--radius-sm).btn.secondary 白底、button.danger--danger--danger-soft.badge.published{background:var(--ok-soft);color:var(--ok)}.badge.draft{background:var(--warn-soft);color:var(--warn)}

  • Step 5: 驗證npm run buildExpected: success0 errors)。

  • Step 6: 目測npm run dev,喺 ≥900px 同 <900px 開 /admin,確認 sidebar、active 態、漢堡開合、各頁表單/表格可讀。

  • Step 7: Commitgit add src/layouts/AdminLayout.astro && git commit -m "feat(admin): sidebar shell and design tokens"


Task 2: getDashboardData(db)

Files: Modify src/data/content.ts

  • Step 1: 加 importimport { and, asc, desc, eq, isNull, ne, sql } from "drizzle-orm";,並喺 schema import 加入 CONTENT_KINDS
  • Step 2: 加 type functiongetKeywords 之後):見 spec §4,實作 DashboardData + getDashboardData(用 groupBy + sql\count(*)`pick helper 取 status 計數,recentPosts limit 5postsWithoutCoverisNull(posts.coverImage)`)。
  • Step 3: 驗證npm run buildExpected: success。
  • Step 4: Commitgit add src/data/content.ts && git commit -m "feat(admin): add getDashboardData query"

Task 3: Dashboard /admin 文章頁 /admin/posts redirects

Files: Create src/pages/admin/posts.astroModify src/pages/admin/index.astrosrc/pages/admin/post/[id].astro

  • Step 1: 建立 posts.astro — 將原 index.astro 查詢+文章表搬去,<AdminLayout title="文章"><h1>文章</h1>,heading 加「+ 寫新文」;保留 export const prerender = false
  • Step 2: 改寫 index.astro 成 dashboardfrontmatter 用 getDashboardData + getSettings,砌 todosmarkup 見 spec §3)。
  • Step 3: 加 dashboard CSS.stats.stat.dash.todo.status-row.status-dotRWD breakpoints 1100900560)。
  • Step 4: 改 redirectpost/[id].astro line 293996 "/admin""/admin/posts"line 174 取消 link href="/admin""/admin/posts"
  • Step 5: 驗證npm run buildExpected: success。
  • Step 6: 目測 — dev:登入後 /admin 見總覽;/admin/posts 新增/儲存/刪文後返 /admin/posts;統計同待辦數字正確。
  • Step 7: Commitgit add src/pages/admin && git commit -m "feat(admin): dashboard overview and move posts list to /admin/posts"

Task 4: 其餘 admin 頁統一視覺

Files: Modify src/pages/admin/cases.astrocontent/[kind].astrosettings.astroai.astro

  • Step 1: 移除 inline 硬編色,改用 tokens/既有 class;成功提示改 .notice
  • Step 2: 統一 section 標題/spacing
  • Step 3: 驗證npm run buildExpected: success。
  • Step 4: 目測 — dev 逐頁睇 casescontent 四 kindsettings/ai,確認色系一致、表單可用、圖片上傳正常。
  • Step 5: Commitgit add src/pages/admin && git commit -m "style(admin): align pages with dashboard tokens"

Task 5: login.astro 重新上色

Files: Modify src/pages/admin/login.astro

  • Step 1:<style is:global> 改成同 tokens 一致(米白底、白卡髮絲邊、琥珀 focus、墨黑掣、serif 標題、.err 用 danger 色)。
  • Step 2: 驗證npm run buildExpected: success。
  • Step 3: 目測 — dev 登出睇 /admin/login,登入流程正常。
  • Step 4: Commitgit add src/pages/admin/login.astro && git commit -m "style(admin): restyle login page"

Task 6: 更新 DOX 文件

Files: Modify src/AGENTS.md

  • Step 1: 更新 src/AGENTS.md Admin 段落(路由、dashboard、sidebar、色系、getDashboardData)。
  • Step 2: 驗證npm run buildExpected: success。
  • Step 3: Commitgit add src/AGENTS.md && git commit -m "docs: document admin dashboard redesign"