diff --git a/apps/admin/src/sections/dashboard/components/statistics.tsx b/apps/admin/src/sections/dashboard/components/statistics.tsx index 5eed7cd..db3b069 100644 --- a/apps/admin/src/sections/dashboard/components/statistics.tsx +++ b/apps/admin/src/sections/dashboard/components/statistics.tsx @@ -13,13 +13,7 @@ import { ChartTooltip, ChartTooltipContent, } from "@workspace/ui/components/chart"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@workspace/ui/components/select"; +// (Select imports removed) import { Separator } from "@workspace/ui/components/separator"; import { Tabs, TabsList, TabsTrigger } from "@workspace/ui/components/tabs"; import Empty from "@workspace/ui/composed/empty"; @@ -62,7 +56,6 @@ export default function Statistics() { }, }); - const [dataType, setDataType] = useState("nodes"); const [timeFrame, setTimeFrame] = useState( "today" ); @@ -93,10 +86,112 @@ export default function Statistics() { })) || [], }, }; - const currentData = - trafficData[dataType as "nodes" | "users"][ - timeFrame as "today" | "yesterday" - ]; + + const TrafficRankCard = ({ type }: { type: "nodes" | "users" }) => { + const currentData = trafficData[type][timeFrame as "today" | "yesterday"]; + + return ( + + + + {type === "nodes" + ? t("nodeTraffic", "Node Traffic") + : t("userTraffic", "User Traffic")} + + + + {t("today", "Today")} + + {t("yesterday", "Yesterday")} + + + + + + {currentData.length > 0 ? ( + + + + formatBytes(value || 0)} + tickLine={false} + type="number" + /> + String(index + 1)} + tickLine={false} + tickMargin={0} + type="category" + width={15} + /> + formatBytes(Number(value) || 0)} + label={true} + labelFormatter={(label, [payload]) => + type === "nodes" ? ( + `${t("nodes", "Nodes")}: ${label}` + ) : ( + <> +
+ +
+ +
{`${t("users", "Users")}: ${label}`}
+ + ) + } + /> + } + trigger="hover" + /> + + + +
+
+ ) : ( +
+ +
+ )} +
+
+ ); + }; return ( <> @@ -189,122 +284,14 @@ export default function Statistics() { ))} -
+
- - - {t("trafficRank", "Traffic Rank")} - - - {t("today", "Today")} - - {t("yesterday", "Yesterday")} - - - - - -
-

- {dataType === "nodes" - ? t("nodeTraffic", "Node Traffic") - : t("userTraffic", "User Traffic")} -

- -
- {currentData.length > 0 ? ( - - - - formatBytes(value || 0)} - tickLine={false} - type="number" - /> - String(index + 1)} - tickLine={false} - tickMargin={0} - type="category" - width={15} - /> - formatBytes(Number(value) || 0)} - label={true} - labelFormatter={(label, [payload]) => - dataType === "nodes" ? ( - `${t("nodes", "Nodes")}: ${label}` - ) : ( - <> -
- -
- -
{`${t("users", "Users")}: ${label}`}
- - ) - } - /> - } - trigger="hover" - /> - - - -
-
- ) : ( -
- -
- )} -
-
+
+ +
+ +
); diff --git a/apps/admin/src/sections/ticket/index.tsx b/apps/admin/src/sections/ticket/index.tsx index 34a1db0..4ad663f 100644 --- a/apps/admin/src/sections/ticket/index.tsx +++ b/apps/admin/src/sections/ticket/index.tsx @@ -168,8 +168,31 @@ export default function Page() { ...pagination, ...filters, }); + + const list = (data.data?.list || []) as API.Ticket[]; + + // Client-side ordering to improve triage efficiency: + // - Put "Pending Follow-up" (status=1) before "Pending Reply" (status=2) + // - Within each group, sort by updated_at desc + const statusPriority = (status: number) => { + if (status === 1) return 0; + if (status === 2) return 1; + return 2; + }; + const toTime = (value: any) => { + const t = new Date(value).getTime(); + return Number.isFinite(t) ? t : 0; + }; + + list.sort((a, b) => { + const pa = statusPriority(a.status); + const pb = statusPriority(b.status); + if (pa !== pb) return pa - pb; + return toTime(b.updated_at) - toTime(a.updated_at); + }); + return { - list: data.data?.list || [], + list, total: data.data?.total || 0, }; }}