Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions messages/en/myUsage.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"keyStats": "Key",
"userStats": "User",
"noData": "No data for selected period",
"breakdownPrevPage": "Previous page",
"breakdownNextPage": "Next page",
"breakdownPageIndicator": "{current} / {total}",
"unknownModel": "Unknown",
"modal": {
"requests": "Requests",
Expand Down
3 changes: 3 additions & 0 deletions messages/ja/myUsage.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"keyStats": "キー",
"userStats": "ユーザー",
"noData": "選択期間のデータがありません",
"breakdownPrevPage": "前のページ",
"breakdownNextPage": "次のページ",
"breakdownPageIndicator": "{current} / {total}",
"unknownModel": "不明",
"modal": {
"requests": "リクエスト",
Expand Down
3 changes: 3 additions & 0 deletions messages/ru/myUsage.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"keyStats": "Ключ",
"userStats": "Пользователь",
"noData": "Нет данных за выбранный период",
"breakdownPrevPage": "Предыдущая страница",
"breakdownNextPage": "Следующая страница",
"breakdownPageIndicator": "{current} / {total}",
"unknownModel": "Неизвестно",
"modal": {
"requests": "Запросов",
Expand Down
3 changes: 3 additions & 0 deletions messages/zh-CN/myUsage.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"keyStats": "密钥",
"userStats": "用户",
"noData": "所选时段无数据",
"breakdownPrevPage": "上一页",
"breakdownNextPage": "下一页",
"breakdownPageIndicator": "{current} / {total}",
"unknownModel": "未知",
"modal": {
"requests": "请求",
Expand Down
3 changes: 3 additions & 0 deletions messages/zh-TW/myUsage.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"keyStats": "金鑰",
"userStats": "使用者",
"noData": "所選時段無資料",
"breakdownPrevPage": "上一頁",
"breakdownNextPage": "下一頁",
"breakdownPageIndicator": "{current} / {total}",
"unknownModel": "不明",
"modal": {
"requests": "請求",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { AlertTriangle, ChevronDown, Infinity, PieChart } from "lucide-react";
import { AlertTriangle, ChevronDown, Infinity as InfinityIcon, PieChart } from "lucide-react";
import { useTranslations } from "next-intl";
import { useState } from "react";
import type { MyUsageQuota } from "@/actions/my-usage";
Expand Down Expand Up @@ -94,7 +94,7 @@ export function CollapsibleQuotaCard({
<div className="flex items-center gap-1.5">
<span className="text-muted-foreground">{t("daily")}:</span>
{dailyPct === null ? (
<Infinity className="h-4 w-4 text-muted-foreground" />
<InfinityIcon className="h-4 w-4 text-muted-foreground" />
) : (
<>
<span className={cn("font-semibold", getPercentColor(dailyPct))}>
Expand All @@ -108,7 +108,7 @@ export function CollapsibleQuotaCard({
<div className="flex items-center gap-1.5">
<span className="text-muted-foreground">{t("monthly")}:</span>
{monthlyPct === null ? (
<Infinity className="h-4 w-4 text-muted-foreground" />
<InfinityIcon className="h-4 w-4 text-muted-foreground" />
) : (
<>
<span className={cn("font-semibold", getPercentColor(monthlyPct))}>
Expand All @@ -122,7 +122,7 @@ export function CollapsibleQuotaCard({
<div className="flex items-center gap-1.5">
<span className="text-muted-foreground">{t("total")}:</span>
{totalPct === null ? (
<Infinity className="h-4 w-4 text-muted-foreground" />
<InfinityIcon className="h-4 w-4 text-muted-foreground" />
) : (
<>
<span className={cn("font-semibold", getPercentColor(totalPct))}>
Expand Down
125 changes: 109 additions & 16 deletions src/app/[locale]/my-usage/_components/provider-group-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,65 @@

import { Layers, ShieldCheck } from "lucide-react";
import { useTranslations } from "next-intl";
import { Badge } from "@/components/ui/badge";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";

function abbreviateModel(name: string): string {
const parts = name.split("-").filter(Boolean);

if (parts.length === 1) {
return parts[0].length <= 4 ? parts[0].toUpperCase() : parts[0].slice(0, 2).toUpperCase();
}

const letterParts: string[] = [];
let versionMixed = "";
const versionNums: string[] = [];

for (const part of parts) {
if (/^\d{8,}$/.test(part)) continue;
if (/^[a-zA-Z]+$/.test(part)) {
letterParts.push(part);
} else if (/^\d+\.\d+$/.test(part)) {
versionMixed = part;
} else if (/^\d+[a-zA-Z]/.test(part)) {
versionMixed = part;
} else if (/^\d+$/.test(part)) {
versionNums.push(part);
} else {
letterParts.push(part);
}
}

const prefix = letterParts
.slice(0, 3)
.map((w) => w[0].toUpperCase())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The map operation will throw an error if letterParts contains an empty string (e.g., if the model name has double hyphens), as w[0] would be undefined. It's safer to use optional chaining or filter out empty strings.

Suggested change
.map((w) => w[0].toUpperCase())
.map((w) => w[0]?.toUpperCase())
.filter(Boolean)

.join("");

let version = "";
if (versionMixed) {
version = versionMixed;
} else if (versionNums.length > 0) {
version = versionNums.slice(0, 2).join(".");
}

if (version && prefix) {
return `${prefix}-${version}`;
}
return prefix || name.toUpperCase().substring(0, 3);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

function abbreviateClient(name: string): string {
const parts = name.split(/[-\s]+/).filter(Boolean);
if (parts.length === 1) {
return name.slice(0, 2).toUpperCase();
}
return parts
.slice(0, 3)
.map((w) => w[0].toUpperCase())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to abbreviateModel, this map operation can crash if parts contains empty strings due to multiple spaces or hyphens in the name.

Suggested change
.map((w) => w[0].toUpperCase())
.filter(Boolean)
.map((w) => w[0].toUpperCase())

.join("");
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

interface ProviderGroupInfoProps {
keyProviderGroup: string | null;
userProviderGroup: string | null;
Expand All @@ -26,10 +83,8 @@ export function ProviderGroupInfo({
const userDisplay = userProviderGroup ?? tGroup("allProviders");
const inherited = !keyProviderGroup && !!userProviderGroup;

const modelsDisplay =
userAllowedModels.length > 0 ? userAllowedModels.join(", ") : tRestrictions("noRestrictions");
const clientsDisplay =
userAllowedClients.length > 0 ? userAllowedClients.join(", ") : tRestrictions("noRestrictions");
const hasModels = userAllowedModels.length > 0;
const hasClients = userAllowedClients.length > 0;

return (
<div
Expand All @@ -45,16 +100,20 @@ export function ProviderGroupInfo({
<span>{tGroup("title")}</span>
</div>
<div className="space-y-1">
<div className="flex items-baseline gap-1.5">
<span className="text-xs text-muted-foreground">{tGroup("keyGroup")}:</span>
<span className="text-sm font-semibold text-foreground">{keyDisplay}</span>
<div className="flex flex-wrap items-center gap-1.5">
<span className="shrink-0 text-xs text-muted-foreground">{tGroup("keyGroup")}:</span>
<Badge variant="outline" className="cursor-default text-xs">
{keyDisplay}
</Badge>
{inherited && (
<span className="text-xs text-muted-foreground">({tGroup("inheritedFromUser")})</span>
)}
</div>
<div className="flex items-baseline gap-1.5">
<span className="text-xs text-muted-foreground">{tGroup("userGroup")}:</span>
<span className="text-sm font-semibold text-foreground">{userDisplay}</span>
<div className="flex flex-wrap items-center gap-1.5">
<span className="shrink-0 text-xs text-muted-foreground">{tGroup("userGroup")}:</span>
<Badge variant="outline" className="cursor-default text-xs">
{userDisplay}
</Badge>
</div>
</div>
</div>
Expand All @@ -66,13 +125,47 @@ export function ProviderGroupInfo({
<span>{tRestrictions("title")}</span>
</div>
<div className="space-y-1">
<div className="flex items-baseline gap-1.5">
<span className="text-xs text-muted-foreground">{tRestrictions("models")}:</span>
<span className="text-sm font-semibold text-foreground">{modelsDisplay}</span>
<div className="flex flex-wrap items-center gap-1.5">
<span className="shrink-0 text-xs text-muted-foreground">
{tRestrictions("models")}:
</span>
{hasModels ? (
userAllowedModels.map((name) => (
<Tooltip key={name}>
<TooltipTrigger asChild>
<Badge variant="outline" className="cursor-default font-mono text-xs">
{abbreviateModel(name)}
</Badge>
</TooltipTrigger>
<TooltipContent>{name}</TooltipContent>
</Tooltip>
))
Comment on lines +133 to +142

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping TooltipTrigger with an extra <span> is unnecessary. The asChild prop makes TooltipTrigger pass its props to its child, so you can directly wrap Badge.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/my-usage/_components/provider-group-info.tsx
Line: 133:144

Comment:
Wrapping `TooltipTrigger` with an extra `<span>` is unnecessary. The `asChild` prop makes `TooltipTrigger` pass its props to its child, so you can directly wrap `Badge`.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

) : (
<span className="text-sm font-semibold text-foreground">
{tRestrictions("noRestrictions")}
</span>
)}
</div>
<div className="flex items-baseline gap-1.5">
<span className="text-xs text-muted-foreground">{tRestrictions("clients")}:</span>
<span className="text-sm font-semibold text-foreground">{clientsDisplay}</span>
<div className="flex flex-wrap items-center gap-1.5">
<span className="shrink-0 text-xs text-muted-foreground">
{tRestrictions("clients")}:
</span>
{hasClients ? (
userAllowedClients.map((name) => (
<Tooltip key={name}>
<TooltipTrigger asChild>
<Badge variant="outline" className="cursor-default font-mono text-xs">
{abbreviateClient(name)}
</Badge>
</TooltipTrigger>
<TooltipContent>{name}</TooltipContent>
</Tooltip>
))
Comment on lines +154 to +163

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue - the extra <span> wrapper is unnecessary when using asChild prop on TooltipTrigger.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/my-usage/_components/provider-group-info.tsx
Line: 156:167

Comment:
Same issue - the extra `<span>` wrapper is unnecessary when using `asChild` prop on `TooltipTrigger`.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

) : (
<span className="text-sm font-semibold text-foreground">
{tRestrictions("noRestrictions")}
</span>
)}
</div>
</div>
</div>
Expand Down
Loading
Loading