diff --git a/src/app/dashboard/ai/costs/page.tsx b/src/app/dashboard/ai/costs/page.tsx new file mode 100644 index 0000000..38c3863 --- /dev/null +++ b/src/app/dashboard/ai/costs/page.tsx @@ -0,0 +1,149 @@ +'use client'; + +import { useQuery } from '@tanstack/react-query'; +import { apiFetch } from '../../../../lib/api'; +import { useSession } from '../../../../components/session-provider'; + +interface AiCosts { + totals: { totalCostUsd: string | null; totalRequests: number }; + byModel: { model: string; requests: number; costUsd: string | null }[]; + byPurpose: { purpose: string; requests: number; costUsd: string | null }[]; + byStatus: { resultStatus: string; requests: number }[]; +} + +export default function AiCostsPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + + const { data, isLoading } = useQuery({ + queryKey: ['ai-costs', tenantId], + queryFn: () => apiFetch('/v1/ai-requests/costs', { tenantId }), + enabled: Boolean(tenantId), + staleTime: 60_000, + }); + + const totalCost = Number(data?.totals?.totalCostUsd ?? 0); + const totalReqs = data?.totals?.totalRequests ?? 0; + + return ( +
+
+

Costuri & Utilizare AI

+

+ Statistici agregate pentru toate cererile AI din workspace. +

+
+ + {isLoading ? ( +

Se încarcă…

+ ) : ( + <> +
+
+

Total cereri

+

{totalReqs}

+
+
+

Cost total

+

${totalCost.toFixed(4)}

+
+
+

Cost mediu/cerere

+

+ {totalReqs > 0 ? `$${(totalCost / totalReqs).toFixed(4)}` : '—'} +

+
+
+

Modele utilizate

+

+ {data?.byModel?.length ?? 0} +

+
+
+ + {totalReqs === 0 ? ( +
+

Nicio cerere AI înregistrată

+

+ Costurile sunt înregistrate automat la utilizarea funcționalităților AI. +

+
+ ) : ( +
+
+

+ Cost per model +

+
    + {(data?.byModel ?? []) + .sort((a, b) => Number(b.costUsd ?? 0) - Number(a.costUsd ?? 0)) + .map((m) => { + const pct = totalCost > 0 ? (Number(m.costUsd ?? 0) / totalCost) * 100 : 0; + return ( +
  • +
    + {m.model} + + ${Number(m.costUsd ?? 0).toFixed(4)} · {m.requests}x + +
    +
    +
    +
    +
  • + ); + })} +
+
+ +
+

+ Cost per scop +

+
    + {(data?.byPurpose ?? []) + .sort((a, b) => Number(b.costUsd ?? 0) - Number(a.costUsd ?? 0)) + .slice(0, 8) + .map((p) => { + const pct = totalCost > 0 ? (Number(p.costUsd ?? 0) / totalCost) * 100 : 0; + return ( +
  • +
    + + {p.purpose.replace(/_/g, ' ')} + + + ${Number(p.costUsd ?? 0).toFixed(4)} + +
    +
    +
    +
    +
  • + ); + })} +
+
+ +
+

+ Distribuție status +

+
    + {(data?.byStatus ?? []).map((s) => ( +
  • + + {s.resultStatus.replace(/_/g, ' ')} + + {s.requests} +
  • + ))} +
+
+
+ )} + + )} +
+ ); +}