diff --git a/frontend/src/components/admin/usage/UsageFilters.vue b/frontend/src/components/admin/usage/UsageFilters.vue
index a632a76e..ee5020e7 100644
--- a/frontend/src/components/admin/usage/UsageFilters.vue
+++ b/frontend/src/components/admin/usage/UsageFilters.vue
@@ -139,17 +139,6 @@
-
-
-
-
-
@@ -177,7 +166,6 @@ import { ref, onMounted, onUnmounted, toRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { adminAPI } from '@/api/admin'
import Select, { type SelectOption } from '@/components/common/Select.vue'
-import DateRangePicker from '@/components/common/DateRangePicker.vue'
import type { SimpleApiKey, SimpleUser } from '@/api/admin/usage'
type ModelValue = Record
@@ -195,8 +183,6 @@ const props = withDefaults(defineProps(), {
})
const emit = defineEmits([
'update:modelValue',
- 'update:startDate',
- 'update:endDate',
'change',
'refresh',
'reset',
@@ -248,16 +234,6 @@ const billingTypeOptions = ref([
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 = () => {
if (userSearchTimeout) clearTimeout(userSearchTimeout)
userSearchTimeout = setTimeout(async () => {
@@ -441,7 +417,11 @@ onMounted(async () => {
groupOptions.value.push(...gs.items.map((g: any) => ({ value: g.id, label: g.name })))
const uniqueModels = new Set()
- 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(
...Array.from(uniqueModels)
.sort()
diff --git a/frontend/src/components/common/DateRangePicker.vue b/frontend/src/components/common/DateRangePicker.vue
index cf5b56fc..b00334a6 100644
--- a/frontend/src/components/common/DateRangePicker.vue
+++ b/frontend/src/components/common/DateRangePicker.vue
@@ -106,7 +106,7 @@ const isOpen = ref(false)
const containerRef = ref(null)
const localStartDate = ref(props.startDate)
const localEndDate = ref(props.endDate)
-const activePreset = ref('7days')
+const activePreset = ref('last24Hours')
const today = computed(() => {
// Use local timezone to avoid UTC timezone issues
@@ -152,6 +152,18 @@ const presets: DatePreset[] = [
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',
value: '7days',
diff --git a/frontend/src/components/common/__tests__/DateRangePicker.spec.ts b/frontend/src/components/common/__tests__/DateRangePicker.spec.ts
new file mode 100644
index 00000000..8b155b60
--- /dev/null
+++ b/frontend/src/components/common/__tests__/DateRangePicker.spec.ts
@@ -0,0 +1,96 @@
+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 = {
+ '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'
+ }
+ ])
+ })
+})
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index 6484a27a..01945d4d 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -920,6 +920,7 @@ export default {
lastWeek: 'Last Week',
thisMonth: 'This Month',
lastMonth: 'Last Month',
+ last24Hours: 'Last 24 Hours',
last7Days: 'Last 7 Days',
last14Days: 'Last 14 Days',
last30Days: 'Last 30 Days',
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index 2f3eb290..6e508a53 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -925,6 +925,7 @@ export default {
lastWeek: '上周',
thisMonth: '本月',
lastMonth: '上月',
+ last24Hours: '近24小时',
last7Days: '近 7 天',
last14Days: '近 14 天',
last30Days: '近 30 天',
diff --git a/frontend/src/views/admin/UsageView.vue b/frontend/src/views/admin/UsageView.vue
index 92d0938c..2bbc82a7 100644
--- a/frontend/src/views/admin/UsageView.vue
+++ b/frontend/src/views/admin/UsageView.vue
@@ -5,10 +5,20 @@
-
-
{{ t('admin.dashboard.granularity') }}:
-
-
+
+
+ {{ t('admin.dashboard.timeRange') }}:
+
+
+
+
{{ t('admin.dashboard.granularity') }}:
+
+
+
@@ -41,7 +51,7 @@
-
+