Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
a0b76bd6
Commit
a0b76bd6
authored
Mar 16, 2026
by
Ethan0x0000
Browse files
feat: implement last 24 hours date range preset and update filters in UsageView
parent
aa5846b2
Changes
7
Hide whitespace changes
Inline
Side-by-side
frontend/src/components/admin/usage/UsageFilters.vue
View file @
a0b76bd6
...
@@ -139,17 +139,6 @@
...
@@ -139,17 +139,6 @@
<
Select
v
-
model
=
"
filters.group_id
"
:
options
=
"
groupOptions
"
searchable
@
change
=
"
emitChange
"
/>
<
Select
v
-
model
=
"
filters.group_id
"
:
options
=
"
groupOptions
"
searchable
@
change
=
"
emitChange
"
/>
<
/div
>
<
/div
>
<!--
Date
Range
Filter
-->
<
div
class
=
"
w-full sm:w-auto [&_.date-picker-trigger]:w-full
"
>
<
label
class
=
"
input-label
"
>
{{
t
(
'
usage.timeRange
'
)
}}
<
/label
>
<
DateRangePicker
:
start
-
date
=
"
startDate
"
:
end
-
date
=
"
endDate
"
@
update
:
startDate
=
"
updateStartDate
"
@
update
:
endDate
=
"
updateEndDate
"
@
change
=
"
emitChange
"
/>
<
/div
>
<
/div
>
<
/div
>
<!--
Right
:
actions
-->
<!--
Right
:
actions
-->
...
@@ -177,7 +166,6 @@ import { ref, onMounted, onUnmounted, toRef, watch } from 'vue'
...
@@ -177,7 +166,6 @@ import { ref, onMounted, onUnmounted, toRef, watch } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
Select
,
{
type
SelectOption
}
from
'
@/components/common/Select.vue
'
import
Select
,
{
type
SelectOption
}
from
'
@/components/common/Select.vue
'
import
DateRangePicker
from
'
@/components/common/DateRangePicker.vue
'
import
type
{
SimpleApiKey
,
SimpleUser
}
from
'
@/api/admin/usage
'
import
type
{
SimpleApiKey
,
SimpleUser
}
from
'
@/api/admin/usage
'
type
ModelValue
=
Record
<
string
,
any
>
type
ModelValue
=
Record
<
string
,
any
>
...
@@ -195,8 +183,6 @@ const props = withDefaults(defineProps<Props>(), {
...
@@ -195,8 +183,6 @@ const props = withDefaults(defineProps<Props>(), {
}
)
}
)
const
emit
=
defineEmits
([
const
emit
=
defineEmits
([
'
update:modelValue
'
,
'
update:modelValue
'
,
'
update:startDate
'
,
'
update:endDate
'
,
'
change
'
,
'
change
'
,
'
refresh
'
,
'
refresh
'
,
'
reset
'
,
'
reset
'
,
...
@@ -248,16 +234,6 @@ const billingTypeOptions = ref<SelectOption[]>([
...
@@ -248,16 +234,6 @@ const billingTypeOptions = ref<SelectOption[]>([
const
emitChange
=
()
=>
emit
(
'
change
'
)
const
emitChange
=
()
=>
emit
(
'
change
'
)
const
updateStartDate
=
(
value
:
string
)
=>
{
emit
(
'
update:startDate
'
,
value
)
filters
.
value
.
start_date
=
value
}
const
updateEndDate
=
(
value
:
string
)
=>
{
emit
(
'
update:endDate
'
,
value
)
filters
.
value
.
end_date
=
value
}
const
debounceUserSearch
=
()
=>
{
const
debounceUserSearch
=
()
=>
{
if
(
userSearchTimeout
)
clearTimeout
(
userSearchTimeout
)
if
(
userSearchTimeout
)
clearTimeout
(
userSearchTimeout
)
userSearchTimeout
=
setTimeout
(
async
()
=>
{
userSearchTimeout
=
setTimeout
(
async
()
=>
{
...
@@ -441,7 +417,11 @@ onMounted(async () => {
...
@@ -441,7 +417,11 @@ onMounted(async () => {
groupOptions
.
value
.
push
(...
gs
.
items
.
map
((
g
:
any
)
=>
({
value
:
g
.
id
,
label
:
g
.
name
}
)))
groupOptions
.
value
.
push
(...
gs
.
items
.
map
((
g
:
any
)
=>
({
value
:
g
.
id
,
label
:
g
.
name
}
)))
const
uniqueModels
=
new
Set
<
string
>
()
const
uniqueModels
=
new
Set
<
string
>
()
ms
.
models
?.
forEach
((
s
:
any
)
=>
s
.
model
&&
uniqueModels
.
add
(
s
.
model
))
ms
.
models
?.
forEach
((
s
:
any
)
=>
{
if
(
s
.
model
)
{
uniqueModels
.
add
(
s
.
model
)
}
}
)
modelOptions
.
value
.
push
(
modelOptions
.
value
.
push
(
...
Array
.
from
(
uniqueModels
)
...
Array
.
from
(
uniqueModels
)
.
sort
()
.
sort
()
...
...
frontend/src/components/common/DateRangePicker.vue
View file @
a0b76bd6
...
@@ -106,7 +106,7 @@ const isOpen = ref(false)
...
@@ -106,7 +106,7 @@ const isOpen = ref(false)
const
containerRef
=
ref
<
HTMLElement
|
null
>
(
null
)
const
containerRef
=
ref
<
HTMLElement
|
null
>
(
null
)
const
localStartDate
=
ref
(
props
.
startDate
)
const
localStartDate
=
ref
(
props
.
startDate
)
const
localEndDate
=
ref
(
props
.
endDate
)
const
localEndDate
=
ref
(
props
.
endDate
)
const
activePreset
=
ref
<
string
|
null
>
(
'
7day
s
'
)
const
activePreset
=
ref
<
string
|
null
>
(
'
last24Hour
s
'
)
const
today
=
computed
(()
=>
{
const
today
=
computed
(()
=>
{
// Use local timezone to avoid UTC timezone issues
// Use local timezone to avoid UTC timezone issues
...
@@ -152,6 +152,18 @@ const presets: DatePreset[] = [
...
@@ -152,6 +152,18 @@ const presets: DatePreset[] = [
return
{
start
:
yesterday
,
end
:
yesterday
}
return
{
start
:
yesterday
,
end
:
yesterday
}
}
}
},
},
{
labelKey
:
'
dates.last24Hours
'
,
value
:
'
last24Hours
'
,
getRange
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
(
end
.
getTime
()
-
24
*
60
*
60
*
1000
)
return
{
start
:
formatDateToString
(
start
),
end
:
formatDateToString
(
end
)
}
}
},
{
{
labelKey
:
'
dates.last7Days
'
,
labelKey
:
'
dates.last7Days
'
,
value
:
'
7days
'
,
value
:
'
7days
'
,
...
...
frontend/src/components/common/__tests__/DateRangePicker.spec.ts
0 → 100644
View file @
a0b76bd6
import
{
describe
,
expect
,
it
,
vi
}
from
'
vitest
'
import
{
mount
}
from
'
@vue/test-utils
'
import
{
ref
}
from
'
vue
'
import
DateRangePicker
from
'
../DateRangePicker.vue
'
const
messages
:
Record
<
string
,
string
>
=
{
'
dates.today
'
:
'
Today
'
,
'
dates.yesterday
'
:
'
Yesterday
'
,
'
dates.last24Hours
'
:
'
Last 24 Hours
'
,
'
dates.last7Days
'
:
'
Last 7 Days
'
,
'
dates.last14Days
'
:
'
Last 14 Days
'
,
'
dates.last30Days
'
:
'
Last 30 Days
'
,
'
dates.thisMonth
'
:
'
This Month
'
,
'
dates.lastMonth
'
:
'
Last Month
'
,
'
dates.startDate
'
:
'
Start Date
'
,
'
dates.endDate
'
:
'
End Date
'
,
'
dates.apply
'
:
'
Apply
'
,
'
dates.selectDateRange
'
:
'
Select date range
'
}
vi
.
mock
(
'
vue-i18n
'
,
()
=>
({
useI18n
:
()
=>
({
t
:
(
key
:
string
)
=>
messages
[
key
]
??
key
,
locale
:
ref
(
'
en
'
)
})
}))
const
formatLocalDate
=
(
date
:
Date
):
string
=>
{
const
year
=
date
.
getFullYear
()
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
'
0
'
)
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
'
0
'
)
return
`
${
year
}
-
${
month
}
-
${
day
}
`
}
describe
(
'
DateRangePicker
'
,
()
=>
{
it
(
'
uses last 24 hours as the default recognized preset
'
,
()
=>
{
const
now
=
new
Date
()
const
yesterday
=
new
Date
(
now
.
getTime
()
-
24
*
60
*
60
*
1000
)
const
wrapper
=
mount
(
DateRangePicker
,
{
props
:
{
startDate
:
formatLocalDate
(
yesterday
),
endDate
:
formatLocalDate
(
now
)
},
global
:
{
stubs
:
{
Icon
:
true
}
}
})
expect
(
wrapper
.
text
()).
toContain
(
'
Last 24 Hours
'
)
})
it
(
'
emits range updates with last24Hours preset when applied
'
,
async
()
=>
{
const
now
=
new
Date
()
const
today
=
formatLocalDate
(
now
)
const
wrapper
=
mount
(
DateRangePicker
,
{
props
:
{
startDate
:
today
,
endDate
:
today
},
global
:
{
stubs
:
{
Icon
:
true
}
}
})
await
wrapper
.
find
(
'
.date-picker-trigger
'
).
trigger
(
'
click
'
)
const
presetButton
=
wrapper
.
findAll
(
'
.date-picker-preset
'
).
find
((
node
)
=>
node
.
text
().
includes
(
'
Last 24 Hours
'
)
)
expect
(
presetButton
).
toBeDefined
()
await
presetButton
!
.
trigger
(
'
click
'
)
await
wrapper
.
find
(
'
.date-picker-apply
'
).
trigger
(
'
click
'
)
const
nowAfterClick
=
new
Date
()
const
yesterdayAfterClick
=
new
Date
(
nowAfterClick
.
getTime
()
-
24
*
60
*
60
*
1000
)
const
expectedStart
=
formatLocalDate
(
yesterdayAfterClick
)
const
expectedEnd
=
formatLocalDate
(
nowAfterClick
)
expect
(
wrapper
.
emitted
(
'
update:startDate
'
)?.[
0
]).
toEqual
([
expectedStart
])
expect
(
wrapper
.
emitted
(
'
update:endDate
'
)?.[
0
]).
toEqual
([
expectedEnd
])
expect
(
wrapper
.
emitted
(
'
change
'
)?.[
0
]).
toEqual
([
{
startDate
:
expectedStart
,
endDate
:
expectedEnd
,
preset
:
'
last24Hours
'
}
])
})
})
frontend/src/i18n/locales/en.ts
View file @
a0b76bd6
...
@@ -920,6 +920,7 @@ export default {
...
@@ -920,6 +920,7 @@ export default {
lastWeek
:
'
Last Week
'
,
lastWeek
:
'
Last Week
'
,
thisMonth
:
'
This Month
'
,
thisMonth
:
'
This Month
'
,
lastMonth
:
'
Last Month
'
,
lastMonth
:
'
Last Month
'
,
last24Hours
:
'
Last 24 Hours
'
,
last7Days
:
'
Last 7 Days
'
,
last7Days
:
'
Last 7 Days
'
,
last14Days
:
'
Last 14 Days
'
,
last14Days
:
'
Last 14 Days
'
,
last30Days
:
'
Last 30 Days
'
,
last30Days
:
'
Last 30 Days
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
a0b76bd6
...
@@ -925,6 +925,7 @@ export default {
...
@@ -925,6 +925,7 @@ export default {
lastWeek
:
'
上周
'
,
lastWeek
:
'
上周
'
,
thisMonth
:
'
本月
'
,
thisMonth
:
'
本月
'
,
lastMonth
:
'
上月
'
,
lastMonth
:
'
上月
'
,
last24Hours
:
'
近24小时
'
,
last7Days
:
'
近 7 天
'
,
last7Days
:
'
近 7 天
'
,
last14Days
:
'
近 14 天
'
,
last14Days
:
'
近 14 天
'
,
last30Days
:
'
近 30 天
'
,
last30Days
:
'
近 30 天
'
,
...
...
frontend/src/views/admin/UsageView.vue
View file @
a0b76bd6
...
@@ -5,10 +5,20 @@
...
@@ -5,10 +5,20 @@
<!-- Charts Section -->
<!-- Charts Section -->
<div
class=
"space-y-4"
>
<div
class=
"space-y-4"
>
<div
class=
"card p-4"
>
<div
class=
"card p-4"
>
<div
class=
"flex items-center gap-4"
>
<div
class=
"flex flex-wrap items-center gap-4"
>
<span
class=
"text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.dashboard.granularity
'
)
}}
:
</span>
<div
class=
"flex items-center gap-2"
>
<div
class=
"w-28"
>
<span
class=
"text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.dashboard.timeRange
'
)
}}
:
</span>
<Select
v-model=
"granularity"
:options=
"granularityOptions"
@
change=
"loadChartData"
/>
<DateRangePicker
v-model:start-date=
"startDate"
v-model:end-date=
"endDate"
@
change=
"onDateRangeChange"
/>
</div>
<div
class=
"ml-auto flex items-center gap-2"
>
<span
class=
"text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.dashboard.granularity
'
)
}}
:
</span>
<div
class=
"w-28"
>
<Select
v-model=
"granularity"
:options=
"granularityOptions"
@
change=
"loadChartData"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -41,7 +51,7 @@
...
@@ -41,7 +51,7 @@
<TokenUsageTrend
:trend-data=
"trendData"
:loading=
"chartsLoading"
/>
<TokenUsageTrend
:trend-data=
"trendData"
:loading=
"chartsLoading"
/>
</div>
</div>
</div>
</div>
<UsageFilters
v-model=
"filters"
v-model
:start
D
ate=
"startDate"
v-model
:end
D
ate=
"endDate"
:exporting=
"exporting"
@
change=
"applyFilters"
@
refresh=
"refreshData"
@
reset=
"resetFilters"
@
cleanup=
"openCleanupDialog"
@
export=
"exportToExcel"
>
<UsageFilters
v-model=
"filters"
:start
-d
ate=
"startDate"
:end
-d
ate=
"endDate"
:exporting=
"exporting"
@
change=
"applyFilters"
@
refresh=
"refreshData"
@
reset=
"resetFilters"
@
cleanup=
"openCleanupDialog"
@
export=
"exportToExcel"
>
<template
#after-reset
>
<template
#after-reset
>
<div
class=
"relative"
ref=
"columnDropdownRef"
>
<div
class=
"relative"
ref=
"columnDropdownRef"
>
<button
<button
...
@@ -106,7 +116,7 @@ import { useRoute } from 'vue-router'
...
@@ -106,7 +116,7 @@ import { useRoute } from 'vue-router'
import
{
useAppStore
}
from
'
@/stores/app
'
;
import
{
adminAPI
}
from
'
@/api/admin
'
;
import
{
adminUsageAPI
}
from
'
@/api/admin/usage
'
import
{
useAppStore
}
from
'
@/stores/app
'
;
import
{
adminAPI
}
from
'
@/api/admin
'
;
import
{
adminUsageAPI
}
from
'
@/api/admin/usage
'
import
{
formatReasoningEffort
}
from
'
@/utils/format
'
import
{
formatReasoningEffort
}
from
'
@/utils/format
'
import
{
resolveUsageRequestType
,
requestTypeToLegacyStream
}
from
'
@/utils/usageRequestType
'
import
{
resolveUsageRequestType
,
requestTypeToLegacyStream
}
from
'
@/utils/usageRequestType
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
;
import
Pagination
from
'
@/components/common/Pagination.vue
'
;
import
Select
from
'
@/components/common/Select.vue
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
;
import
Pagination
from
'
@/components/common/Pagination.vue
'
;
import
Select
from
'
@/components/common/Select.vue
'
;
import
DateRangePicker
from
'
@/components/common/DateRangePicker.vue
'
import
UsageStatsCards
from
'
@/components/admin/usage/UsageStatsCards.vue
'
;
import
UsageFilters
from
'
@/components/admin/usage/UsageFilters.vue
'
import
UsageStatsCards
from
'
@/components/admin/usage/UsageStatsCards.vue
'
;
import
UsageFilters
from
'
@/components/admin/usage/UsageFilters.vue
'
import
UsageTable
from
'
@/components/admin/usage/UsageTable.vue
'
;
import
UsageExportProgress
from
'
@/components/admin/usage/UsageExportProgress.vue
'
import
UsageTable
from
'
@/components/admin/usage/UsageTable.vue
'
;
import
UsageExportProgress
from
'
@/components/admin/usage/UsageExportProgress.vue
'
import
UsageCleanupDialog
from
'
@/components/admin/usage/UsageCleanupDialog.vue
'
import
UsageCleanupDialog
from
'
@/components/admin/usage/UsageCleanupDialog.vue
'
...
@@ -158,9 +168,22 @@ const formatLD = (d: Date) => {
...
@@ -158,9 +168,22 @@ const formatLD = (d: Date) => {
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
'
0
'
)
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
'
0
'
)
return
`
${
year
}
-
${
month
}
-
${
day
}
`
return
`
${
year
}
-
${
month
}
-
${
day
}
`
}
}
const
getTodayLocalDate
=
()
=>
formatLD
(
new
Date
())
const
getLast24HoursRangeDates
=
():
{
start
:
string
;
end
:
string
}
=>
{
const
getGranularityForRange
=
(
start
:
string
,
end
:
string
):
'
day
'
|
'
hour
'
=>
start
===
end
?
'
hour
'
:
'
day
'
const
end
=
new
Date
()
const
startDate
=
ref
(
getTodayLocalDate
());
const
endDate
=
ref
(
getTodayLocalDate
())
const
start
=
new
Date
(
end
.
getTime
()
-
24
*
60
*
60
*
1000
)
return
{
start
:
formatLD
(
start
),
end
:
formatLD
(
end
)
}
}
const
getGranularityForRange
=
(
start
:
string
,
end
:
string
):
'
day
'
|
'
hour
'
=>
{
const
startTime
=
new
Date
(
`
${
start
}
T00:00:00`
).
getTime
()
const
endTime
=
new
Date
(
`
${
end
}
T00:00:00`
).
getTime
()
const
daysDiff
=
Math
.
ceil
((
endTime
-
startTime
)
/
(
1000
*
60
*
60
*
24
))
return
daysDiff
<=
1
?
'
hour
'
:
'
day
'
}
const
defaultRange
=
getLast24HoursRangeDates
()
const
startDate
=
ref
(
defaultRange
.
start
);
const
endDate
=
ref
(
defaultRange
.
end
)
const
filters
=
ref
<
AdminUsageQueryParams
>
({
user_id
:
undefined
,
model
:
undefined
,
group_id
:
undefined
,
request_type
:
undefined
,
billing_type
:
null
,
start_date
:
startDate
.
value
,
end_date
:
endDate
.
value
})
const
filters
=
ref
<
AdminUsageQueryParams
>
({
user_id
:
undefined
,
model
:
undefined
,
group_id
:
undefined
,
request_type
:
undefined
,
billing_type
:
null
,
start_date
:
startDate
.
value
,
end_date
:
endDate
.
value
})
const
pagination
=
reactive
({
page
:
1
,
page_size
:
20
,
total
:
0
})
const
pagination
=
reactive
({
page
:
1
,
page_size
:
20
,
total
:
0
})
...
@@ -197,6 +220,18 @@ const applyRouteQueryFilters = () => {
...
@@ -197,6 +220,18 @@ const applyRouteQueryFilters = () => {
granularity
.
value
=
getGranularityForRange
(
startDate
.
value
,
endDate
.
value
)
granularity
.
value
=
getGranularityForRange
(
startDate
.
value
,
endDate
.
value
)
}
}
const
onDateRangeChange
=
(
range
:
{
startDate
:
string
;
endDate
:
string
;
preset
:
string
|
null
})
=>
{
startDate
.
value
=
range
.
startDate
endDate
.
value
=
range
.
endDate
filters
.
value
=
{
...
filters
.
value
,
start_date
:
range
.
startDate
,
end_date
:
range
.
endDate
}
granularity
.
value
=
getGranularityForRange
(
range
.
startDate
,
range
.
endDate
)
applyFilters
()
}
const
loadLogs
=
async
()
=>
{
const
loadLogs
=
async
()
=>
{
abortController
?.
abort
();
const
c
=
new
AbortController
();
abortController
=
c
;
loading
.
value
=
true
abortController
?.
abort
();
const
c
=
new
AbortController
();
abortController
=
c
;
loading
.
value
=
true
try
{
try
{
...
@@ -260,7 +295,14 @@ const loadChartData = async () => {
...
@@ -260,7 +295,14 @@ const loadChartData = async () => {
}
}
const
applyFilters
=
()
=>
{
pagination
.
page
=
1
;
loadLogs
();
loadStats
();
loadChartData
()
}
const
applyFilters
=
()
=>
{
pagination
.
page
=
1
;
loadLogs
();
loadStats
();
loadChartData
()
}
const
refreshData
=
()
=>
{
loadLogs
();
loadStats
();
loadChartData
()
}
const
refreshData
=
()
=>
{
loadLogs
();
loadStats
();
loadChartData
()
}
const
resetFilters
=
()
=>
{
startDate
.
value
=
getTodayLocalDate
();
endDate
.
value
=
getTodayLocalDate
();
filters
.
value
=
{
start_date
:
startDate
.
value
,
end_date
:
endDate
.
value
,
request_type
:
undefined
,
billing_type
:
null
};
granularity
.
value
=
getGranularityForRange
(
startDate
.
value
,
endDate
.
value
);
applyFilters
()
}
const
resetFilters
=
()
=>
{
const
range
=
getLast24HoursRangeDates
()
startDate
.
value
=
range
.
start
endDate
.
value
=
range
.
end
filters
.
value
=
{
start_date
:
startDate
.
value
,
end_date
:
endDate
.
value
,
request_type
:
undefined
,
billing_type
:
null
}
granularity
.
value
=
getGranularityForRange
(
startDate
.
value
,
endDate
.
value
)
applyFilters
()
}
const
handlePageChange
=
(
p
:
number
)
=>
{
pagination
.
page
=
p
;
loadLogs
()
}
const
handlePageChange
=
(
p
:
number
)
=>
{
pagination
.
page
=
p
;
loadLogs
()
}
const
handlePageSizeChange
=
(
s
:
number
)
=>
{
pagination
.
page_size
=
s
;
pagination
.
page
=
1
;
loadLogs
()
}
const
handlePageSizeChange
=
(
s
:
number
)
=>
{
pagination
.
page_size
=
s
;
pagination
.
page
=
1
;
loadLogs
()
}
const
cancelExport
=
()
=>
exportAbortController
?.
abort
()
const
cancelExport
=
()
=>
exportAbortController
?.
abort
()
...
...
frontend/src/views/admin/__tests__/UsageView.spec.ts
View file @
a0b76bd6
...
@@ -19,11 +19,19 @@ const { list, getStats, getSnapshotV2, getById } = vi.hoisted(() => {
...
@@ -19,11 +19,19 @@ const { list, getStats, getSnapshotV2, getById } = vi.hoisted(() => {
})
})
const
messages
:
Record
<
string
,
string
>
=
{
const
messages
:
Record
<
string
,
string
>
=
{
'
admin.dashboard.timeRange
'
:
'
Time Range
'
,
'
admin.dashboard.day
'
:
'
Day
'
,
'
admin.dashboard.day
'
:
'
Day
'
,
'
admin.dashboard.hour
'
:
'
Hour
'
,
'
admin.dashboard.hour
'
:
'
Hour
'
,
'
admin.usage.failedToLoadUser
'
:
'
Failed to load user
'
,
'
admin.usage.failedToLoadUser
'
:
'
Failed to load user
'
,
}
}
const
formatLocalDate
=
(
date
:
Date
):
string
=>
{
const
year
=
date
.
getFullYear
()
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
'
0
'
)
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
'
0
'
)
return
`
${
year
}
-
${
month
}
-
${
day
}
`
}
vi
.
mock
(
'
@/api/admin
'
,
()
=>
({
vi
.
mock
(
'
@/api/admin
'
,
()
=>
({
adminAPI
:
{
adminAPI
:
{
usage
:
{
usage
:
{
...
@@ -68,6 +76,12 @@ vi.mock('vue-i18n', async () => {
...
@@ -68,6 +76,12 @@ vi.mock('vue-i18n', async () => {
}
}
})
})
vi
.
mock
(
'
vue-router
'
,
()
=>
({
useRoute
:
()
=>
({
query
:
{}
})
}))
const
AppLayoutStub
=
{
template
:
'
<div><slot /></div>
'
}
const
AppLayoutStub
=
{
template
:
'
<div><slot /></div>
'
}
const
UsageFiltersStub
=
{
template
:
'
<div><slot name="after-reset" /></div>
'
}
const
UsageFiltersStub
=
{
template
:
'
<div><slot name="after-reset" /></div>
'
}
const
ModelDistributionChartStub
=
{
const
ModelDistributionChartStub
=
{
...
@@ -138,6 +152,7 @@ describe('admin UsageView distribution metric toggles', () => {
...
@@ -138,6 +152,7 @@ describe('admin UsageView distribution metric toggles', () => {
UserBalanceHistoryModal
:
true
,
UserBalanceHistoryModal
:
true
,
Pagination
:
true
,
Pagination
:
true
,
Select
:
true
,
Select
:
true
,
DateRangePicker
:
true
,
Icon
:
true
,
Icon
:
true
,
TokenUsageTrend
:
true
,
TokenUsageTrend
:
true
,
ModelDistributionChart
:
ModelDistributionChartStub
,
ModelDistributionChart
:
ModelDistributionChartStub
,
...
@@ -150,6 +165,13 @@ describe('admin UsageView distribution metric toggles', () => {
...
@@ -150,6 +165,13 @@ describe('admin UsageView distribution metric toggles', () => {
await
flushPromises
()
await
flushPromises
()
expect
(
getSnapshotV2
).
toHaveBeenCalledTimes
(
1
)
expect
(
getSnapshotV2
).
toHaveBeenCalledTimes
(
1
)
const
now
=
new
Date
()
const
yesterday
=
new
Date
(
now
.
getTime
()
-
24
*
60
*
60
*
1000
)
expect
(
getSnapshotV2
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
start_date
:
formatLocalDate
(
yesterday
),
end_date
:
formatLocalDate
(
now
),
granularity
:
'
hour
'
}))
const
modelChart
=
wrapper
.
find
(
'
[data-test="model-chart"]
'
)
const
modelChart
=
wrapper
.
find
(
'
[data-test="model-chart"]
'
)
const
groupChart
=
wrapper
.
find
(
'
[data-test="group-chart"]
'
)
const
groupChart
=
wrapper
.
find
(
'
[data-test="group-chart"]
'
)
...
...
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