Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
fc377dae
authored
Apr 28, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
style: optimize UI
parent
df14a0bf
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
128 additions
and
332 deletions
+128
-332
web/default/src/features/pricing/components/dynamic-pricing-breakdown.tsx
+3
-3
web/default/src/features/profile/components/available-models-card.tsx
+0
-234
web/default/src/features/profile/index.tsx
+0
-5
web/default/src/features/usage-logs/components/columns/common-logs-columns.tsx
+109
-81
web/default/src/features/usage-logs/components/dialogs/details-dialog.tsx
+16
-9
No files found.
web/default/src/features/pricing/components/dynamic-pricing-breakdown.tsx
View file @
fc377dae
...
...
@@ -176,7 +176,7 @@ export function DynamicPricingBreakdown({
if
(
!
hasTiers
&&
!
hasRules
)
{
return
(
<
section
className=
'py-4'
>
<
section
className=
'
min-w-0
py-4'
>
<
div
className=
'mb-3 flex items-center gap-2'
>
<
span
className=
'inline-flex size-6 items-center justify-center rounded-full bg-amber-100 text-amber-700 shadow-sm dark:bg-amber-500/20 dark:text-amber-300'
>
<
TagIcon
className=
'size-3.5'
/>
...
...
@@ -201,7 +201,7 @@ export function DynamicPricingBreakdown({
})
return
(
<
section
className=
'py-4'
>
<
section
className=
'
min-w-0
py-4'
>
<
div
className=
'mb-4 flex items-start gap-2'
>
<
span
className=
'mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-amber-100 text-amber-700 shadow-sm dark:bg-amber-500/20 dark:text-amber-300'
>
<
TagIcon
className=
'size-3.5'
/>
...
...
@@ -221,7 +221,7 @@ export function DynamicPricingBreakdown({
<
div
className=
'text-foreground mb-2 text-sm font-semibold'
>
{
t
(
'Tiered price table'
)
}
</
div
>
<
div
className=
'-mx-4
sm:mx-0
'
>
<
div
className=
'-mx-4
max-w-[calc(100%+2rem)] overflow-x-auto sm:mx-0 sm:max-w-full
'
>
<
Table
className=
'text-sm'
>
<
TableHeader
>
<
TableRow
className=
'hover:bg-transparent'
>
...
...
web/default/src/features/profile/components/available-models-card.tsx
deleted
100644 → 0
View file @
df14a0bf
import
{
useMemo
,
useState
}
from
'react'
import
{
useQuery
}
from
'@tanstack/react-query'
import
{
ChevronDown
,
ChevronUp
,
Copy
,
Settings
}
from
'lucide-react'
import
{
useTranslation
}
from
'react-i18next'
import
{
toast
}
from
'sonner'
import
{
api
}
from
'@/lib/api'
import
{
Button
}
from
'@/components/ui/button'
import
{
Card
,
CardContent
,
CardDescription
,
CardHeader
,
CardTitle
,
}
from
'@/components/ui/card'
import
{
Skeleton
}
from
'@/components/ui/skeleton'
import
{
Tabs
,
TabsList
,
TabsTrigger
}
from
'@/components/ui/tabs'
import
{
StatusBadge
}
from
'@/components/status-badge'
const
MODEL_CATEGORIES
=
[
{
key
:
'all'
,
label
:
'All'
,
filter
:
()
=>
true
},
{
key
:
'gpt'
,
label
:
'GPT'
,
filter
:
(
m
:
string
)
=>
/^
(
gpt|o
[
0-9
]
|chatgpt
)
/i
.
test
(
m
),
},
{
key
:
'claude'
,
label
:
'Claude'
,
filter
:
(
m
:
string
)
=>
/claude/i
.
test
(
m
)
},
{
key
:
'gemini'
,
label
:
'Gemini'
,
filter
:
(
m
:
string
)
=>
/gemini|gemma/i
.
test
(
m
),
},
{
key
:
'llama'
,
label
:
'Llama'
,
filter
:
(
m
:
string
)
=>
/llama/i
.
test
(
m
)
},
{
key
:
'mistral'
,
label
:
'Mistral'
,
filter
:
(
m
:
string
)
=>
/mistral|mixtral/i
.
test
(
m
),
},
{
key
:
'deepseek'
,
label
:
'DeepSeek'
,
filter
:
(
m
:
string
)
=>
/deepseek/i
.
test
(
m
),
},
{
key
:
'qwen'
,
label
:
'Qwen'
,
filter
:
(
m
:
string
)
=>
/qwen/i
.
test
(
m
)
},
{
key
:
'embedding'
,
label
:
'Embedding'
,
filter
:
(
m
:
string
)
=>
/embed/i
.
test
(
m
),
},
{
key
:
'image'
,
label
:
'Image'
,
filter
:
(
m
:
string
)
=>
/dall-e|stable-diffusion|midjourney|sd
[
x3
]
|flux|imagen/i
.
test
(
m
),
},
{
key
:
'tts'
,
label
:
'TTS'
,
filter
:
(
m
:
string
)
=>
/tts|whisper|speech/i
.
test
(
m
),
},
]
as
const
const
MODELS_DISPLAY_COUNT
=
25
export
function
AvailableModelsCard
()
{
const
{
t
}
=
useTranslation
()
const
[
activeCategory
,
setActiveCategory
]
=
useState
(
'all'
)
const
[
isExpanded
,
setIsExpanded
]
=
useState
(()
=>
{
try
{
return
JSON
.
parse
(
localStorage
.
getItem
(
'modelsExpanded'
)
??
'false'
)
}
catch
{
return
false
}
})
const
{
data
:
models
=
[],
isLoading
}
=
useQuery
({
queryKey
:
[
'user-available-models'
],
queryFn
:
async
()
=>
{
const
res
=
await
api
.
get
(
'/api/user/models'
)
if
(
!
res
.
data
.
success
||
!
Array
.
isArray
(
res
.
data
.
data
))
return
[]
return
res
.
data
.
data
as
string
[]
},
staleTime
:
5
*
60
*
1000
,
})
const
toggleExpand
=
(
val
:
boolean
)
=>
{
setIsExpanded
(
val
)
localStorage
.
setItem
(
'modelsExpanded'
,
JSON
.
stringify
(
val
))
}
const
copyModel
=
(
model
:
string
)
=>
{
navigator
.
clipboard
.
writeText
(
model
)
toast
.
success
(
t
(
'Copied: {{model}}'
,
{
model
}))
}
const
categoriesWithCounts
=
useMemo
(
()
=>
MODEL_CATEGORIES
.
map
((
cat
)
=>
({
...
cat
,
count
:
cat
.
key
===
'all'
?
models
.
length
:
models
.
filter
(
cat
.
filter
).
length
,
})).
filter
((
cat
)
=>
cat
.
key
===
'all'
||
cat
.
count
>
0
),
[
models
]
)
const
filteredModels
=
useMemo
(()
=>
{
const
cat
=
MODEL_CATEGORIES
.
find
((
c
)
=>
c
.
key
===
activeCategory
)
if
(
!
cat
||
cat
.
key
===
'all'
)
return
models
return
models
.
filter
(
cat
.
filter
)
},
[
models
,
activeCategory
])
if
(
isLoading
)
{
return
(
<
Card
>
<
CardHeader
>
<
CardTitle
className=
'flex items-center gap-2'
>
<
Settings
className=
'h-4 w-4'
/>
{
t
(
'Available Models'
)
}
</
CardTitle
>
<
CardDescription
>
{
t
(
'View all currently available models'
)
}
</
CardDescription
>
</
CardHeader
>
<
CardContent
>
<
div
className=
'flex flex-wrap gap-2'
>
{
Array
.
from
({
length
:
12
}).
map
((
_
,
i
)
=>
(
<
Skeleton
key=
{
i
}
className=
'h-7 w-24 rounded-full'
/>
))
}
</
div
>
</
CardContent
>
</
Card
>
)
}
if
(
models
.
length
===
0
)
{
return
(
<
Card
>
<
CardHeader
>
<
CardTitle
className=
'flex items-center gap-2'
>
<
Settings
className=
'h-4 w-4'
/>
{
t
(
'Available Models'
)
}
</
CardTitle
>
</
CardHeader
>
<
CardContent
>
<
p
className=
'text-muted-foreground text-sm'
>
{
t
(
'No available models'
)
}
</
p
>
</
CardContent
>
</
Card
>
)
}
const
needsExpand
=
filteredModels
.
length
>
MODELS_DISPLAY_COUNT
const
displayModels
=
needsExpand
&&
!
isExpanded
?
filteredModels
.
slice
(
0
,
MODELS_DISPLAY_COUNT
)
:
filteredModels
return
(
<
Card
>
<
CardHeader
>
<
CardTitle
className=
'flex items-center gap-2'
>
<
Settings
className=
'h-4 w-4'
/>
{
t
(
'Available Models'
)
}
</
CardTitle
>
<
CardDescription
>
{
t
(
'View all currently available models'
)
}
·
{
models
.
length
}{
' '
}
{
t
(
'models'
)
}
</
CardDescription
>
</
CardHeader
>
<
CardContent
className=
'space-y-4'
>
<
Tabs
value=
{
activeCategory
}
onValueChange=
{
setActiveCategory
}
>
<
TabsList
className=
'h-auto flex-wrap'
>
{
categoriesWithCounts
.
map
((
cat
)
=>
(
<
TabsTrigger
key=
{
cat
.
key
}
value=
{
cat
.
key
}
className=
'text-xs'
>
{
cat
.
label
}
<
StatusBadge
label=
{
String
(
cat
.
count
)
}
variant=
{
activeCategory
===
cat
.
key
?
'info'
:
'neutral'
}
className=
'ml-1'
copyable=
{
false
}
/>
</
TabsTrigger
>
))
}
</
TabsList
>
</
Tabs
>
<
div
className=
'flex flex-wrap gap-1.5'
>
{
displayModels
.
map
((
model
)
=>
(
<
StatusBadge
key=
{
model
}
variant=
'neutral'
className=
'cursor-pointer font-normal transition-opacity hover:opacity-70'
onClick=
{
()
=>
copyModel
(
model
)
}
copyable=
{
false
}
>
<
Copy
className=
'h-2.5 w-2.5 opacity-50'
/>
{
model
}
</
StatusBadge
>
))
}
{
needsExpand
&&
!
isExpanded
&&
(
<
Button
variant=
'ghost'
size=
'sm'
className=
'h-6 gap-1 text-xs'
onClick=
{
()
=>
toggleExpand
(
true
)
}
>
<
ChevronDown
className=
'h-3 w-3'
/>
{
t
(
'More'
)
}
{
filteredModels
.
length
-
MODELS_DISPLAY_COUNT
}{
' '
}
{
t
(
'models'
)
}
</
Button
>
)
}
{
needsExpand
&&
isExpanded
&&
(
<
Button
variant=
'ghost'
size=
'sm'
className=
'h-6 gap-1 text-xs'
onClick=
{
()
=>
toggleExpand
(
false
)
}
>
<
ChevronUp
className=
'h-3 w-3'
/>
{
t
(
'Collapse'
)
}
</
Button
>
)
}
</
div
>
{
filteredModels
.
length
===
0
&&
(
<
p
className=
'text-muted-foreground py-4 text-center text-sm'
>
{
t
(
'No models available in this category'
)
}
</
p
>
)
}
</
CardContent
>
</
Card
>
)
}
web/default/src/features/profile/index.tsx
View file @
fc377dae
...
...
@@ -5,7 +5,6 @@ import {
CardStaggerContainer
,
CardStaggerItem
,
}
from
'@/components/page-transition'
import
{
AvailableModelsCard
}
from
'./components/available-models-card'
import
{
CheckinCalendarCard
}
from
'./components/checkin-calendar-card'
import
{
PasskeyCard
}
from
'./components/passkey-card'
import
{
ProfileHeader
}
from
'./components/profile-header'
...
...
@@ -38,10 +37,6 @@ export function Profile() {
</
CardStaggerItem
>
<
CardStaggerItem
>
<
AvailableModelsCard
/>
</
CardStaggerItem
>
<
CardStaggerItem
>
<
div
className=
'grid gap-6 lg:grid-cols-2 lg:items-start'
>
<
div
className=
'space-y-6'
>
<
ProfileSecurityCard
profile=
{
profile
}
loading=
{
loading
}
/>
...
...
web/default/src/features/usage-logs/components/columns/common-logs-columns.tsx
View file @
fc377dae
This diff is collapsed.
Click to expand it.
web/default/src/features/usage-logs/components/dialogs/details-dialog.tsx
View file @
fc377dae
...
...
@@ -175,7 +175,7 @@ function BillingBreakdown(props: {
})
}
if
(
!
tieredSummary
&&
isClaude
)
{
if
(
!
tieredSummary
&&
isClaude
&&
hasAnyCacheTokens
(
other
)
)
{
if
(
other
.
cache_ratio
!=
null
&&
other
.
cache_ratio
!==
1
)
{
rows
.
push
({
label
:
t
(
'Cache Read'
),
...
...
@@ -376,6 +376,11 @@ export function DetailsDialog(props: DetailsDialogProps) {
const
isTopup
=
props
.
log
.
type
===
1
const
isManage
=
props
.
log
.
type
===
3
const
isSubscription
=
other
?.
billing_source
===
'subscription'
const
isTieredBilling
=
isConsume
&&
!
isViolation
&&
other
?.
billing_mode
===
'tiered_expr'
&&
!!
other
?.
expr_b64
const
hasAudioTokens
=
other
?.
ws
||
other
?.
audio
const
showTiming
=
isTimingLogType
(
props
.
log
.
type
)
const
showAdminIp
=
...
...
@@ -446,7 +451,12 @@ export function DetailsDialog(props: DetailsDialogProps) {
return
(
<
Dialog
open=
{
props
.
open
}
onOpenChange=
{
props
.
onOpenChange
}
>
<
DialogContent
className=
'sm:max-w-lg'
>
<
DialogContent
className=
{
cn
(
'min-w-0'
,
isTieredBilling
?
'sm:max-w-4xl lg:max-w-5xl'
:
'sm:max-w-lg'
)
}
>
<
DialogHeader
>
<
DialogTitle
className=
'flex items-center gap-2 text-base'
>
{
t
(
'Log Details'
)
}
...
...
@@ -462,8 +472,8 @@ export function DetailsDialog(props: DetailsDialogProps) {
</
DialogDescription
>
</
DialogHeader
>
<
ScrollArea
className=
'max-h-[70vh] pr-4'
>
<
div
className=
'space-y-3 py-1'
>
<
ScrollArea
className=
'max-h-[70vh]
min-w-0
pr-4'
>
<
div
className=
'
min-w-0
space-y-3 py-1'
>
{
/* Overview section - key identifiers */
}
<
div
className=
'space-y-1.5'
>
{
props
.
log
.
request_id
&&
(
...
...
@@ -797,11 +807,8 @@ export function DetailsDialog(props: DetailsDialogProps) {
)
}
{
/* Tiered pricing breakdown (when billing_mode is tiered_expr) */
}
{
isConsume
&&
!
isViolation
&&
other
?.
billing_mode
===
'tiered_expr'
&&
other
?.
expr_b64
&&
(
<
div
className=
'bg-muted/30 rounded-md border px-3'
>
{
isTieredBilling
&&
other
?.
expr_b64
&&
(
<
div
className=
'bg-muted/30 min-w-0 rounded-md border px-3'
>
<
DynamicPricingBreakdown
billingExpr=
{
decodeBillingExprB64
(
other
.
expr_b64
)
}
matchedTierLabel=
{
other
.
matched_tier
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment