refactor(dashboard): remove Pyramid and align activity charts
This commit is contained in:
@@ -3,9 +3,7 @@ import {
|
|||||||
Banner,
|
Banner,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Input,
|
|
||||||
Spin,
|
Spin,
|
||||||
Table,
|
|
||||||
Tag,
|
Tag,
|
||||||
Toast,
|
Toast,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -14,9 +12,7 @@ import {
|
|||||||
BarChart3,
|
BarChart3,
|
||||||
CalendarDays,
|
CalendarDays,
|
||||||
Clock3,
|
Clock3,
|
||||||
Layers3,
|
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Search,
|
|
||||||
Send,
|
Send,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@@ -24,14 +20,6 @@ import { api, post } from "../api";
|
|||||||
import type { Account, Job } from "../types";
|
import type { Account, Job } from "../types";
|
||||||
import "./home.css";
|
import "./home.css";
|
||||||
|
|
||||||
type PyramidRow = {
|
|
||||||
region: string;
|
|
||||||
delay: number;
|
|
||||||
category_id: string;
|
|
||||||
category_name: string;
|
|
||||||
value: number | null;
|
|
||||||
};
|
|
||||||
type PyramidSnapshot = { items: PyramidRow[]; fetched_at: string };
|
|
||||||
type ActivityDay = { date: string; count: number | null };
|
type ActivityDay = { date: string; count: number | null };
|
||||||
type ActivitySnapshot = {
|
type ActivitySnapshot = {
|
||||||
items: ActivityDay[];
|
items: ActivityDay[];
|
||||||
@@ -108,7 +96,6 @@ export function HomePage({
|
|||||||
const [sending, setSending] = useState(false);
|
const [sending, setSending] = useState(false);
|
||||||
const [verificationId, setVerificationId] = useState<string | null>(null);
|
const [verificationId, setVerificationId] = useState<string | null>(null);
|
||||||
const [verificationError, setVerificationError] = useState("");
|
const [verificationError, setVerificationError] = useState("");
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = window.setInterval(() => setNow(new Date()), 1000);
|
const timer = window.setInterval(() => setNow(new Date()), 1000);
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
@@ -128,11 +115,6 @@ export function HomePage({
|
|||||||
connected,
|
connected,
|
||||||
revision,
|
revision,
|
||||||
);
|
);
|
||||||
const multipliers = useResource<PyramidSnapshot>(
|
|
||||||
"/dashboard/pyramid-multipliers",
|
|
||||||
connected,
|
|
||||||
revision,
|
|
||||||
);
|
|
||||||
const verification = jobs.find((job) => job.id === verificationId);
|
const verification = jobs.find((job) => job.id === verificationId);
|
||||||
const authRunning = jobs.some(
|
const authRunning = jobs.some(
|
||||||
(job) =>
|
(job) =>
|
||||||
@@ -166,21 +148,6 @@ export function HomePage({
|
|||||||
setSending(false);
|
setSending(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const eligible = (multipliers.data?.items ?? [])
|
|
||||||
.filter((row) => row.value !== null && row.value > 1)
|
|
||||||
.sort(
|
|
||||||
(a, b) =>
|
|
||||||
b.value! - a.value! ||
|
|
||||||
a.region.localeCompare(b.region) ||
|
|
||||||
a.delay - b.delay ||
|
|
||||||
a.category_id.localeCompare(b.category_id),
|
|
||||||
);
|
|
||||||
const query = search.trim().toLowerCase();
|
|
||||||
const rows = eligible.filter((row) =>
|
|
||||||
`${row.region} D${row.delay} ${row.category_id} ${row.category_name}`
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(query),
|
|
||||||
);
|
|
||||||
const submittedToday =
|
const submittedToday =
|
||||||
submissions.data?.today === today
|
submissions.data?.today === today
|
||||||
? submissions.data.items.find((day) => day.date === today)?.count
|
? submissions.data.items.find((day) => day.date === today)?.count
|
||||||
@@ -191,8 +158,6 @@ export function HomePage({
|
|||||||
(sum, day) => sum + day.count!,
|
(sum, day) => sum + day.count!,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
const unknownMultipliers =
|
|
||||||
multipliers.data?.items.filter((row) => row.value === null).length ?? 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="home-page" aria-label="首页看板">
|
<section className="home-page" aria-label="首页看板">
|
||||||
@@ -286,15 +251,8 @@ export function HomePage({
|
|||||||
}
|
}
|
||||||
loading={simulations.loading}
|
loading={simulations.loading}
|
||||||
/>
|
/>
|
||||||
<Metric
|
|
||||||
label="Pyramid 加成类别"
|
|
||||||
icon={<Layers3 size={18} aria-hidden="true" />}
|
|
||||||
value={multipliers.data ? eligible.length : null}
|
|
||||||
unit="类"
|
|
||||||
hint="当前乘数 > 1 的地区 / 延迟 / 类别"
|
|
||||||
loading={multipliers.loading}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="home-panels">
|
||||||
<Card
|
<Card
|
||||||
className="home-card home-calendar-card"
|
className="home-card home-calendar-card"
|
||||||
title={
|
title={
|
||||||
@@ -313,7 +271,6 @@ export function HomePage({
|
|||||||
{submissions.data && <SubmissionCalendar data={submissions.data} />}
|
{submissions.data && <SubmissionCalendar data={submissions.data} />}
|
||||||
</ResourceContent>
|
</ResourceContent>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="home-panels">
|
|
||||||
<Card
|
<Card
|
||||||
className="home-card"
|
className="home-card"
|
||||||
title={
|
title={
|
||||||
@@ -332,92 +289,6 @@ export function HomePage({
|
|||||||
{simulations.data && <SimulationChart data={simulations.data} />}
|
{simulations.data && <SimulationChart data={simulations.data} />}
|
||||||
</ResourceContent>
|
</ResourceContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
|
||||||
className="home-card home-pyramid-card"
|
|
||||||
title={
|
|
||||||
<PanelTitle
|
|
||||||
icon={<Layers3 size={18} aria-hidden="true" />}
|
|
||||||
title="Pyramid 乘数"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
headerExtraContent={<Tag color="blue">乘数 > 1</Tag>}
|
|
||||||
>
|
|
||||||
<ResourceContent
|
|
||||||
state={multipliers}
|
|
||||||
connected={connected}
|
|
||||||
name="Pyramid 乘数"
|
|
||||||
>
|
|
||||||
{multipliers.data && (
|
|
||||||
<>
|
|
||||||
<div className="home-table-tools">
|
|
||||||
<Input
|
|
||||||
aria-label="搜索 Pyramid 类别"
|
|
||||||
prefix={<Search size={16} aria-hidden="true" />}
|
|
||||||
value={search}
|
|
||||||
onChange={setSearch}
|
|
||||||
showClear
|
|
||||||
placeholder="搜索地区、延迟或类别"
|
|
||||||
/>
|
|
||||||
<span>{rows.length} 类</span>
|
|
||||||
</div>
|
|
||||||
<div className="home-table-wrap">
|
|
||||||
<Table<PyramidRow>
|
|
||||||
dataSource={rows}
|
|
||||||
rowKey={(row) =>
|
|
||||||
row ? `${row.region}:${row.delay}:${row.category_id}` : ""
|
|
||||||
}
|
|
||||||
pagination={false}
|
|
||||||
size="small"
|
|
||||||
sticky
|
|
||||||
empty={
|
|
||||||
eligible.length
|
|
||||||
? "没有匹配的 Pyramid 类别"
|
|
||||||
: "当前没有乘数大于 1 的类别"
|
|
||||||
}
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
title: "数据类别",
|
|
||||||
dataIndex: "category_name",
|
|
||||||
render: (_, row) => (
|
|
||||||
<div className="home-category">
|
|
||||||
<strong>{row.category_name}</strong>
|
|
||||||
<small>{row.category_id}</small>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{ title: "地区", dataIndex: "region", width: 68 },
|
|
||||||
{
|
|
||||||
title: "延迟",
|
|
||||||
key: "delay",
|
|
||||||
width: 58,
|
|
||||||
render: (_, row) => `D${row.delay}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "乘数",
|
|
||||||
key: "value",
|
|
||||||
width: 80,
|
|
||||||
align: "right",
|
|
||||||
render: (_, row) => (
|
|
||||||
<Tag color="blue">
|
|
||||||
{row.value?.toLocaleString("zh-CN")}×
|
|
||||||
</Tag>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p className="home-footnote">
|
|
||||||
按乘数从高到低排列
|
|
||||||
{unknownMultipliers
|
|
||||||
? ` · ${unknownMultipliers} 类乘数未知,未列入`
|
|
||||||
: ""}{" "}
|
|
||||||
· 更新于 {easternTime(new Date(multipliers.data.fetched_at))}{" "}
|
|
||||||
美东
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</ResourceContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
}
|
}
|
||||||
.home-metrics {
|
.home-metrics {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
@@ -107,14 +107,11 @@
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.home-calendar-card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.home-panels {
|
.home-panels {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
align-items: start;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.home-connect {
|
.home-connect {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -170,15 +167,15 @@
|
|||||||
.home-calendar {
|
.home-calendar {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
min-width: 550px;
|
min-width: 510px;
|
||||||
max-width: 840px;
|
max-width: 840px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
.home-weekdays {
|
.home-weekdays {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: repeat(7, 1fr);
|
grid-template-rows: repeat(7, 1fr);
|
||||||
gap: 5px;
|
gap: 4px;
|
||||||
padding-top: 25px;
|
padding-top: 24px;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
@@ -188,15 +185,17 @@
|
|||||||
}
|
}
|
||||||
.home-calendar-weeks {
|
.home-calendar-weeks {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
grid-auto-columns: minmax(14px, 1fr);
|
grid-auto-columns: minmax(14px, 1fr);
|
||||||
gap: 5px;
|
gap: 4px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.home-calendar-week {
|
.home-calendar-week {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
grid-template-rows: 20px repeat(7, 1fr);
|
grid-template-rows: 20px repeat(7, 1fr);
|
||||||
gap: 5px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
.home-month {
|
.home-month {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
@@ -282,32 +281,6 @@ body[theme-mode="dark"] .heat-4 {
|
|||||||
.home-legend > span {
|
.home-legend > span {
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
.home-table-tools {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.home-table-tools > span {
|
|
||||||
color: var(--muted);
|
|
||||||
font-size: 12px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.home-table-wrap {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.home-category {
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
.home-category strong {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
.home-category small {
|
|
||||||
display: block;
|
|
||||||
margin-top: 3px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
.home-footnote {
|
.home-footnote {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|||||||
Reference in New Issue
Block a user