feat: implement last 24 hours date range preset and update filters in UsageView
This commit is contained in:
@@ -139,17 +139,6 @@
|
||||
<Select v-model="filters.group_id" :options="groupOptions" searchable @change="emitChange" />
|
||||
</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>
|
||||
|
||||
<!-- Right: actions -->
|
||||
@@ -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<string, any>
|
||||
@@ -195,8 +183,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
})
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'update:startDate',
|
||||
'update:endDate',
|
||||
'change',
|
||||
'refresh',
|
||||
'reset',
|
||||
@@ -248,16 +234,6 @@ const billingTypeOptions = ref<SelectOption[]>([
|
||||
|
||||
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<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(
|
||||
...Array.from(uniqueModels)
|
||||
.sort()
|
||||
|
||||
@@ -106,7 +106,7 @@ const isOpen = ref(false)
|
||||
const containerRef = ref<HTMLElement | null>(null)
|
||||
const localStartDate = ref(props.startDate)
|
||||
const localEndDate = ref(props.endDate)
|
||||
const activePreset = ref<string | null>('7days')
|
||||
const activePreset = ref<string | null>('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',
|
||||
|
||||
@@ -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<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'
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user