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
dc245ae7
authored
May 26, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(web): improve channel and usage log UI
Fixes #5121
parent
f8add4ca
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
177 additions
and
116 deletions
+177
-116
web/default/src/components/status-badge.tsx
+1
-1
web/default/src/components/ui/select.tsx
+31
-24
web/default/src/components/ui/sidebar.tsx
+13
-10
web/default/src/features/channels/components/channels-columns.tsx
+23
-13
web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
+5
-13
web/default/src/features/dashboard/components/overview/overview-dashboard.tsx
+50
-35
web/default/src/features/usage-logs/components/columns/column-helpers.tsx
+1
-1
web/default/src/features/usage-logs/components/columns/common-logs-columns.tsx
+29
-11
web/default/src/features/usage-logs/components/columns/drawing-logs-columns.tsx
+2
-2
web/default/src/features/usage-logs/components/columns/task-logs-columns.tsx
+3
-2
web/default/src/features/usage-logs/components/model-badge.tsx
+3
-3
web/default/src/features/usage-logs/components/usage-logs-mobile-card.tsx
+0
-0
web/default/src/features/usage-logs/components/usage-logs-table.tsx
+8
-0
web/default/src/i18n/locales/en.json
+1
-0
web/default/src/i18n/locales/fr.json
+1
-0
web/default/src/i18n/locales/ja.json
+1
-0
web/default/src/i18n/locales/ru.json
+1
-0
web/default/src/i18n/locales/vi.json
+1
-0
web/default/src/i18n/locales/zh.json
+1
-0
web/default/src/i18n/static-keys.ts
+1
-0
web/default/src/lib/currency.ts
+1
-1
No files found.
web/default/src/components/status-badge.tsx
View file @
dc245ae7
...
@@ -131,7 +131,7 @@ export function StatusBadge({
...
@@ -131,7 +131,7 @@ export function StatusBadge({
return
(
return
(
<
span
<
span
className=
{
cn
(
className=
{
cn
(
'inline-flex w-fit max-w-full shrink-0 items-center rounded-
ful
l font-medium tracking-normal whitespace-nowrap transition-colors'
,
'inline-flex w-fit max-w-full shrink-0 items-center rounded-
4x
l font-medium tracking-normal whitespace-nowrap transition-colors'
,
sizeMap
[
size
??
'sm'
],
sizeMap
[
size
??
'sm'
],
textColorMap
[
computedVariant
],
textColorMap
[
computedVariant
],
pulse
&&
'animate-pulse'
,
pulse
&&
'animate-pulse'
,
...
...
web/default/src/components/ui/select.tsx
View file @
dc245ae7
...
@@ -19,6 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
...
@@ -19,6 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
'use client'
'use client'
import
*
as
React
from
'react'
import
*
as
React
from
'react'
import
{
useMediaQuery
}
from
'@/hooks'
import
{
Select
as
SelectPrimitive
}
from
'@base-ui/react/select'
import
{
Select
as
SelectPrimitive
}
from
'@base-ui/react/select'
import
{
import
{
UnfoldMoreIcon
,
UnfoldMoreIcon
,
...
@@ -97,32 +98,38 @@ function SelectContent({
...
@@ -97,32 +98,38 @@ function SelectContent({
SelectPrimitive
.
Positioner
.
Props
,
SelectPrimitive
.
Positioner
.
Props
,
'align'
|
'alignOffset'
|
'side'
|
'sideOffset'
|
'alignItemWithTrigger'
'align'
|
'alignOffset'
|
'side'
|
'sideOffset'
|
'alignItemWithTrigger'
>
)
{
>
)
{
return
(
const
isMobile
=
useMediaQuery
(
'(max-width: 640px)'
)
<
SelectPrimitive
.
Portal
>
<
SelectPrimitive
.
Positioner
const
content
=
(
side=
{
side
}
<
SelectPrimitive
.
Positioner
sideOffset=
{
sideOffset
}
side=
{
side
}
align=
{
align
}
sideOffset=
{
sideOffset
}
alignOffset=
{
alignOffset
}
align=
{
align
}
alignItemWithTrigger=
{
alignItemWithTrigger
}
alignOffset=
{
alignOffset
}
className=
'isolate z-50'
alignItemWithTrigger=
{
alignItemWithTrigger
}
className=
'isolate z-50'
>
<
SelectPrimitive
.
Popup
data
-
slot=
'select-content'
data
-
align
-
trigger=
{
alignItemWithTrigger
}
className=
{
cn
(
'bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none'
,
className
)
}
{
...
props
}
>
>
<
SelectPrimitive
.
Popup
<
SelectScrollUpButton
/>
data
-
slot=
'select-content'
<
SelectPrimitive
.
List
>
{
children
}
</
SelectPrimitive
.
List
>
data
-
align
-
trigger=
{
alignItemWithTrigger
}
<
SelectScrollDownButton
/>
className=
{
cn
(
</
SelectPrimitive
.
Popup
>
'bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none'
,
</
SelectPrimitive
.
Positioner
>
className
)
}
{
...
props
}
>
<
SelectScrollUpButton
/>
<
SelectPrimitive
.
List
>
{
children
}
</
SelectPrimitive
.
List
>
<
SelectScrollDownButton
/>
</
SelectPrimitive
.
Popup
>
</
SelectPrimitive
.
Positioner
>
</
SelectPrimitive
.
Portal
>
)
)
if
(
isMobile
)
{
return
content
}
return
<
SelectPrimitive
.
Portal
>
{
content
}
</
SelectPrimitive
.
Portal
>
}
}
function
SelectLabel
({
function
SelectLabel
({
...
...
web/default/src/components/ui/sidebar.tsx
View file @
dc245ae7
...
@@ -40,6 +40,7 @@ import { Skeleton } from '@/components/ui/skeleton'
...
@@ -40,6 +40,7 @@ import { Skeleton } from '@/components/ui/skeleton'
import
{
import
{
Tooltip
,
Tooltip
,
TooltipContent
,
TooltipContent
,
TooltipProvider
,
TooltipTrigger
,
TooltipTrigger
,
}
from
'@/components/ui/tooltip'
}
from
'@/components/ui/tooltip'
...
@@ -418,7 +419,7 @@ function SidebarGroupLabel({
...
@@ -418,7 +419,7 @@ function SidebarGroupLabel({
props
:
mergeProps
<
'div'
>
(
props
:
mergeProps
<
'div'
>
(
{
{
className
:
cn
(
className
:
cn
(
'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0'
,
'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:
pointer-events-none group-data-[collapsible=icon]:
-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0'
,
className
className
),
),
},
},
...
@@ -556,15 +557,17 @@ function SidebarMenuButton({
...
@@ -556,15 +557,17 @@ function SidebarMenuButton({
}
}
return
(
return
(
<
Tooltip
>
<
TooltipProvider
delay=
{
0
}
>
{
comp
}
<
Tooltip
>
<
TooltipContent
{
comp
}
side=
'right'
<
TooltipContent
align=
'center'
side=
'right'
hidden=
{
state
!==
'collapsed'
||
isMobile
}
align=
'center'
{
...
tooltip
}
hidden=
{
state
!==
'collapsed'
||
isMobile
}
/>
{
...
tooltip
}
</
Tooltip
>
/>
</
Tooltip
>
</
TooltipProvider
>
)
)
}
}
...
...
web/default/src/features/channels/components/channels-columns.tsx
View file @
dc245ae7
...
@@ -26,6 +26,7 @@ import {
...
@@ -26,6 +26,7 @@ import {
ChevronRight
,
ChevronRight
,
ListOrdered
,
ListOrdered
,
Shuffle
,
Shuffle
,
SlidersHorizontal
,
}
from
'lucide-react'
}
from
'lucide-react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
toast
}
from
'sonner'
import
{
toast
}
from
'sonner'
...
@@ -301,15 +302,18 @@ function BalanceCell({ channel }: { channel: Channel }) {
...
@@ -301,15 +302,18 @@ function BalanceCell({ channel }: { channel: Channel }) {
const
usedDisplay
=
withSuffix
(
formatQuotaValue
(
usedQuota
))
const
usedDisplay
=
withSuffix
(
formatQuotaValue
(
usedQuota
))
const
remainingDisplay
=
withSuffix
(
formatBalance
(
balance
))
const
remainingDisplay
=
withSuffix
(
formatBalance
(
balance
))
const
usedLabel
=
`
${
t
(
'Used:'
)}
${
usedDisplay
}
`
const
remainingLabel
=
`
${
t
(
'Remaining:'
)}
${
remainingDisplay
}
`
// Tag row: only show cumulative used quota
// Tag row: only show cumulative used quota
if
(
isTagRow
)
{
if
(
isTagRow
)
{
return
(
return
(
<
StatusBadge
<
StatusBadge
label=
{
`Used: ${usedDisplay}`
}
label=
{
usedLabel
}
variant=
'neutral'
variant=
'neutral'
size=
'sm'
size=
'sm'
copyable=
{
false
}
copyable=
{
false
}
showDot=
{
false
}
/>
/>
)
)
}
}
...
@@ -354,14 +358,13 @@ function BalanceCell({ channel }: { channel: Channel }) {
...
@@ -354,14 +358,13 @@ function BalanceCell({ channel }: { channel: Channel }) {
variant=
'neutral'
variant=
'neutral'
size=
'sm'
size=
'sm'
copyable=
{
false
}
copyable=
{
false
}
showDot=
{
false
}
className=
'cursor-help'
className=
'cursor-help'
/>
/>
}
}
/>
/>
<
TooltipContent
>
<
TooltipContent
>
<
p
>
<
p
>
{
usedLabel
}
</
p
>
{
t
(
'Used:'
)
}
{
usedDisplay
}
</
p
>
</
TooltipContent
>
</
TooltipContent
>
</
Tooltip
>
</
Tooltip
>
<
Tooltip
>
<
Tooltip
>
...
@@ -384,6 +387,7 @@ function BalanceCell({ channel }: { channel: Channel }) {
...
@@ -384,6 +387,7 @@ function BalanceCell({ channel }: { channel: Channel }) {
}
}
size=
'sm'
size=
'sm'
copyable=
{
false
}
copyable=
{
false
}
showDot=
{
false
}
className=
'cursor-pointer'
className=
'cursor-pointer'
onClick=
{
handleClickUpdate
}
onClick=
{
handleClickUpdate
}
/>
/>
...
@@ -393,7 +397,7 @@ function BalanceCell({ channel }: { channel: Channel }) {
...
@@ -393,7 +397,7 @@ function BalanceCell({ channel }: { channel: Channel }) {
<
p
>
<
p
>
{
channel
.
type
===
57
{
channel
.
type
===
57
?
t
(
'Click to view Codex usage'
)
?
t
(
'Click to view Codex usage'
)
:
`${t('Remaining:')} ${remainingDisplay}`
}
:
remainingLabel
}
</
p
>
</
p
>
{
channel
.
type
!==
57
&&
<
p
>
{
t
(
'Click to update balance'
)
}
</
p
>
}
{
channel
.
type
!==
57
&&
<
p
>
{
t
(
'Click to update balance'
)
}
</
p
>
}
</
TooltipContent
>
</
TooltipContent
>
...
@@ -494,7 +498,6 @@ export function useChannelsColumns(): ColumnDef<Channel>[] {
...
@@ -494,7 +498,6 @@ export function useChannelsColumns(): ColumnDef<Channel>[] {
const
isTagRow
=
isTagAggregateRow
(
row
.
original
)
const
isTagRow
=
isTagAggregateRow
(
row
.
original
)
const
name
=
row
.
getValue
(
'name'
)
as
string
const
name
=
row
.
getValue
(
'name'
)
as
string
const
channel
=
row
.
original
const
channel
=
row
.
original
const
isMultiKey
=
isMultiKeyChannel
(
channel
)
// Tag row with expand/collapse
// Tag row with expand/collapse
if
(
isTagRow
)
{
if
(
isTagRow
)
{
...
@@ -531,6 +534,7 @@ export function useChannelsColumns(): ColumnDef<Channel>[] {
...
@@ -531,6 +534,7 @@ export function useChannelsColumns(): ColumnDef<Channel>[] {
// Regular channel row
// Regular channel row
const
settings
=
parseChannelSettings
(
channel
.
setting
)
const
settings
=
parseChannelSettings
(
channel
.
setting
)
const
isPassThrough
=
settings
.
pass_through_body_enabled
===
true
const
isPassThrough
=
settings
.
pass_through_body_enabled
===
true
const
hasParamOverride
=
Boolean
(
channel
.
param_override
?.
trim
())
return
(
return
(
<
div
className=
'flex items-center gap-2'
>
<
div
className=
'flex items-center gap-2'
>
...
@@ -557,13 +561,19 @@ export function useChannelsColumns(): ColumnDef<Channel>[] {
...
@@ -557,13 +561,19 @@ export function useChannelsColumns(): ColumnDef<Channel>[] {
</
Tooltip
>
</
Tooltip
>
</
TooltipProvider
>
</
TooltipProvider
>
)
}
)
}
{
isMultiKey
&&
(
{
hasParamOverride
&&
(
<
StatusBadge
<
TooltipProvider
delay=
{
100
}
>
label=
{
`${channel.channel_info.multi_key_size} keys`
}
<
Tooltip
>
variant=
'purple'
<
TooltipTrigger
size=
'sm'
render=
{
copyable=
{
false
}
<
SlidersHorizontal
className=
'text-info h-3.5 w-3.5 flex-shrink-0'
/>
/>
}
></
TooltipTrigger
>
<
TooltipContent
side=
'top'
>
{
t
(
'Override request parameters'
)
}
</
TooltipContent
>
</
Tooltip
>
</
TooltipProvider
>
)
}
)
}
<
UpstreamUpdateTags
channel=
{
channel
}
/>
<
UpstreamUpdateTags
channel=
{
channel
}
/>
</
div
>
</
div
>
...
...
web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
View file @
dc245ae7
...
@@ -2754,23 +2754,15 @@ export function ChannelMutateDrawer({
...
@@ -2754,23 +2754,15 @@ export function ChannelMutateDrawer({
</
div
>
</
div
>
</
div
>
</
div
>
<
FormControl
>
<
FormControl
>
<
JsonEditor
<
Textarea
value=
{
field
.
value
||
''
}
value=
{
field
.
value
||
''
}
onChange=
{
field
.
onChange
}
onChange=
{
field
.
onChange
}
disabled=
{
isSubmitting
}
disabled=
{
isSubmitting
}
keyPlaceholder=
'temperature'
rows=
{
8
}
valuePlaceholder=
'0.7'
placeholder=
{
t
(
keyLabel=
'Parameter'
'Override request parameters. Cannot override stream parameter.'
valueLabel=
'Value'
emptyMessage=
{
t
(
'No parameter overrides configured.'
)
}
)
}
template=
{
{
className=
'max-h-72 min-h-40 resize-y overflow-auto font-mono text-xs'
temperature
:
0.7
,
max_tokens
:
2000
,
top_p
:
1
,
}
}
valueType=
'any'
/>
/>
</
FormControl
>
</
FormControl
>
<
FormMessage
/>
<
FormMessage
/>
...
...
web/default/src/features/dashboard/components/overview/overview-dashboard.tsx
View file @
dc245ae7
...
@@ -26,6 +26,7 @@ import {
...
@@ -26,6 +26,7 @@ import {
ChevronDown
,
ChevronDown
,
ChevronUp
,
ChevronUp
,
Circle
,
Circle
,
Copy
,
CreditCard
,
CreditCard
,
FileText
,
FileText
,
KeyRound
,
KeyRound
,
...
@@ -38,13 +39,14 @@ import {
...
@@ -38,13 +39,14 @@ import {
}
from
'lucide-react'
}
from
'lucide-react'
import
{
motion
,
useReducedMotion
}
from
'motion/react'
import
{
motion
,
useReducedMotion
}
from
'motion/react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
toast
}
from
'sonner'
import
{
useAuthStore
}
from
'@/stores/auth-store'
import
{
useAuthStore
}
from
'@/stores/auth-store'
import
{
getUserModels
}
from
'@/lib/api'
import
{
getUserModels
}
from
'@/lib/api'
import
{
MOTION_TRANSITION
}
from
'@/lib/motion'
import
{
MOTION_TRANSITION
}
from
'@/lib/motion'
import
{
ROLE
}
from
'@/lib/roles'
import
{
ROLE
}
from
'@/lib/roles'
import
{
cn
}
from
'@/lib/utils'
import
{
cn
}
from
'@/lib/utils'
import
{
useCopyToClipboard
}
from
'@/hooks/use-copy-to-clipboard'
import
{
Button
}
from
'@/components/ui/button'
import
{
Button
}
from
'@/components/ui/button'
import
{
CopyButton
}
from
'@/components/copy-button'
import
{
import
{
CardStaggerContainer
,
CardStaggerContainer
,
CardStaggerItem
,
CardStaggerItem
,
...
@@ -104,8 +106,8 @@ interface RequestExample {
...
@@ -104,8 +106,8 @@ interface RequestExample {
endpoint
:
string
endpoint
:
string
model
:
string
model
:
string
keyName
:
string
keyName
:
string
keyId
?:
number
displayKey
:
string
displayKey
:
string
curl
:
string
ready
:
boolean
ready
:
boolean
}
}
...
@@ -179,7 +181,7 @@ function SetupGuideBackdrop(props: { compact?: boolean }) {
...
@@ -179,7 +181,7 @@ function SetupGuideBackdrop(props: { compact?: boolean }) {
<>
<>
<
div
<
div
className=
{
cn
(
className=
{
cn
(
'pointer-events-none absolute inset-0 bg-[
linear-gradient(112deg,oklch(0.97_0.04_250/.92)_0%,oklch(0.95_0.08_315/.82)_38%,oklch(0.96_0.12_92/.78)_74%,oklch(0.94_0.1_132/.62)_100%)] dark:opacity-2
5'
,
'pointer-events-none absolute inset-0 bg-[
radial-gradient(ellipse_48%_120%_at_78%_0%,color-mix(in_oklch,var(--primary)_8%,transparent)_0%,transparent_62%),linear-gradient(112deg,color-mix(in_oklch,var(--card)_98%,var(--primary)_2%)_0%,color-mix(in_oklch,var(--card)_94%,var(--muted)_6%)_48%,color-mix(in_oklch,var(--background)_92%,var(--accent)_8%)_100%)] dark:opacity-6
5'
,
props
.
compact
props
.
compact
?
'[mask-image:linear-gradient(90deg,black_0%,black_48%,transparent_74%)] opacity-55'
?
'[mask-image:linear-gradient(90deg,black_0%,black_48%,transparent_74%)] opacity-55'
:
'opacity-85'
:
'opacity-85'
...
@@ -188,7 +190,7 @@ function SetupGuideBackdrop(props: { compact?: boolean }) {
...
@@ -188,7 +190,7 @@ function SetupGuideBackdrop(props: { compact?: boolean }) {
/>
/>
<
div
<
div
className=
{
cn
(
className=
{
cn
(
'
pointer-events-none absolute inset-y-0 right-0 hidden overflow-hidden font-mono text-lime-100/75 sm:block dark:text-lime-200/25
'
,
'
text-foreground/5 pointer-events-none absolute inset-y-0 right-0 hidden overflow-hidden font-mono sm:block dark:text-foreground/8
'
,
props
.
compact
?
'w-1/2 opacity-45'
:
'w-[58%] opacity-75'
props
.
compact
?
'w-1/2 opacity-45'
:
'w-[58%] opacity-75'
)
}
)
}
aria
-
hidden=
'true'
aria
-
hidden=
'true'
...
@@ -275,12 +277,41 @@ function RequestPreview(props: {
...
@@ -275,12 +277,41 @@ function RequestPreview(props: {
})
{
})
{
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
const
shouldReduceMotion
=
useReducedMotion
()
const
shouldReduceMotion
=
useReducedMotion
()
const
previewLines
=
props
.
example
.
curl
.
split
(
'\n'
).
map
((
line
)
=>
{
const
[
isCopying
,
setIsCopying
]
=
useState
(
false
)
if
(
line
.
includes
(
'Authorization: Bearer'
))
{
const
{
copyToClipboard
}
=
useCopyToClipboard
({
notify
:
false
})
return
` -H "Authorization: Bearer
${
props
.
example
.
displayKey
}
" \\`
const
previewCurl
=
buildCurlCommand
({
}
endpoint
:
props
.
example
.
endpoint
,
return
line
apiKey
:
props
.
example
.
displayKey
,
model
:
props
.
example
.
model
,
})
})
const
previewLines
=
previewCurl
.
split
(
'\n'
)
const
handleCopyRequest
=
async
()
=>
{
if
(
!
props
.
example
.
keyId
||
isCopying
)
return
setIsCopying
(
true
)
try
{
const
result
=
await
fetchTokenKey
(
props
.
example
.
keyId
)
const
key
=
result
.
success
&&
result
.
data
?.
key
?
result
.
data
.
key
:
''
if
(
!
key
)
{
toast
.
error
(
result
.
message
||
t
(
'Failed to copy to clipboard'
))
return
}
const
realCurl
=
buildCurlCommand
({
endpoint
:
props
.
example
.
endpoint
,
apiKey
:
`sk-
${
key
}
`
,
model
:
props
.
example
.
model
,
})
const
copied
=
await
copyToClipboard
(
realCurl
)
if
(
copied
)
{
toast
.
success
(
t
(
'Copied to clipboard'
))
}
else
{
toast
.
error
(
t
(
'Failed to copy to clipboard'
))
}
}
finally
{
setIsCopying
(
false
)
}
}
return
(
return
(
<
motion
.
div
<
motion
.
div
...
@@ -315,17 +346,17 @@ function RequestPreview(props: {
...
@@ -315,17 +346,17 @@ function RequestPreview(props: {
</
div
>
</
div
>
</
div
>
</
div
>
{
props
.
example
.
ready
?
(
{
props
.
example
.
ready
?
(
<
CopyButton
<
Button
value=
{
props
.
example
.
curl
}
variant=
'outline'
variant=
'outline'
size=
'sm'
size=
'sm'
className=
'h-7 gap-1.5 px-2 text-xs'
className=
'h-7 gap-1.5 px-2 text-xs'
tooltip=
{
t
(
'Copy ready-to-run curl'
)
}
disabled=
{
isCopying
}
successTooltip=
{
t
(
'Copied!'
)
}
onClick=
{
handleCopyRequest
}
aria
-
label=
{
t
(
'Copy ready-to-run curl'
)
}
aria
-
label=
{
t
(
'Copy ready-to-run curl'
)
}
>
>
{
t
(
'Copy'
)
}
<
Copy
data
-
icon=
'inline-start'
/>
</
CopyButton
>
{
isCopying
?
t
(
'Loading'
)
:
t
(
'Copy'
)
}
</
Button
>
)
:
(
)
:
(
<
Button
size=
'sm'
variant=
'outline'
render=
{
<
Link
to=
'/keys'
/>
}
>
<
Button
size=
'sm'
variant=
'outline'
render=
{
<
Link
to=
'/keys'
/>
}
>
{
t
(
'Create API Key'
)
}
{
t
(
'Create API Key'
)
}
...
@@ -463,17 +494,6 @@ export function OverviewDashboard() {
...
@@ -463,17 +494,6 @@ export function OverviewDashboard() {
[
apiKeysQuery
.
data
]
[
apiKeysQuery
.
data
]
)
)
const
realKeyQuery
=
useQuery
({
queryKey
:
[
'dashboard'
,
'overview'
,
'token-key'
,
preferredKey
?.
id
],
queryFn
:
async
()
=>
{
if
(
!
preferredKey
?.
id
)
return
''
const
result
=
await
fetchTokenKey
(
preferredKey
.
id
)
return
result
.
success
&&
result
.
data
?.
key
?
`sk-
${
result
.
data
.
key
}
`
:
''
},
enabled
:
Boolean
(
preferredKey
?.
id
),
staleTime
:
5
*
60
*
1000
,
})
const
startSteps
=
useMemo
<
StartStep
[]
>
(
const
startSteps
=
useMemo
<
StartStep
[]
>
(
()
=>
[
()
=>
[
{
{
...
@@ -561,23 +581,18 @@ export function OverviewDashboard() {
...
@@ -561,23 +581,18 @@ export function OverviewDashboard() {
const
requestExample
=
useMemo
<
RequestExample
>
(()
=>
{
const
requestExample
=
useMemo
<
RequestExample
>
(()
=>
{
const
endpoint
=
normalizeEndpoint
(
apiInfoItems
[
0
]?.
url
)
const
endpoint
=
normalizeEndpoint
(
apiInfoItems
[
0
]?.
url
)
const
model
=
modelsQuery
.
data
?.[
0
]
??
'gpt-4o-mini'
const
model
=
modelsQuery
.
data
?.[
0
]
??
'gpt-4o-mini'
const
apiKey
=
realKeyQuery
.
data
??
''
const
keyName
=
preferredKey
?.
name
??
t
(
'No API key yet'
)
const
keyName
=
preferredKey
?.
name
??
t
(
'No API key yet'
)
const
ready
=
Boolean
(
apiKey
&&
model
)
const
ready
=
Boolean
(
preferredKey
?.
id
&&
model
)
return
{
return
{
endpoint
,
endpoint
,
model
,
model
,
keyName
,
keyName
,
displayKey
:
formatDisplayKey
(
apiKey
),
keyId
:
preferredKey
?.
id
,
displayKey
:
preferredKey
?
formatDisplayKey
(
`sk-
${
preferredKey
.
key
}
`
)
:
'sk-...'
,
ready
,
ready
,
curl
:
buildCurlCommand
({
endpoint
,
apiKey
:
apiKey
||
'sk-...'
,
model
,
}),
}
}
},
[
apiInfoItems
,
modelsQuery
.
data
,
preferredKey
,
realKeyQuery
.
data
,
t
])
},
[
apiInfoItems
,
modelsQuery
.
data
,
preferredKey
,
t
])
const
completedStepCount
=
startSteps
.
filter
((
step
)
=>
step
.
completed
).
length
const
completedStepCount
=
startSteps
.
filter
((
step
)
=>
step
.
completed
).
length
const
setupComplete
=
completedStepCount
===
startSteps
.
length
const
setupComplete
=
completedStepCount
===
startSteps
.
length
...
...
web/default/src/features/usage-logs/components/columns/column-helpers.tsx
View file @
dc245ae7
...
@@ -150,7 +150,7 @@ export function createDurationColumn<T>(config: {
...
@@ -150,7 +150,7 @@ export function createDurationColumn<T>(config: {
variant=
{
variant
}
variant=
{
variant
}
size=
'sm'
size=
'sm'
copyable=
{
false
}
copyable=
{
false
}
className=
{
cn
(
'font-mono'
,
durationBgMap
[
variant
])
}
className=
{
cn
(
'
rounded-md
font-mono'
,
durationBgMap
[
variant
])
}
/>
/>
)
)
},
},
...
...
web/default/src/features/usage-logs/components/columns/common-logs-columns.tsx
View file @
dc245ae7
...
@@ -90,6 +90,12 @@ function getGroupRatioText(other: LogOtherData | null): string | null {
...
@@ -90,6 +90,12 @@ function getGroupRatioText(other: LogOtherData | null): string | null {
return
null
return
null
}
}
function
splitQuotaDisplay
(
value
:
string
):
{
prefix
:
string
;
amount
:
string
}
{
const
match
=
value
.
match
(
/^
([^
0-9+
\-
.,
\s]
+
)(
.+
)
$/
)
if
(
!
match
)
return
{
prefix
:
''
,
amount
:
value
}
return
{
prefix
:
match
[
1
],
amount
:
match
[
2
]
}
}
function
buildDetailSegments
(
function
buildDetailSegments
(
log
:
UsageLog
,
log
:
UsageLog
,
other
:
LogOtherData
|
null
,
other
:
LogOtherData
|
null
,
...
@@ -277,6 +283,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -277,6 +283,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
variant={config.color as StatusBadgeProps['variant']}
variant={config.color as StatusBadgeProps['variant']}
size='sm'
size='sm'
copyable={false}
copyable={false}
className='!text-xs [&_span]:!text-xs'
/>
/>
</div>
</div>
)
)
...
@@ -295,6 +302,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -295,6 +302,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
columns.push(
columns.push(
{
{
id: 'channel',
id: 'channel',
accessorFn: (row) => row.channel,
header: ({ column }) => (
header: ({ column }) => (
<DataTableColumnHeader column={column} title={t('Channel')} />
<DataTableColumnHeader column={column} title={t('Channel')} />
),
),
...
@@ -395,10 +403,11 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -395,10 +403,11 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
</TooltipProvider>
</TooltipProvider>
)
)
},
},
meta: { label: t('Channel')
, mobileHidden: true
},
meta: { label: t('Channel') },
},
},
{
{
id: 'user',
id: 'user',
accessorFn: (row) => row.username,
header: ({ column }) => (
header: ({ column }) => (
<DataTableColumnHeader column={column} title={t('User')} />
<DataTableColumnHeader column={column} title={t('User')} />
),
),
...
@@ -419,7 +428,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -419,7 +428,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
setUserInfoDialogOpen(true)
setUserInfoDialogOpen(true)
}}
}}
>
>
<Avatar className='ring-border/60 size-6 ring-1'>
<Avatar className='ring-border/60 size-6 ring-1
max-sm:hidden
'>
<AvatarFallback
<AvatarFallback
className={cn(
className={cn(
'text-[11px] font-semibold',
'text-[11px] font-semibold',
...
@@ -451,7 +460,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -451,7 +460,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
</button>
</button>
)
)
},
},
meta: { label: t('User')
, mobileHidden: true
},
meta: { label: t('User') },
}
}
)
)
}
}
...
@@ -555,7 +564,9 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -555,7 +564,9 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
? log.completion_tokens / useTime
? log.completion_tokens / useTime
: null
: null
const timeVariant = getResponseTimeColor(useTime, log.completion_tokens)
const timeVariant = getResponseTimeColor(useTime, log.completion_tokens)
const frtVariant = frt ? getFirstResponseTimeColor(frt / 1000) : 'neutral'
const frtVariant = frt
? getFirstResponseTimeColor(frt / 1000)
: 'neutral'
const timingBgMap: Record<string, string> = {
const timingBgMap: Record<string, string> = {
success:
success:
...
@@ -576,7 +587,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -576,7 +587,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
variant={timeVariant as StatusBadgeProps['variant']}
variant={timeVariant as StatusBadgeProps['variant']}
size='sm'
size='sm'
copyable={false}
copyable={false}
className={cn('font-mono', timingBgMap[timeVariant])}
className={cn('
rounded-md
font-mono', timingBgMap[timeVariant])}
/>
/>
{log.is_stream &&
{log.is_stream &&
(frt != null && frt > 0 ? (
(frt != null && frt > 0 ? (
...
@@ -586,7 +597,10 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -586,7 +597,10 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
size='sm'
size='sm'
showDot={false}
showDot={false}
copyable={false}
copyable={false}
className={cn('font-mono', timingBgMap[frtVariant])}
className={cn(
'rounded-md font-mono',
timingBgMap[frtVariant]
)}
/>
/>
) : (
) : (
<StatusBadge
<StatusBadge
...
@@ -595,7 +609,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -595,7 +609,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
size='sm'
size='sm'
showDot={false}
showDot={false}
copyable={false}
copyable={false}
className={
timingBgMap.neutral
}
className={
cn('rounded-md font-mono', timingBgMap.neutral)
}
/>
/>
))}
))}
</div>
</div>
...
@@ -641,7 +655,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -641,7 +655,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
</div>
</div>
)
)
},
},
meta: { label: t('Timing')
, mobileHidden: true
},
meta: { label: t('Timing') },
},
},
{
{
...
@@ -692,7 +706,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -692,7 +706,7 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
</div>
</div>
)
)
},
},
meta: { label: 'Tokens'
, mobileHidden: true
},
meta: { label: 'Tokens' },
},
},
{
{
...
@@ -734,11 +748,15 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
...
@@ -734,11 +748,15 @@ export function useCommonLogsColumns(isAdmin: boolean): ColumnDef<UsageLog>[] {
}
}
const quotaStr = formatLogQuota(quota)
const quotaStr = formatLogQuota(quota)
const quotaDisplay = splitQuotaDisplay(quotaStr)
return (
return (
<div className='flex flex-col gap-0.5'>
<div className='flex flex-col gap-0.5'>
<span className='border-border/80 bg-muted/60 inline-flex w-fit items-center rounded-md border px-1.5 py-0.5 [font-family:var(--font-body)] font-semibold tabular-nums'>
<span className='border-border/80 bg-muted/60 inline-flex h-6 w-fit items-center rounded-md border px-2 text-sm leading-none [font-family:var(--font-body)] font-semibold tabular-nums'>
{quotaStr}
{quotaDisplay.prefix && (
<span className='mr-1'>{quotaDisplay.prefix}</span>
)}
<span>{quotaDisplay.amount}</span>
</span>
</span>
</div>
</div>
)
)
...
...
web/default/src/features/usage-logs/components/columns/drawing-logs-columns.tsx
View file @
dc245ae7
...
@@ -231,7 +231,7 @@ export function useDrawingLogsColumns(
...
@@ -231,7 +231,7 @@ export function useDrawingLogsColumns(
</>
</>
)
)
},
},
meta
:
{
label
:
t
(
'Image'
)
,
mobileHidden
:
true
},
meta
:
{
label
:
t
(
'Image'
)
},
},
},
{
{
accessorKey
:
'prompt'
,
accessorKey
:
'prompt'
,
...
@@ -268,7 +268,7 @@ export function useDrawingLogsColumns(
...
@@ -268,7 +268,7 @@ export function useDrawingLogsColumns(
</>
</>
)
)
},
},
meta
:
{
label
:
t
(
'Prompt'
)
,
mobileHidden
:
true
},
meta
:
{
label
:
t
(
'Prompt'
)
},
size
:
200
,
size
:
200
,
maxSize
:
220
,
maxSize
:
220
,
},
},
...
...
web/default/src/features/usage-logs/components/columns/task-logs-columns.tsx
View file @
dc245ae7
...
@@ -123,6 +123,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
...
@@ -123,6 +123,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
if
(
isAdmin
)
{
if
(
isAdmin
)
{
columns
.
push
(
createChannelColumn
<
TaskLog
>
({
headerLabel
:
t
(
'Channel'
)
}),
{
columns
.
push
(
createChannelColumn
<
TaskLog
>
({
headerLabel
:
t
(
'Channel'
)
}),
{
id
:
'user'
,
id
:
'user'
,
accessorFn
:
(
row
)
=>
row
.
username
||
row
.
user_id
,
header
:
({
column
})
=>
(
header
:
({
column
})
=>
(
<
DataTableColumnHeader
column=
{
column
}
title=
{
t
(
'User'
)
}
/>
<
DataTableColumnHeader
column=
{
column
}
title=
{
t
(
'User'
)
}
/>
),
),
...
@@ -142,7 +143,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
...
@@ -142,7 +143,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
setUserInfoDialogOpen
(
true
)
setUserInfoDialogOpen
(
true
)
}
}
}
}
>
>
<
Avatar
className=
'ring-border/60 size-6 ring-1'
>
<
Avatar
className=
'ring-border/60 size-6 ring-1
max-sm:hidden
'
>
<
AvatarFallback
<
AvatarFallback
className=
{
cn
(
className=
{
cn
(
'text-[11px] font-semibold'
,
'text-[11px] font-semibold'
,
...
@@ -161,7 +162,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
...
@@ -161,7 +162,7 @@ export function useTaskLogsColumns(isAdmin: boolean): ColumnDef<TaskLog>[] {
</
button
>
</
button
>
)
)
},
},
meta
:
{
label
:
t
(
'User'
)
,
mobileHidden
:
true
},
meta
:
{
label
:
t
(
'User'
)
},
})
})
}
}
...
...
web/default/src/features/usage-logs/components/model-badge.tsx
View file @
dc245ae7
...
@@ -101,12 +101,12 @@ function ModelBadgeContent(props: ModelBadgeProps) {
...
@@ -101,12 +101,12 @@ function ModelBadgeContent(props: ModelBadgeProps) {
showDot=
{
!
provider
}
showDot=
{
!
provider
}
autoColor=
{
provider
?
undefined
:
props
.
modelName
}
autoColor=
{
provider
?
undefined
:
props
.
modelName
}
className=
{
cn
(
className=
{
cn
(
'border-border/60 bg-muted/30 h-
auto min-h-6 gap-1.5 rounded-md border px-2 py-0.5 [font-family:var(--font-body)] break-all whitespace-normal
'
,
'border-border/60 bg-muted/30 h-
6 max-w-full gap-1.5 rounded-md border px-2 [font-family:var(--font-body)]
'
,
provider
&&
'text-foreground'
,
provider
&&
'text-foreground'
,
props
.
className
props
.
className
)
}
)
}
>
>
<
span
className=
'flex min-w-0 items-center gap-1.5'
>
<
span
className=
'flex m
ax-w-full m
in-w-0 items-center gap-1.5'
>
{
provider
&&
(
{
provider
&&
(
<
span
<
span
className=
'flex size-3.5 shrink-0 items-center justify-center'
className=
'flex size-3.5 shrink-0 items-center justify-center'
...
@@ -116,7 +116,7 @@ function ModelBadgeContent(props: ModelBadgeProps) {
...
@@ -116,7 +116,7 @@ function ModelBadgeContent(props: ModelBadgeProps) {
{
getLobeIcon
(
provider
.
icon
,
14
)
}
{
getLobeIcon
(
provider
.
icon
,
14
)
}
</
span
>
</
span
>
)
}
)
}
<
span
>
{
props
.
modelName
}
</
span
>
<
span
className=
'truncate'
>
{
props
.
modelName
}
</
span
>
</
span
>
</
span
>
</
StatusBadge
>
</
StatusBadge
>
)
)
...
...
web/default/src/features/usage-logs/components/usage-logs-mobile-card.tsx
0 → 100644
View file @
dc245ae7
This diff is collapsed.
Click to expand it.
web/default/src/features/usage-logs/components/usage-logs-table.tsx
View file @
dc245ae7
...
@@ -47,6 +47,7 @@ import { fetchLogsByCategory } from '../lib/utils'
...
@@ -47,6 +47,7 @@ import { fetchLogsByCategory } from '../lib/utils'
import
type
{
LogCategory
}
from
'../types'
import
type
{
LogCategory
}
from
'../types'
import
{
CommonLogsFilterBar
}
from
'./common-logs-filter-bar'
import
{
CommonLogsFilterBar
}
from
'./common-logs-filter-bar'
import
{
TaskLogsFilterBar
}
from
'./task-logs-filter-bar'
import
{
TaskLogsFilterBar
}
from
'./task-logs-filter-bar'
import
{
UsageLogsMobileList
}
from
'./usage-logs-mobile-card'
const
route
=
getRouteApi
(
'/_authenticated/usage-logs/$section'
)
const
route
=
getRouteApi
(
'/_authenticated/usage-logs/$section'
)
...
@@ -190,6 +191,13 @@ export function UsageLogsTable({ logCategory }: UsageLogsTableProps) {
...
@@ -190,6 +191,13 @@ export function UsageLogsTable({ logCategory }: UsageLogsTableProps) {
'[&_[data-slot=table]]:text-[13px] [&_[data-slot=table]_td]:text-[13px] [&_[data-slot=table]_td_*]:text-[13px] [&_[data-slot=table]_th]:text-[13px] [&_[data-slot=table]_th_*]:text-[13px]'
'[&_[data-slot=table]]:text-[13px] [&_[data-slot=table]_td]:text-[13px] [&_[data-slot=table]_td_*]:text-[13px] [&_[data-slot=table]_th]:text-[13px] [&_[data-slot=table]_th_*]:text-[13px]'
)
}
)
}
tableHeaderClassName=
'bg-muted/30 sticky top-0 z-10'
tableHeaderClassName=
'bg-muted/30 sticky top-0 z-10'
mobile=
{
<
UsageLogsMobileList
table=
{
table
}
isLoading=
{
isLoadingData
}
logCategory=
{
logCategory
}
/>
}
toolbar=
{
toolbar=
{
isCommon
?
(
isCommon
?
(
<
CommonLogsFilterBar
table=
{
table
}
/>
<
CommonLogsFilterBar
table=
{
table
}
/>
...
...
web/default/src/i18n/locales/en.json
View file @
dc245ae7
...
@@ -2778,6 +2778,7 @@
...
@@ -2778,6 +2778,7 @@
"Override auto-discovered endpoint"
:
"Override auto-discovered endpoint"
,
"Override auto-discovered endpoint"
:
"Override auto-discovered endpoint"
,
"Override request headers"
:
"Override request headers"
,
"Override request headers"
:
"Override request headers"
,
"Override request headers (JSON format)"
:
"Override request headers (JSON format)"
,
"Override request headers (JSON format)"
:
"Override request headers (JSON format)"
,
"Override request parameters"
:
"Override request parameters"
,
"Override request parameters (JSON format)"
:
"Override request parameters (JSON format)"
,
"Override request parameters (JSON format)"
:
"Override request parameters (JSON format)"
,
"Override request parameters. Cannot override"
:
"Override request parameters. Cannot override"
,
"Override request parameters. Cannot override"
:
"Override request parameters. Cannot override"
,
"Override request parameters. Cannot override stream parameter."
:
"Override request parameters. Cannot override stream parameter."
,
"Override request parameters. Cannot override stream parameter."
:
"Override request parameters. Cannot override stream parameter."
,
...
...
web/default/src/i18n/locales/fr.json
View file @
dc245ae7
...
@@ -2778,6 +2778,7 @@
...
@@ -2778,6 +2778,7 @@
"Override auto-discovered endpoint"
:
"Remplacer le point de terminaison auto-découvert"
,
"Override auto-discovered endpoint"
:
"Remplacer le point de terminaison auto-découvert"
,
"Override request headers"
:
"Remplacer les en-têtes de requête"
,
"Override request headers"
:
"Remplacer les en-têtes de requête"
,
"Override request headers (JSON format)"
:
"Surcharge des en-têtes de requête (format JSON)"
,
"Override request headers (JSON format)"
:
"Surcharge des en-têtes de requête (format JSON)"
,
"Override request parameters"
:
"Remplacer les paramètres de requête"
,
"Override request parameters (JSON format)"
:
"Remplacer les paramètres de requête (format JSON)"
,
"Override request parameters (JSON format)"
:
"Remplacer les paramètres de requête (format JSON)"
,
"Override request parameters. Cannot override"
:
"Remplacer les paramètres de requête. Impossible de remplacer"
,
"Override request parameters. Cannot override"
:
"Remplacer les paramètres de requête. Impossible de remplacer"
,
"Override request parameters. Cannot override stream parameter."
:
"Remplace les paramètres de requête. Impossible de remplacer le paramètre stream."
,
"Override request parameters. Cannot override stream parameter."
:
"Remplace les paramètres de requête. Impossible de remplacer le paramètre stream."
,
...
...
web/default/src/i18n/locales/ja.json
View file @
dc245ae7
...
@@ -2778,6 +2778,7 @@
...
@@ -2778,6 +2778,7 @@
"Override auto-discovered endpoint"
:
"自動検出されたエンドポイントを上書きする"
,
"Override auto-discovered endpoint"
:
"自動検出されたエンドポイントを上書きする"
,
"Override request headers"
:
"リクエストヘッダーを上書きする"
,
"Override request headers"
:
"リクエストヘッダーを上書きする"
,
"Override request headers (JSON format)"
:
"リクエストヘッダーのオーバーライド (JSON 形式)"
,
"Override request headers (JSON format)"
:
"リクエストヘッダーのオーバーライド (JSON 形式)"
,
"Override request parameters"
:
"リクエストパラメータを上書き"
,
"Override request parameters (JSON format)"
:
"リクエストパラメータの上書き (JSON形式)"
,
"Override request parameters (JSON format)"
:
"リクエストパラメータの上書き (JSON形式)"
,
"Override request parameters. Cannot override"
:
"リクエストパラメーターを上書きします。上書きできません"
,
"Override request parameters. Cannot override"
:
"リクエストパラメーターを上書きします。上書きできません"
,
"Override request parameters. Cannot override stream parameter."
:
"リクエストパラメータを上書きします。stream パラメータは上書きできません。"
,
"Override request parameters. Cannot override stream parameter."
:
"リクエストパラメータを上書きします。stream パラメータは上書きできません。"
,
...
...
web/default/src/i18n/locales/ru.json
View file @
dc245ae7
...
@@ -2778,6 +2778,7 @@
...
@@ -2778,6 +2778,7 @@
"Override auto-discovered endpoint"
:
"Переопределить автоматически обнаруженную конечную точку"
,
"Override auto-discovered endpoint"
:
"Переопределить автоматически обнаруженную конечную точку"
,
"Override request headers"
:
"Переопределить заголовки запроса"
,
"Override request headers"
:
"Переопределить заголовки запроса"
,
"Override request headers (JSON format)"
:
"Переопределение заголовков запроса (формат JSON)"
,
"Override request headers (JSON format)"
:
"Переопределение заголовков запроса (формат JSON)"
,
"Override request parameters"
:
"Переопределить параметры запроса"
,
"Override request parameters (JSON format)"
:
"Переопределить параметры запроса (формат JSON)"
,
"Override request parameters (JSON format)"
:
"Переопределить параметры запроса (формат JSON)"
,
"Override request parameters. Cannot override"
:
"Переопределить параметры запроса. Невозможно переопределить"
,
"Override request parameters. Cannot override"
:
"Переопределить параметры запроса. Невозможно переопределить"
,
"Override request parameters. Cannot override stream parameter."
:
"Переопределяет параметры запроса. Параметр stream переопределить нельзя."
,
"Override request parameters. Cannot override stream parameter."
:
"Переопределяет параметры запроса. Параметр stream переопределить нельзя."
,
...
...
web/default/src/i18n/locales/vi.json
View file @
dc245ae7
...
@@ -2778,6 +2778,7 @@
...
@@ -2778,6 +2778,7 @@
"Override auto-discovered endpoint"
:
"Ghi đè điểm cuối tự động phát hiện"
,
"Override auto-discovered endpoint"
:
"Ghi đè điểm cuối tự động phát hiện"
,
"Override request headers"
:
"Ghi đè tiêu đề yêu cầu"
,
"Override request headers"
:
"Ghi đè tiêu đề yêu cầu"
,
"Override request headers (JSON format)"
:
"Ghi đè tiêu đề yêu cầu (định dạng JSON)"
,
"Override request headers (JSON format)"
:
"Ghi đè tiêu đề yêu cầu (định dạng JSON)"
,
"Override request parameters"
:
"Ghi đè tham số yêu cầu"
,
"Override request parameters (JSON format)"
:
"Ghi đè tham số yêu cầu (định dạng JSON)"
,
"Override request parameters (JSON format)"
:
"Ghi đè tham số yêu cầu (định dạng JSON)"
,
"Override request parameters. Cannot override"
:
"Ghi đè tham số yêu cầu. Không thể ghi đè"
,
"Override request parameters. Cannot override"
:
"Ghi đè tham số yêu cầu. Không thể ghi đè"
,
"Override request parameters. Cannot override stream parameter."
:
"Ghi đè tham số yêu cầu. Không thể ghi đè tham số stream."
,
"Override request parameters. Cannot override stream parameter."
:
"Ghi đè tham số yêu cầu. Không thể ghi đè tham số stream."
,
...
...
web/default/src/i18n/locales/zh.json
View file @
dc245ae7
...
@@ -2778,6 +2778,7 @@
...
@@ -2778,6 +2778,7 @@
"Override auto-discovered endpoint"
:
"覆盖自动发现的端点"
,
"Override auto-discovered endpoint"
:
"覆盖自动发现的端点"
,
"Override request headers"
:
"覆盖请求标头"
,
"Override request headers"
:
"覆盖请求标头"
,
"Override request headers (JSON format)"
:
"覆盖请求头(JSON 格式)"
,
"Override request headers (JSON format)"
:
"覆盖请求头(JSON 格式)"
,
"Override request parameters"
:
"覆盖请求参数"
,
"Override request parameters (JSON format)"
:
"覆盖请求参数 (JSON 格式)"
,
"Override request parameters (JSON format)"
:
"覆盖请求参数 (JSON 格式)"
,
"Override request parameters. Cannot override"
:
"覆盖请求参数。无法覆盖"
,
"Override request parameters. Cannot override"
:
"覆盖请求参数。无法覆盖"
,
"Override request parameters. Cannot override stream parameter."
:
"覆盖请求参数。无法覆盖 stream 参数。"
,
"Override request parameters. Cannot override stream parameter."
:
"覆盖请求参数。无法覆盖 stream 参数。"
,
...
...
web/default/src/i18n/static-keys.ts
View file @
dc245ae7
...
@@ -315,6 +315,7 @@ export const STATIC_I18N_KEYS = [
...
@@ -315,6 +315,7 @@ export const STATIC_I18N_KEYS = [
'Regex Replace'
,
'Regex Replace'
,
'Return Error'
,
'Return Error'
,
'Param Override'
,
'Param Override'
,
'Override request parameters'
,
// Profile / 2FA
// Profile / 2FA
'Backed up'
,
'Backed up'
,
...
...
web/default/src/lib/currency.ts
View file @
dc245ae7
...
@@ -292,7 +292,7 @@ function formatCurrencyValue(
...
@@ -292,7 +292,7 @@ function formatCurrencyValue(
maximumFractionDigits
:
digits
,
maximumFractionDigits
:
digits
,
}).
format
(
adjustedValue
)
}).
format
(
adjustedValue
)
return
`
${
meta
.
symbol
}${
decimal
}
`
return
`
${
meta
.
symbol
}
${
decimal
}
`
}
}
/**
/**
...
...
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