docs: document admin dashboard redesign

This commit is contained in:
2026-09-13 21:46:58 +08:00
parent 9eed1d32d3
commit cc142bfd06
2 changed files with 6 additions and 3 deletions
+5 -3
View File
@@ -9,7 +9,7 @@
- 擁有 `src/` 全層,亦直接擁有以下冇獨立 child doc 的部分: - 擁有 `src/` 全層,亦直接擁有以下冇獨立 child doc 的部分:
- `theme/system.ts` — Chakra systemtokens / semanticTokens / fonts / globalCss),前台顏色同字體的唯一來源。現行設計:editorial 極簡風(暖米白 `#FAF8F4` 底、琥珀金 `brand` accent、墨黑 `ink`、Noto Serif HK 標題),詳見 `docs/superpowers/specs/2026-09-11-editorial-redesign-design.md` - `theme/system.ts` — Chakra systemtokens / semanticTokens / fonts / globalCss),前台顏色同字體的唯一來源。現行設計:editorial 極簡風(暖米白 `#FAF8F4` 底、琥珀金 `brand` accent、墨黑 `ink`、Noto Serif HK 標題),詳見 `docs/superpowers/specs/2026-09-11-editorial-redesign-design.md`
- `layouts/Base.astro` — 前台 SEO headcanonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `<noscript>` fallback)+ 同意後先載入嘅 analytics(`gaId` / `metaPixelId` props,見下面 Analytics & consent)。 - `layouts/Base.astro` — 前台 SEO headcanonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `<noscript>` fallback)+ 同意後先載入嘅 analytics(`gaId` / `metaPixelId` props,見下面 Analytics & consent)。
- `layouts/AdminLayout.astro` — 後台外框,純 CSS、與前台一致的色系 - `layouts/AdminLayout.astro` — 後台外框:墨黑左側 sidebar`/admin` 總覽、內容管理、系統分組)+米白內容區,純 CSS、色值集中喺 CSS variables`--ink` / `--brand` / `--bg` 等),手機用純 CSS checkbox 漢堡開合;同時保留 R2 上傳 script(`ImageField` 依賴 `.image-url` / `.image-preview` / `.image-status`
- `config.ts` — build-time 靜態頁用的 `SITE_NAME` / `SITE_TAGLINE`(可管理內容一律放 D1,唔放呢度)。 - `config.ts` — build-time 靜態頁用的 `SITE_NAME` / `SITE_TAGLINE`(可管理內容一律放 D1,唔放呢度)。
- `middleware.ts` — 保護 `/admin`:設 `locals.isAdmin`,無 `ADMIN_PASSWORD` 回 503,未登入導向 `/admin/login` - `middleware.ts` — 保護 `/admin`:設 `locals.isAdmin`,無 `ADMIN_PASSWORD` 回 503,未登入導向 `/admin/login`
- `env.d.ts``App.Locals.isAdmin` 型別宣告。 - `env.d.ts``App.Locals.isAdmin` 型別宣告。
@@ -49,14 +49,16 @@
### Admin`pages/admin/` ### Admin`pages/admin/`
- 所有 `/admin` 路由**必須** `export const prerender = false` - 所有 `/admin` 路由**必須** `export const prerender = false`
- **視覺**:全部用 `AdminLayout.astro` 的墨黑 sidebar shell,色值一律用其 CSS variables(唔好硬編色)。`/admin` 係總覽 dashboard。
- **認證**由 `middleware.ts` 統一處理;登入喺 `admin/login.astro``checkPassword` + `createSession`),登出喺 `logout.ts` - **認證**由 `middleware.ts` 統一處理;登入喺 `admin/login.astro``checkPassword` + `createSession`),登出喺 `logout.ts`
- **UI**:全部用 `AdminLayout.astro`,純 Astro SSR 表單(`POST` + `formData`),**唔引入** React / Chakra。 - **UI**:全部用 `AdminLayout.astro`,純 Astro SSR 表單(`POST` + `formData`),**唔引入** React / Chakra。
- **流程**:每個 POST 處理完 `Astro.redirect` 返對應列表頁;`/admin/ai` 用 POST/Redirect/Get + `?ok=<key>` + `okMessages` 顯示成功提示(避免重複提交)。 - **流程**:每個 POST 處理完 `Astro.redirect` 返對應列表頁;`/admin/ai` 用 POST/Redirect/Get + `?ok=<key>` + `okMessages` 顯示成功提示(避免重複提交)。成功提示用 `.notice`(唔好再用 `.badge.published`)。
- **總覽**`index.astro``/admin`)係 dashboard,資料由 `data/content.ts``getDashboardData(db)` 提供(統計卡/待辦提示/最近更新文章/系統狀態);AI secrets 由頁面層 `getEnv()` 讀。
- **表單驗證**:所有 admin 表單經 `parseForm(form, schema)``src/schemas/`),失敗時以 `errors` 逐欄顯示,唔好手寫逐欄檢查。 - **表單驗證**:所有 admin 表單經 `parseForm(form, schema)``src/schemas/`),失敗時以 `errors` 逐欄顯示,唔好手寫逐欄檢查。
- **通用動作**`add` / `save` / `delete` / `up` / `down`(排序以交換 `sortOrder` 實作)。 - **通用動作**`add` / `save` / `delete` / `up` / `down`(排序以交換 `sortOrder` 實作)。
- **通用內容編輯器**`content/[kind].astro``kind ∈ service | feature | step | faq`(見 `db/schema.ts``CONTENT_KINDS`),欄位標籤由檔案內 `META` 定義。 - **通用內容編輯器**`content/[kind].astro``kind ∈ service | feature | step | faq`(見 `db/schema.ts``CONTENT_KINDS`),欄位標籤由檔案內 `META` 定義。
- **網站設定**`settings.astro``data/settings-fields.ts``SETTINGS_GROUPS` / `ALL_SETTING_KEYS` 產生表單,逐 key upsert。 - **網站設定**`settings.astro``data/settings-fields.ts``SETTINGS_GROUPS` / `ALL_SETTING_KEYS` 產生表單,逐 key upsert。
- **文章**`index.astro` 列表、`post/[id].astro` 新增/編輯`id === "new"` 為新增);slug 自動 `slugify` 並用 `uniqueSlug` 去重。 - **文章**列表喺 `posts.astro``/admin/posts`)、編輯喺 `post/[id].astro``id === "new"` 為新增);slug 自動 `slugify` 並用 `uniqueSlug` 去重。文章存/刪後 redirect 去 `/admin/posts`
- **AI 生成**`ai.astro``/admin/ai`)管 AI 設定 + 關鍵字佇列,同步呼叫 `generateBlogPost`;成功會 redirect 去新草稿 `/admin/post/<id>`。生成按鈕用 inline `onsubmit` 顯示「生成中…」。 - **AI 生成**`ai.astro``/admin/ai`)管 AI 設定 + 關鍵字佇列,同步呼叫 `generateBlogPost`;成功會 redirect 去新草稿 `/admin/post/<id>`。生成按鈕用 inline `onsubmit` 顯示「生成中…」。
- **圖片**:圖片欄位(settings 的 `hero_image` / `og_image``cases.imageUrl``posts.coverImage`)用 `components/admin/ImageField.astro`,可揀檔即時上傳去 R2(binding `MEDIA`),亦可貼 URLclient 端縮到最大寬 1600px 並轉 WebP 先上傳。上傳 endpoint `/admin/upload`(受 middleware 保護,`scope ∈ settings | cases | posts`、JPG/PNG/WebP/GIF、上限 8MB),出圖 endpoint `/media/[...key]`(公開、`immutable` cache)。換圖/刪除時由 `lib/media.ts``deleteMedia` 清舊 R2 檔。 - **圖片**:圖片欄位(settings 的 `hero_image` / `og_image``cases.imageUrl``posts.coverImage`)用 `components/admin/ImageField.astro`,可揀檔即時上傳去 R2(binding `MEDIA`),亦可貼 URLclient 端縮到最大寬 1600px 並轉 WebP 先上傳。上傳 endpoint `/admin/upload`(受 middleware 保護,`scope ∈ settings | cases | posts`、JPG/PNG/WebP/GIF、上限 8MB),出圖 endpoint `/media/[...key]`(公開、`immutable` cache)。換圖/刪除時由 `lib/media.ts``deleteMedia` 清舊 R2 檔。
- 前台可見性靠 `status``published` / `draft`);列表頁顯示全部,前台只顯示 published。 - 前台可見性靠 `status``published` / `draft`);列表頁顯示全部,前台只顯示 published。
+1
View File
@@ -14,6 +14,7 @@ D1 讀取查詢層(`content.ts`)同後台網站設定欄位定義(`setting
- **查詢集中**:所有前台/Blog 的 D1 查詢寫喺 `content.ts`,唔好散落喺頁面或元件。 - **查詢集中**:所有前台/Blog 的 D1 查詢寫喺 `content.ts`,唔好散落喺頁面或元件。
- **只回前台可見**`getItems` / `getCases` / `getPublishedPosts` 只回 `status = "published"`,並按 `sortOrder``publishedAt DESC` 排序;`getPostBySlug` 同時要求 published。 - **只回前台可見**`getItems` / `getCases` / `getPublishedPosts` 只回 `status = "published"`,並按 `sortOrder``publishedAt DESC` 排序;`getPostBySlug` 同時要求 published。
- **首頁聚合**`getHomeData(db)``Promise.all` 一次過攞 settings + services/features/steps/faqs + cases,形狀係 `HomeData` - **首頁聚合**`getHomeData(db)``Promise.all` 一次過攞 settings + services/features/steps/faqs + cases,形狀係 `HomeData`
- **後台總覽聚合**`getDashboardData(db)``DashboardData``posts``cases``content``keywords` 各狀態計數+`recentPosts` 最新 5 篇+`postsWithoutCover`),供 `/admin` dashboard 用;只讀 DBAI secrets 由頁面層 `getEnv()` 提供。
- **關鍵字佇列**`getKeywords(db)` 回全部 `blog_keywords`(按 `createdAt``id` 排序),供 `/admin/ai` 用。 - **關鍵字佇列**`getKeywords(db)` 回全部 `blog_keywords`(按 `createdAt``id` 排序),供 `/admin/ai` 用。
- **Slug 去重**`uniqueSlug(db, base, selfId?)` 撞 slug 就加 `-2``-3`…;`selfId` 用喺更新自己時排除自己。admin 文章同 AI 生成都用。 - **Slug 去重**`uniqueSlug(db, base, selfId?)` 撞 slug 就加 `-2``-3`…;`selfId` 用喺更新自己時排除自己。admin 文章同 AI 生成都用。
- **`Db` 型別**`DrizzleD1Database<typeof schema>` - **`Db` 型別**`DrizzleD1Database<typeof schema>`