Add admin dashboard filtering and sorting
Provide stable server-side list queries and focused chart controls so operators can inspect large datasets without misleading partial-page ordering.
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
import type { TrendPoint } from "../../api/types";
|
||||
import { formatNumber } from "../../lib/format";
|
||||
|
||||
export function TrendChart({ points }: { points: TrendPoint[] }) {
|
||||
export function TrendChart({
|
||||
points,
|
||||
showRegistrations = true,
|
||||
showCredits = true,
|
||||
}: {
|
||||
points: TrendPoint[];
|
||||
showRegistrations?: boolean;
|
||||
showCredits?: boolean;
|
||||
}) {
|
||||
if (points.length === 0) {
|
||||
return <div className="grid h-full place-items-center text-sm text-muted">暂无趋势数据</div>;
|
||||
}
|
||||
if (!showRegistrations && !showCredits) {
|
||||
return <div className="grid h-full place-items-center text-sm text-muted">请选择至少一个趋势系列</div>;
|
||||
}
|
||||
|
||||
const width = 760;
|
||||
const height = 300;
|
||||
@@ -53,26 +64,38 @@ export function TrendChart({ points }: { points: TrendPoint[] }) {
|
||||
className="chart-grid-line"
|
||||
/>
|
||||
))}
|
||||
<text x={paddingX} y={paddingY - 9} className="chart-label">
|
||||
{formatNumber(registrationMax)} 用户
|
||||
</text>
|
||||
<text x={width - paddingX} y={paddingY - 9} textAnchor="end" className="chart-label">
|
||||
{formatNumber(creditMax)} 积分
|
||||
</text>
|
||||
<polyline points={registrationLine} className="chart-line chart-line--primary" />
|
||||
<polyline points={creditLine} className="chart-line chart-line--violet" />
|
||||
{showRegistrations ? (
|
||||
<text x={paddingX} y={paddingY - 9} className="chart-label">
|
||||
{formatNumber(registrationMax)} 用户
|
||||
</text>
|
||||
) : null}
|
||||
{showCredits ? (
|
||||
<text x={width - paddingX} y={paddingY - 9} textAnchor="end" className="chart-label">
|
||||
{formatNumber(creditMax)} 积分
|
||||
</text>
|
||||
) : null}
|
||||
{showRegistrations ? (
|
||||
<polyline points={registrationLine} className="chart-line chart-line--primary" />
|
||||
) : null}
|
||||
{showCredits ? (
|
||||
<polyline points={creditLine} className="chart-line chart-line--violet" />
|
||||
) : null}
|
||||
{coordinates.map(({ point, x, registrationY, creditY }, index) => (
|
||||
<g key={point.date}>
|
||||
<circle cx={x} cy={registrationY} r="4" className="chart-dot chart-dot--primary">
|
||||
<title>
|
||||
{point.date}:新增 {formatNumber(point.registrations)} 位用户
|
||||
</title>
|
||||
</circle>
|
||||
<circle cx={x} cy={creditY} r="3.5" className="chart-dot chart-dot--violet">
|
||||
<title>
|
||||
{point.date}:消耗 {formatNumber(point.creditsUsed)} 积分
|
||||
</title>
|
||||
</circle>
|
||||
{showRegistrations ? (
|
||||
<circle cx={x} cy={registrationY} r="4" className="chart-dot chart-dot--primary">
|
||||
<title>
|
||||
{point.date}:新增 {formatNumber(point.registrations)} 位用户
|
||||
</title>
|
||||
</circle>
|
||||
) : null}
|
||||
{showCredits ? (
|
||||
<circle cx={x} cy={creditY} r="3.5" className="chart-dot chart-dot--violet">
|
||||
<title>
|
||||
{point.date}:消耗 {formatNumber(point.creditsUsed)} 积分
|
||||
</title>
|
||||
</circle>
|
||||
) : null}
|
||||
{index % labelEvery === 0 || index === points.length - 1 ? (
|
||||
<text
|
||||
x={x}
|
||||
@@ -92,16 +115,16 @@ export function TrendChart({ points }: { points: TrendPoint[] }) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">UTC 日期</th>
|
||||
<th scope="col">新增用户</th>
|
||||
<th scope="col">消耗积分</th>
|
||||
{showRegistrations ? <th scope="col">新增用户</th> : null}
|
||||
{showCredits ? <th scope="col">消耗积分</th> : null}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{points.map((point) => (
|
||||
<tr key={point.date}>
|
||||
<td>{point.date}</td>
|
||||
<td>{formatNumber(point.registrations)}</td>
|
||||
<td>{formatNumber(point.creditsUsed)}</td>
|
||||
{showRegistrations ? <td>{formatNumber(point.registrations)}</td> : null}
|
||||
{showCredits ? <td>{formatNumber(point.creditsUsed)}</td> : null}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user