mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 22:52:19 +03:00
fix(dashboard): show account email/name in Utilization Account Split cards (#10939)
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint todos verdes. Fix de UX real e bem documentado (cards de Account Split mostravam UUID cru em vez de email/nome da conta). CI vermelho é o base-red já rastreado em #9985. Obrigado!
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useProviderNodeMap, resolveProviderName } from "@/lib/display/useProviderNodeMap";
|
||||
import { getAccountDisplayName } from "@/lib/display/names";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const ProviderCharts = dynamic(() => import("./components/ProviderCharts"), { ssr: false });
|
||||
@@ -311,19 +312,39 @@ export default function ProviderUtilizationTab() {
|
||||
{latestPoints.map((point) => {
|
||||
const isLow = point.remainingPct <= 20;
|
||||
|
||||
// For Account Split, parse "provider:connectionId" and resolve display name
|
||||
const colonIdx = point.provider.indexOf(":");
|
||||
const isConnectionKey = aggregateBy === "connection" && colonIdx !== -1;
|
||||
const providerPart = isConnectionKey
|
||||
? point.provider.slice(0, colonIdx)
|
||||
: point.provider;
|
||||
const connectionId = isConnectionKey ? point.provider.slice(colonIdx + 1) : null;
|
||||
const connMeta = connectionId ? data?.connectionMeta?.[connectionId] : null;
|
||||
const cardTitle = isConnectionKey
|
||||
? getAccountDisplayName({
|
||||
id: connectionId ?? undefined,
|
||||
email: connMeta?.email,
|
||||
name: connMeta?.name,
|
||||
displayName: connMeta?.displayName,
|
||||
})
|
||||
: resolveProviderName(point.provider, nodeMap);
|
||||
const cardSubtitle = isConnectionKey
|
||||
? `${providerPart} · account ${(connectionId ?? "").slice(0, 8)}…`
|
||||
: t("providerUtilizationLatestSnapshot");
|
||||
|
||||
return (
|
||||
<Card.Section key={point.provider} className="flex h-full flex-col gap-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-11 w-11 items-center justify-center rounded-xl border border-black/5 bg-surface text-text-main dark:border-white/5">
|
||||
<ProviderIcon providerId={point.provider} size={22} />
|
||||
<ProviderIcon providerId={providerPart} size={22} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-text-main">
|
||||
{resolveProviderName(point.provider, nodeMap)}
|
||||
{cardTitle}
|
||||
</p>
|
||||
<p className="text-xs text-text-muted">
|
||||
{t("providerUtilizationLatestSnapshot")}
|
||||
{cardSubtitle}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getAggregatedSnapshots } from "@/lib/db/quotaSnapshots";
|
||||
import type { ProviderUtilizationResponse, UtilizationTimeRange } from "@/shared/types/utilization";
|
||||
import { getConnection } from "@/lib/db/connections";
|
||||
import type {
|
||||
ProviderUtilizationResponse,
|
||||
UtilizationTimeRange,
|
||||
ConnectionMetaEntry,
|
||||
} from "@/shared/types/utilization";
|
||||
import { BUCKET_SIZES } from "@/shared/types/utilization";
|
||||
|
||||
const VALID_RANGES: UtilizationTimeRange[] = ["1h", "24h", "7d", "30d"];
|
||||
@@ -55,11 +60,28 @@ export async function GET(request: Request) {
|
||||
|
||||
const providers = Array.from(new Set(data.map((d) => d.provider)));
|
||||
|
||||
let connectionMeta: Record<string, ConnectionMetaEntry> | undefined;
|
||||
if (aggregateBy === "connection") {
|
||||
const uniqueConnectionIds = new Set(
|
||||
data.map((d) => d.provider.split(":").slice(1).join(":")).filter(Boolean)
|
||||
);
|
||||
connectionMeta = {};
|
||||
for (const cid of uniqueConnectionIds) {
|
||||
const conn = getConnection(cid);
|
||||
connectionMeta[cid] = {
|
||||
email: conn?.email ?? null,
|
||||
name: conn?.name ?? null,
|
||||
displayName: conn?.displayName ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const response: ProviderUtilizationResponse = {
|
||||
timeRange: range,
|
||||
bucketSizeMinutes: bucketMinutes,
|
||||
providers,
|
||||
data,
|
||||
connectionMeta,
|
||||
};
|
||||
|
||||
return NextResponse.json(response);
|
||||
|
||||
@@ -19,11 +19,18 @@ export interface ProviderUtilizationPoint {
|
||||
windowKey: string;
|
||||
}
|
||||
|
||||
export interface ConnectionMetaEntry {
|
||||
email: string | null;
|
||||
name: string | null;
|
||||
displayName: string | null;
|
||||
}
|
||||
|
||||
export interface ProviderUtilizationResponse {
|
||||
timeRange: "1h" | "24h" | "7d" | "30d";
|
||||
bucketSizeMinutes: number;
|
||||
providers: string[];
|
||||
data: ProviderUtilizationPoint[];
|
||||
connectionMeta?: Record<string, ConnectionMetaEntry>;
|
||||
}
|
||||
|
||||
export interface ComboHealthMetrics {
|
||||
|
||||
Reference in New Issue
Block a user