Imported from zsfai/xxgcms (
admin-frontend/AGENTS.md). Install upstream withnpx skills add zsfai/xxgcms --skill admin-frontend. Copyright stays with the author.
admin-frontend — Agent Instructions
React admin SPA for xxg-cms. 说明用中文,命令/路径保持英文。
Parent doc: ../AGENTS.md
Stack
| Layer | Technology |
|---|---|
| UI | React 18 + TypeScript |
| Build | Vite 6 |
| Routing | React Router 6 (createBrowserRouter) |
| Styling | Tailwind CSS 3 + shadcn/ui (new-york) |
| State | zustand (minimal) + page-local useState |
| HTTP | axios |
| Forms | react-hook-form + zod (login only) |
| Rich text | @wangeditor-next |
| Toast | sonner |
| Icons | lucide-react |
Entry: src/main.tsx → src/router/index.tsx → src/layouts/MainLayout.tsx
Path alias: @/ → src/
Key Paths
| Purpose | Path |
|---|---|
| All API calls | src/api/service.ts |
| Routes | src/router/index.tsx |
| Sidebar menu | src/components/layout/Sidebar.tsx |
| Page shell | src/components/PageShell.tsx |
| Form label | src/components/FormLabel.tsx |
| Shared types | src/types/index.ts |
| Global store | src/stores/app-store.ts |
| Utilities | src/lib/utils.ts |
| Layout CSS classes | src/index.css |
| shadcn primitives | src/components/ui/* |
| Vite config + proxy | vite.config.ts |
Dev Commands
npm install
npm run dev # http://localhost:8080, proxy /api /media → :8000
npm run build # tsc -b && vite build
npm run preview
npm run lint
Requires admin-backend running on :8000.
Page Organization
src/pages/
├── login/LoginPage.tsx # Standalone, no MainLayout
├── sys/ # Global (no site selection required)
│ ├── SitePage.tsx
│ ├── AiConfigPage.tsx
│ ├── LoginLogPage.tsx
│ ├── ChangelogPage.tsx
│ └── McpAccessPage.tsx
├── article/ # Site-scoped features
│ ├── ArticleListPage.tsx
│ ├── AiTopicPage.tsx
│ ├── CatePage.tsx
│ ├── CarouselPage.tsx
│ ├── FriendLinkPage.tsx
│ └── SiteConfPage.tsx
└── keyword/
└── KeywordListPage.tsx
Naming: file {Feature}Page.tsx, export export function {Feature}Page().
Routes
Defined in src/router/index.tsx:
| Path | Page | Scope |
|---|---|---|
/ |
LoginPage | — |
/sites |
SitePage | Global |
/ai |
AiSettingsLayout | Global(二级:config / mcp) |
/ai/config |
AiConfigPage | Global |
/ai/mcp |
McpAccessPage | Global(MCP Key / 配置示例) |
/articles |
ArticleListPage | Site |
/ai-topics |
AiTopicPage | Site |
/cates |
CatePage | Site |
/keywords |
KeywordListPage | Site |
/carousels |
CarouselPage | Site |
/links |
FriendLinkPage | Site |
/media |
MediaLibraryPage | Site |
/conf |
SiteConfPage | Site |
/system/login-logs |
LoginLogPage | Global |
/system/changelog |
ChangelogPage | Global |
Sidebar sections in Sidebar.tsx: global items vs requiresSite: true (disabled when no domain selected).
API Client Pattern
All services in one file: src/api/service.ts. Do not split unless explicitly requested.
Naming
{verb}{Resource}Service — e.g. getLinkListService, topicSuggestService
Site-Scoped Calls
export const getLinkListService = (data: Record<string, unknown>) =>
axios.post('/api/get_link_list/', xData(data)) as Promise<ApiResponse>
xData() injects domain from sessionStorage.
Auth Interceptor
- Request: sets header
auth-keyfromsessionStorage.token - Response:
code === 403→useAppStore.getState().showLoginForm()
Response Type
src/types/index.ts → ApiResponse<T> with code, message, data/datas, total_count, ret.
Success check: res.code === 0. Some write ops also check res.ret.
File Upload
Do not use axios service. Use src/components/FileUpload.tsx with getAuthHeaders() from service.ts.
Add New Page (5 Steps)
- Create
src/pages/{domain}/{Name}Page.tsxwithexport function {Name}Page() - Register route in
src/router/index.tsxunderMainLayoutchildren - Add sidebar entry in
src/components/layout/Sidebar.tsx(site-scoped →requiresSite: truesection) - Add API functions in
src/api/service.ts(usexData()for site endpoints) - Add shared entity types in
src/types/index.tsif needed (page-local types can stay in the page file)
UI Conventions
PageShell
<PageShell title="标题" description="说明" actions={<Button>操作</Button>}>
{children}
</PageShell>
Max width 1400px. Used on every page.
List / CRUD Pages
Pattern (reference: src/pages/article/FriendLinkPage.tsx):
PageShell
└── content-panel
└── Table (shadcn) + Pagination
└── Dialog (create/edit form)
└── ConfirmDialog (delete)
└── Loading overlay
└── toast via sonner
State: useState for loading, list, total, pageNum, pageSize, dialogOpen, form, deleteTarget; useEffect + useCallback for data fetch.
Form Layout Classes (src/index.css)
| Class | Use |
|---|---|
dialog-form-row |
Label + control, vertically centered |
dialog-form-row-top |
Label + control, top-aligned (textarea, Select) |
dialog-form-row-wide / -wide-top |
Wider label column |
dialog-form-narrow |
Narrow label (6rem) |
content-panel |
Card wrapper for table/content |
table-scroll-panel |
Horizontal scroll for wide tables |
table-action-icon / -danger |
Row action buttons |
Use FormLabel for required fields (shows red *), Label from shadcn for simple labels.
Interactive Pages
Reference: src/pages/article/AiTopicPage.tsx — form panels, polling, navigation after job complete.
State Management Reality
| Tool | Usage |
|---|---|
Page useState + useEffect |
Primary pattern for all list/form pages |
zustand (useAppStore) |
Login dialog, siteNameX, sidebar collapsed |
| sessionStorage | token, name, domain, root_path |
| TanStack Query | Provider mounted in main.tsx but not used — do not introduce unless requested |
| TanStack Table | Dependency installed but not used — use shadcn Table |
AI Pages
| Route | File | Flow |
|---|---|---|
/ai-topics |
AiTopicPage.tsx |
Seed keyword → suggestions → select → confirm generate → poll → navigate /articles?ai=1 |
/ai |
AiSettingsLayout.tsx |
Secondary tabs for AI admin pages |
/ai/config |
AiConfigPage.tsx |
Provider/model settings panel |
/ai/mcp |
McpAccessPage.tsx |
MCP Key + 桌面 Agent 配置示例 |
AI API services (all in service.ts, prefix /api/ai/):
| Service | Endpoint |
|---|---|
topicSuggestService |
topic_suggest/ |
topicConfirmGenerateService |
topic_confirm_generate/ |
getTopicSessionService |
topic_session/ |
getTopicSessionsService |
topic_sessions/ |
getAiConfigSettingsService |
config_settings/ |
ArticleListPage.tsx integrates AI: URL ?ai=1 filters ai_only, shows AI badge when ai_generated === 'Y'.
Reference Pages
| Pattern | File |
|---|---|
| Standard CRUD | src/pages/article/FriendLinkPage.tsx |
| Complex list | src/pages/article/ArticleListPage.tsx |
| Interactive / polling | src/pages/article/AiTopicPage.tsx |
| Config panel | src/pages/sys/AiConfigPage.tsx |
DO NOT
- Split
service.tsinto multiple files without explicit request - Use TanStack Table in new pages — use shadcn
Table - Introduce TanStack Query in new pages by default
- Upload files via axios — use
FileUpload.tsx - Add routes without updating
Sidebar.tsxwhen the page needs menu access - Use class components
