Files
model-platform/frontend/app/features/admin/DashboardPage.tsx
T
tao.chenandClaude Fable 5 91031342b8 refactor(frontend): consolidate features & components into single tree
把 components/admin/* 和 components/platform/* 共 11 个文件
移到对应 features/<name>/ 下,components/ 只保留跨特性共享的
common/ 子目录(features/admin/* 之前和 components/admin/* 同名
共存,迫使 features/admin/AdminPages.tsx 出现 barrel,顺带消除了
barrel 副作用 CSS 丢失的隐患)。

新的目录约定:
  - features/<name>/  = 特性模块,所有页面 / Modal / state / hooks
                         / routes 都在内,无 components/<name>/ 并列
  - components/common/ = 仅放 ≥2 个特性共用的 widgets
                         (Icon、Sidebar、Toast、Topbar、WelcomePanel)

改动:
  - 11 文件 git mv (components/{admin,platform}/* → features/*)
  - 8 文件的 Icon 相对路径 '../common/Icon' → '../../components/common/Icon'
  - 2 文件补 admin.css side-effect import(UserManagementPage /
    ProjectManagementPage 之前依赖 AdminPages barrel)
  - 3 文件(引用方)更新 import:SystemAdminPage、DashboardRoute、
    ScriptsPage
  - 删除 features/admin/AdminPages.tsx barrel(无消费者)
  - 删除空目录 components/admin、components/platform

验证:
  - pnpm typecheck 通过
  - pnpm build 通过,CSS 体积 35.11 kB 与重构前一致(无样式增减)
  - routes.ts / routes/platform.tsx 路径未动,route id 不变

踩坑:
  第一轮把 '../common/Icon' 错改成 '../../../components/common/Icon',
  typecheck 报 11 个 Cannot find module。原因:features/admin/ 和
  components/admin/ 都是 depth 2,'../../X' 在两边都解析到 app/X,
  只有 '../X' 才需要多一层。正确改法是 '../../components/common/Icon'。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:27:26 +08:00

91 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Icon from "../../components/common/Icon";
import { useAuth } from "../../context/AuthContext";
import "../../styles/admin.css";
import "../../styles/dashboard.css";
export function DashboardPage({
scriptCount,
online,
onNavigate,
}: {
scriptCount: number;
online: boolean;
onNavigate: (page: "scripts" | "schedules" | "system") => void;
}) {
const { user, currentWorkspace } = useAuth();
const isSystemAdmin = user?.is_system_admin ?? false;
return (
<section className="dashboard-page">
<div className="dashboard-hero">
<div>
<span>MODEL DEVELOPMENT PLATFORM</span>
<h2>下午好,{user?.display_name ?? "用户"}</h2>
<p>
当前位于 {currentWorkspace?.workspace_name ?? "(未选择 Workspace"}
,可以继续构建脚本或配置调度。
</p>
</div>
<span className="dashboard-hero__badge">{online ? "服务正常" : "服务连接中"}</span>
</div>
<div className="dashboard-metrics">
<article><Icon name="script" /><span><b>{scriptCount}</b><small>工作副本</small></span></article>
<article><Icon name="workspace" /><span><b>2</b><small>Workspace</small></span></article>
<article><Icon name="settings" /><span><b>4</b><small>平台用户</small></span></article>
<article><Icon name="check" /><span><b>{online ? "正常" : "检查中"}</b><small>平台状态</small></span></article>
</div>
<div className="dashboard-actions">
<button type="button" onClick={() => onNavigate("scripts")}><Icon name="script" />构建脚本</button>
<button type="button" onClick={() => onNavigate("schedules")}><Icon name="schedule" />调度配置</button>
{isSystemAdmin && (
<button type="button" onClick={() => onNavigate("system")}><Icon name="settings" />系统管理</button>
)}
</div>
<div className="dashboard-grid">
<section className="dashboard-panel dashboard-panel--trend">
<header><div><span>运行趋势</span><h3> 7 天调度执行</h3></div><b>成功率 92.6%</b></header>
<div className="trend-chart">
{[38, 55, 44, 73, 61, 86, 78].map((value, index) => (
<div className="trend-column" key={index}>
<span className="trend-column__value">{Math.round(value / 7)}</span>
<i style={{ height: `${value}%` }} />
<small>{["周一", "周二", "周三", "周四", "周五", "周六", "今天"][index]}</small>
</div>
))}
</div>
<footer><span><i className="legend-dot is-success" />成功 75</span><span><i className="legend-dot is-failed" />失败 6</span></footer>
</section>
<section className="dashboard-panel dashboard-panel--donut">
<header><div><span>脚本资产</span><h3>类型分布</h3></div></header>
<div className="donut-layout">
<div className="donut-chart"><span><b>{scriptCount}</b><small>全部脚本</small></span></div>
<div className="donut-legend">
<span><i className="legend-dot is-notebook" /><b>Notebook</b><small>{Math.max(1, Math.round(scriptCount * .67))} · 67%</small></span>
<span><i className="legend-dot is-python" /><b>Python</b><small>{Math.max(0, scriptCount - Math.round(scriptCount * .67))} · 33%</small></span>
<span><i className="legend-dot is-version" /><b>稳定版本</b><small>3 个已发布</small></span>
</div>
</div>
</section>
<section className="dashboard-panel dashboard-panel--activity">
<header><div><span>ACTIVITY</span><h3>最近平台活动</h3></div><button type="button">查看全部</button></header>
<div className="activity-table">
<div className="activity-table__head"><span>操作内容</span><span>执行人</span><span>状态</span><span>时间</span></div>
{[
["数据探索.ipynb 发布稳定版本 v3.0", "张三", "成功", "16:42"],
["每日模型训练流程完成调度运行", "Scheduler", "成功", "15:25"],
["批量预测.py 更新工作副本", "王五", "已同步", "14:18"],
["风险验证流程完成 DAG 校验", "李四", "成功", "11:06"],
].map((row) => (
<div className="activity-row" key={row[0]}>
<span><i className="activity-icon"><Icon name="check" size={13} /></i>{row[0]}</span>
<span>{row[1]}</span><span><b>{row[2]}</b></span><span>{row[3]}</span>
</div>
))}
</div>
</section>
</div>
</section>
);
}