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