fix: usage datepicker
This commit is contained in:
@@ -47,6 +47,7 @@ const SimpleSelect: React.FC<
|
||||
SelectProps & {
|
||||
ref?: any;
|
||||
showTags?: boolean;
|
||||
optionLabelRender?: (option: any) => React.ReactNode;
|
||||
styles?: {
|
||||
wrapper?: React.CSSProperties;
|
||||
select?: React.CSSProperties;
|
||||
@@ -58,6 +59,7 @@ const SimpleSelect: React.FC<
|
||||
options = [],
|
||||
showTags: tagsVisible,
|
||||
styles = {},
|
||||
optionLabelRender,
|
||||
maxTagCount: tagCount,
|
||||
...restProps
|
||||
} = props;
|
||||
@@ -90,7 +92,9 @@ const SimpleSelect: React.FC<
|
||||
) : (
|
||||
<Checkbox></Checkbox>
|
||||
)}
|
||||
<AutoTooltip ghost>{label}</AutoTooltip>
|
||||
<AutoTooltip ghost>
|
||||
{optionLabelRender ? optionLabelRender(option) : label}
|
||||
</AutoTooltip>
|
||||
</OptionWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,6 +18,11 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
|
||||
label: React.ReactNode;
|
||||
value: [Dayjs, Dayjs] | (() => [Dayjs, Dayjs]);
|
||||
}[];
|
||||
normalizeRangeValue: (
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
picker: 'date' | 'week' | 'month' | 'quarter' | 'year'
|
||||
) => [Dayjs, Dayjs];
|
||||
handleOnPickerChange: (
|
||||
value: 'date' | 'week' | 'month' | 'quarter' | 'year'
|
||||
) => void;
|
||||
@@ -28,22 +33,76 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
|
||||
const intl = useIntl();
|
||||
const [picker, setPicker] = useState<
|
||||
'date' | 'week' | 'month' | 'quarter' | 'year'
|
||||
>('month');
|
||||
>('date');
|
||||
|
||||
const getYearMonth = (date: Dayjs) => date.year() * 12 + date.month();
|
||||
|
||||
const normalizeRangeValue = (
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
picker: 'date' | 'week' | 'month' | 'quarter' | 'year'
|
||||
): [Dayjs, Dayjs] => {
|
||||
const start = dayjs(startDate);
|
||||
const end = dayjs(endDate);
|
||||
|
||||
switch (picker) {
|
||||
case 'week':
|
||||
return [start.startOf('week'), end.endOf('week')];
|
||||
|
||||
case 'month':
|
||||
return [start.startOf('month'), end.endOf('month')];
|
||||
|
||||
case 'year':
|
||||
return [start.startOf('year'), end.endOf('year')];
|
||||
|
||||
default:
|
||||
return [start, end];
|
||||
}
|
||||
};
|
||||
|
||||
const getRangeBoundary = (
|
||||
from: Dayjs,
|
||||
range: number,
|
||||
type: 'date' | 'week' | 'month' | 'quarter' | 'year'
|
||||
) => {
|
||||
switch (type) {
|
||||
case 'year':
|
||||
return {
|
||||
minDate: from.add(1 - range, 'year'),
|
||||
maxDate: from.add(range - 1, 'year')
|
||||
};
|
||||
|
||||
case 'month':
|
||||
return {
|
||||
minDate: from.add(1 - range, 'month'),
|
||||
maxDate: from.add(range - 1, 'month')
|
||||
};
|
||||
|
||||
case 'week':
|
||||
return {
|
||||
minDate: from.add(1 - range, 'week'),
|
||||
maxDate: from.add(range - 1, 'week')
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
minDate: from.add(1 - range, 'day'),
|
||||
maxDate: from.add(range - 1, 'day')
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const disabledRangeDaysDate: DatePickerProps['disabledDate'] = (
|
||||
current,
|
||||
{ from, type }
|
||||
) => {
|
||||
if (!disabledDate) {
|
||||
if (!disabledDate || !from) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (from) {
|
||||
const minDate = from.add(1 - range, 'days');
|
||||
const maxDate = from.add(range - 1, 'days');
|
||||
const { minDate, maxDate } = getRangeBoundary(from, range, type as any);
|
||||
|
||||
if (from) {
|
||||
switch (type) {
|
||||
case 'year':
|
||||
return (
|
||||
@@ -56,6 +115,16 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
|
||||
getYearMonth(current) > getYearMonth(maxDate)
|
||||
);
|
||||
|
||||
case 'week': {
|
||||
const currentWeekStart = current.startOf('week');
|
||||
const currentWeekEnd = current.endOf('week');
|
||||
|
||||
return (
|
||||
currentWeekEnd.isBefore(minDate, 'day') ||
|
||||
currentWeekStart.isAfter(maxDate, 'day')
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
return current.isBefore(minDate) || current.isAfter(maxDate);
|
||||
}
|
||||
@@ -94,6 +163,7 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
|
||||
return {
|
||||
disabledRangeDaysDate,
|
||||
handleOnPickerChange,
|
||||
normalizeRangeValue,
|
||||
rangePresets,
|
||||
picker,
|
||||
range
|
||||
|
||||
@@ -28,6 +28,7 @@ const BreakdownTabs: React.FC<{
|
||||
forceRender: true,
|
||||
children: (
|
||||
<ModelsTable
|
||||
key="models"
|
||||
models={filters.models || []}
|
||||
dateRange={dateRange}
|
||||
scope={scope}
|
||||
@@ -40,6 +41,7 @@ const BreakdownTabs: React.FC<{
|
||||
forceRender: true,
|
||||
children: (
|
||||
<UsersTable
|
||||
key="users"
|
||||
users={filters.users || []}
|
||||
dateRange={dateRange}
|
||||
scope={scope}
|
||||
@@ -52,6 +54,7 @@ const BreakdownTabs: React.FC<{
|
||||
forceRender: true,
|
||||
children: (
|
||||
<ApiKeysTable
|
||||
key="api_keys"
|
||||
apiKeys={filters.api_keys || []}
|
||||
dateRange={dateRange}
|
||||
scope={scope}
|
||||
|
||||
@@ -25,8 +25,6 @@ const ControlsWrapper = styled.div`
|
||||
const ControlLabel = styled.span`
|
||||
font-size: 14px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
border-right: 1px solid var(--ant-color-split);
|
||||
padding-right: 8px;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
@@ -154,8 +152,9 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
||||
prefix={<ControlLabel>Metric</ControlLabel>}
|
||||
options={metricOptions}
|
||||
value={metric}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={onMetricChange}
|
||||
style={{ width: 200 }}
|
||||
style={{ width: 180 }}
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
@@ -163,19 +162,17 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
||||
prefix={<ControlLabel>Group by</ControlLabel>}
|
||||
options={groupByOptions}
|
||||
value={groupBy}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={onGroupByChange}
|
||||
style={{ width: 200 }}
|
||||
/>
|
||||
</div>
|
||||
<Segmented
|
||||
size="middle"
|
||||
shape="round"
|
||||
size="small"
|
||||
options={granularities}
|
||||
value={granularity}
|
||||
onChange={onGranularityChange}
|
||||
></Segmented>
|
||||
</ControlsWrapper>
|
||||
{/* <Divider style={{ margin: 0 }} /> */}
|
||||
|
||||
{/* <SimpleCard dataList={summaryCards} height={80} /> */}
|
||||
<MixLineBar
|
||||
|
||||
@@ -62,37 +62,42 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
handleSearch
|
||||
} = props;
|
||||
const intl = useIntl();
|
||||
const { disabledRangeDaysDate, rangePresets, picker, handleOnPickerChange } =
|
||||
useRangePickerPreset({
|
||||
range: DefaultDateConfig.maxRange,
|
||||
disabledDate: true,
|
||||
presetRanges: [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last7days'
|
||||
}),
|
||||
value: [dayjs().add(-6, 'd'), dayjs()]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last30days'
|
||||
}),
|
||||
value: [dayjs().add(-29, 'd'), dayjs()]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last60days'
|
||||
}),
|
||||
value: [dayjs().add(-59, 'd'), dayjs()]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last90days'
|
||||
}),
|
||||
value: [dayjs().add(-89, 'd'), dayjs()]
|
||||
}
|
||||
]
|
||||
});
|
||||
const {
|
||||
disabledRangeDaysDate,
|
||||
rangePresets,
|
||||
picker,
|
||||
normalizeRangeValue,
|
||||
handleOnPickerChange
|
||||
} = useRangePickerPreset({
|
||||
range: DefaultDateConfig.maxRange,
|
||||
disabledDate: true,
|
||||
presetRanges: [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last7days'
|
||||
}),
|
||||
value: [dayjs().add(-6, 'd'), dayjs()]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last30days'
|
||||
}),
|
||||
value: [dayjs().add(-29, 'd'), dayjs()]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last60days'
|
||||
}),
|
||||
value: [dayjs().add(-59, 'd'), dayjs()]
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'dashboard.usage.datePicker.last90days'
|
||||
}),
|
||||
value: [dayjs().add(-89, 'd'), dayjs()]
|
||||
}
|
||||
]
|
||||
});
|
||||
const [activeModels, setActiveModels] = React.useState<valueType[][]>([]);
|
||||
const [activeApiKeys, setActiveApiKeys] = React.useState<valueType[][]>([]);
|
||||
|
||||
@@ -222,6 +227,16 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const userOptionRender = (option: any) => {
|
||||
const { data } = option;
|
||||
return (
|
||||
<span className="flex-center gap-8">
|
||||
<span>{data.label}</span>
|
||||
{data.isCurrent && <span className="text-tertiary"> [Current]</span>}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={FilterBarCss.wrapper}>
|
||||
<div className={FilterBarCss.filters}>
|
||||
@@ -231,12 +246,12 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
dayjs().add(-DefaultDateConfig.defaultRange, 'd'),
|
||||
dayjs()
|
||||
]}
|
||||
format={'YYYY-MM-DD'}
|
||||
picker={picker}
|
||||
disabledDate={disabledRangeDaysDate}
|
||||
presets={rangePresets}
|
||||
allowClear={false}
|
||||
style={{ width: 240 }}
|
||||
value={[dayjs(startDate), dayjs(endDate)]}
|
||||
onChange={onDateChange}
|
||||
renderExtraFooter={renderFooter}
|
||||
/>
|
||||
@@ -294,6 +309,7 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
wrapper: { flex: 1, maxWidth: 240, minWidth: 150 }
|
||||
}}
|
||||
value={selectedUsers}
|
||||
optionLabelRender={userOptionRender}
|
||||
onChange={onUsersChange}
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface UsageFilterItem {
|
||||
};
|
||||
current: {
|
||||
model_id: string | null;
|
||||
user_id: string | null;
|
||||
user_id: number | null;
|
||||
api_key_id: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -129,6 +129,7 @@ export const useUsageFilters = ({
|
||||
};
|
||||
|
||||
const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
console.log('Selected Time: ', dateStrings);
|
||||
const [start_date, end_date] = dateStrings;
|
||||
const next = { ...commonFilters, start_date, end_date };
|
||||
setCommonFilters(next);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useQueryData } from '@/hooks/use-query-data-list';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { useState } from 'react';
|
||||
import { queryUsageMetaData } from '../apis';
|
||||
import { GroupOption, groupToOptions } from '../config';
|
||||
@@ -14,7 +15,7 @@ export default function useQueryUsageMetaData() {
|
||||
fetchDetail: queryUsageMetaData,
|
||||
key: 'usageMetaData'
|
||||
});
|
||||
|
||||
const { initialState } = useModel('@@initialState');
|
||||
const [result, setResult] = useState<{
|
||||
models: GroupOption<UsageFilterItem>[];
|
||||
users: UserOptionType[];
|
||||
@@ -25,9 +26,23 @@ export default function useQueryUsageMetaData() {
|
||||
api_keys: []
|
||||
});
|
||||
|
||||
// the current user sort in the first place
|
||||
const sortUsers = (users: UsageFilterItem[]) => {
|
||||
const currentUserId = initialState?.currentUser?.id;
|
||||
if (!currentUserId) return users;
|
||||
|
||||
const sortedUsers = [...users].sort((a, b) => {
|
||||
if (a.identity.current.user_id === currentUserId) return -1;
|
||||
if (b.identity.current.user_id === currentUserId) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return sortedUsers;
|
||||
};
|
||||
|
||||
const queryMetaData = async () => {
|
||||
const res = await fetchData({});
|
||||
|
||||
const sortedUsers = sortUsers(res?.filters?.users || []);
|
||||
const data = {
|
||||
models: groupToOptions(res?.filters?.models || [], {
|
||||
getGroupKey: (item) => item.identity.value.provider_name || 'gpustack',
|
||||
@@ -40,8 +55,10 @@ export default function useQueryUsageMetaData() {
|
||||
})
|
||||
}),
|
||||
users:
|
||||
res?.filters?.users?.map((item) => ({
|
||||
sortedUsers.map((item) => ({
|
||||
value: item.label,
|
||||
isCurrent:
|
||||
item.identity.current.user_id === initialState?.currentUser?.id,
|
||||
...item
|
||||
})) || [],
|
||||
api_keys: groupToOptions(res?.filters?.api_keys || [], {
|
||||
|
||||
Reference in New Issue
Block a user