feat(metrics): implement server-side pagination and sorting
Refactors metrics list view to use server-side pagination and sorting instead of client-side implementation. This improves performance for large datasets by reducing client workload and network payload size. Key changes: - Add pagination API endpoint in MetricService - Update MetricStore to handle server pagination - Refactor ListView component to use server-side sorting - Remove client-side sorting and pagination logic
This commit is contained in:
parent
4b09213448
commit
687ab05f22
5 changed files with 193 additions and 256 deletions
|
|
@ -41,9 +41,9 @@ function MetricViewHeader() {
|
|||
// Show header if there are cards or if a filter is active
|
||||
const showHeader = cardsLength > 0 || isFilterActive;
|
||||
|
||||
useEffect(() => {
|
||||
metricStore.updateKey('sort', { by: 'desc' });
|
||||
}, [metricStore]);
|
||||
// useEffect(() => {
|
||||
// metricStore.updateKey('sort', { by: 'desc' });
|
||||
// }, [metricStore]);
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
metricStore.updateKey('filter', { ...filter, type: key });
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import {
|
|||
Button,
|
||||
Dropdown,
|
||||
Modal as AntdModal,
|
||||
Avatar,
|
||||
Avatar, TableColumnType
|
||||
} from 'antd';
|
||||
import {
|
||||
TeamOutlined,
|
||||
LockOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
DeleteOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { EllipsisVertical } from 'lucide-react';
|
||||
import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface';
|
||||
|
|
@ -37,90 +37,41 @@ interface Props {
|
|||
toggleSelection?: (metricId: number | number[]) => void;
|
||||
disableSelection?: boolean;
|
||||
inLibrary?: boolean;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const ListView: React.FC<Props> = ({
|
||||
list,
|
||||
siteId,
|
||||
selectedList,
|
||||
toggleSelection,
|
||||
disableSelection = false,
|
||||
inLibrary = false
|
||||
}) => {
|
||||
list,
|
||||
siteId,
|
||||
selectedList,
|
||||
toggleSelection,
|
||||
disableSelection = false,
|
||||
inLibrary = false,
|
||||
loading = false
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [sorter, setSorter] = useState<{ field: string; order: 'ascend' | 'descend' }>({
|
||||
field: 'lastModified',
|
||||
order: 'descend',
|
||||
});
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [editingMetricId, setEditingMetricId] = useState<number | null>(null);
|
||||
const [newName, setNewName] = useState('');
|
||||
const { metricStore } = useStore();
|
||||
const history = useHistory();
|
||||
|
||||
const sortedData = useMemo(
|
||||
() =>
|
||||
[...list].sort((a, b) => {
|
||||
if (sorter.field === 'lastModified') {
|
||||
return sorter.order === 'ascend'
|
||||
? new Date(a.lastModified).getTime() -
|
||||
new Date(b.lastModified).getTime()
|
||||
: new Date(b.lastModified).getTime() -
|
||||
new Date(a.lastModified).getTime();
|
||||
}
|
||||
if (sorter.field === 'name') {
|
||||
return sorter.order === 'ascend'
|
||||
? a.name?.localeCompare(b.name) || 0
|
||||
: b.name?.localeCompare(a.name) || 0;
|
||||
}
|
||||
if (sorter.field === 'owner') {
|
||||
return sorter.order === 'ascend'
|
||||
? a.owner?.localeCompare(b.owner) || 0
|
||||
: b.owner?.localeCompare(a.owner) || 0;
|
||||
}
|
||||
return 0;
|
||||
}),
|
||||
[list, sorter],
|
||||
);
|
||||
|
||||
const paginatedData = useMemo(() => {
|
||||
const start = ((pagination.current || 1) - 1) * (pagination.pageSize || 10);
|
||||
return sortedData.slice(start, start + (pagination.pageSize || 10));
|
||||
}, [sortedData, pagination]);
|
||||
|
||||
const totalMessage = (
|
||||
<>
|
||||
{t('Showing')}{' '}
|
||||
<Text strong>
|
||||
{(pagination.pageSize || 10) * ((pagination.current || 1) - 1) + 1}
|
||||
{(metricStore.pageSize || 10) * ((metricStore.page || 1) - 1) + 1}
|
||||
</Text>{' '}
|
||||
{t('to')}{' '}
|
||||
<Text strong>
|
||||
{Math.min(
|
||||
(pagination.pageSize || 10) * (pagination.current || 1),
|
||||
list.length,
|
||||
(metricStore.pageSize || 10) * (metricStore.page || 1),
|
||||
list.length
|
||||
)}
|
||||
</Text>{' '}
|
||||
{t('of')} <Text strong>{list.length}</Text> {t('cards')}
|
||||
</>
|
||||
);
|
||||
|
||||
const handleTableChange = (
|
||||
pag: TablePaginationConfig,
|
||||
_filters: Record<string, (string | number | boolean)[] | null>,
|
||||
sorterParam: SorterResult<Widget> | SorterResult<Widget>[],
|
||||
) => {
|
||||
const sortRes = sorterParam as SorterResult<Widget>;
|
||||
setSorter({
|
||||
field: sortRes.field as string,
|
||||
order: sortRes.order as 'ascend' | 'descend',
|
||||
});
|
||||
setPagination(pag);
|
||||
};
|
||||
|
||||
const parseDate = (dateString: string) => {
|
||||
let date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) {
|
||||
|
|
@ -182,7 +133,7 @@ const ListView: React.FC<Props> = ({
|
|||
cancelText: t('No'),
|
||||
onOk: async () => {
|
||||
await metricStore.delete(metric);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
if (key === 'rename') {
|
||||
|
|
@ -206,7 +157,7 @@ const ListView: React.FC<Props> = ({
|
|||
|
||||
const menuItems = [
|
||||
{ key: 'rename', icon: <EditOutlined />, label: t('Rename') },
|
||||
{ key: 'delete', icon: <DeleteOutlined />, label: t('Delete') },
|
||||
{ key: 'delete', icon: <DeleteOutlined />, label: t('Delete') }
|
||||
];
|
||||
|
||||
const renderTitle = (_text: string, metric: Widget) => (
|
||||
|
|
@ -245,80 +196,109 @@ const ListView: React.FC<Props> = ({
|
|||
</div>
|
||||
);
|
||||
|
||||
const columns = [
|
||||
const columns: TableColumnType<any>[] = [
|
||||
{
|
||||
title: t('Title'),
|
||||
dataIndex: 'name',
|
||||
key: 'title',
|
||||
className: 'cap-first pl-4',
|
||||
sorter: true,
|
||||
sortOrder: metricStore.sort.field === 'name' ? metricStore.sort.order : undefined,
|
||||
width: inLibrary ? '31%' : '25%',
|
||||
render: renderTitle,
|
||||
render: renderTitle
|
||||
},
|
||||
{
|
||||
title: t('Owner'),
|
||||
dataIndex: 'owner',
|
||||
dataIndex: 'owner_email',
|
||||
key: 'owner',
|
||||
className: 'capitalize',
|
||||
sorter: true,
|
||||
sortOrder: metricStore.sort.field === 'owner_email' ? metricStore.sort.order : undefined,
|
||||
width: inLibrary ? '31%' : '25%',
|
||||
render: renderOwner,
|
||||
render: renderOwner
|
||||
},
|
||||
{
|
||||
title: t('Last Modified'),
|
||||
dataIndex: 'lastModified',
|
||||
dataIndex: 'edited_at',
|
||||
key: 'lastModified',
|
||||
sorter: true,
|
||||
sortOrder: metricStore.sort.field === 'edited_at' ? metricStore.sort.order : undefined,
|
||||
width: inLibrary ? '31%' : '25%',
|
||||
render: renderLastModified,
|
||||
},
|
||||
render: renderLastModified
|
||||
}
|
||||
];
|
||||
|
||||
if (!inLibrary) {
|
||||
columns.push({
|
||||
title: '',
|
||||
key: 'options',
|
||||
className: 'text-right',
|
||||
width: '5%',
|
||||
render: renderOptions,
|
||||
render: renderOptions
|
||||
});
|
||||
}
|
||||
|
||||
// if (metricStore.sort.field) {
|
||||
// columns.forEach((col) => {
|
||||
// col.sortOrder = col.key === metricStore.sort.field ? metricStore.sort.order : false;
|
||||
// });
|
||||
// }
|
||||
|
||||
console.log('store', metricStore.sort);
|
||||
|
||||
const handleTableChange = (
|
||||
pag: TablePaginationConfig,
|
||||
_filters: Record<string, (string | number | boolean)[] | null>,
|
||||
sorterParam: SorterResult<Widget> | SorterResult<Widget>[]
|
||||
) => {
|
||||
const sorter = Array.isArray(sorterParam) ? sorterParam[0] : sorterParam;
|
||||
let order = sorter.order;
|
||||
if (metricStore.sort.field === sorter.field) {
|
||||
order = metricStore.sort.order === 'ascend' ? 'descend' : 'ascend';
|
||||
}
|
||||
console.log('sorter', { field: sorter.field, order });
|
||||
metricStore.updateKey('sort', { field: sorter.field, order });
|
||||
metricStore.updateKey('page', pag.current || 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={paginatedData}
|
||||
dataSource={list}
|
||||
rowKey="metricId"
|
||||
showSorterTooltip={false}
|
||||
onChange={handleTableChange}
|
||||
sortDirections={['ascend', 'descend']}
|
||||
onRow={
|
||||
inLibrary
|
||||
? (record) => ({
|
||||
onClick: () => {
|
||||
if (!disableSelection) toggleSelection?.(record?.metricId);
|
||||
},
|
||||
})
|
||||
onClick: () => {
|
||||
if (!disableSelection) toggleSelection?.(record?.metricId);
|
||||
}
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
rowSelection={
|
||||
!disableSelection
|
||||
? {
|
||||
selectedRowKeys: selectedList,
|
||||
onChange: (keys) => toggleSelection && toggleSelection(keys),
|
||||
columnWidth: 16,
|
||||
}
|
||||
selectedRowKeys: selectedList,
|
||||
onChange: (keys) => toggleSelection && toggleSelection(keys),
|
||||
columnWidth: 16
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: sortedData.length,
|
||||
current: metricStore.page,
|
||||
pageSize: metricStore.pageSize,
|
||||
total: metricStore.total,
|
||||
showSizeChanger: false,
|
||||
className: 'px-4',
|
||||
showLessItems: true,
|
||||
showTotal: () => totalMessage,
|
||||
size: 'small',
|
||||
simple: true,
|
||||
simple: true
|
||||
}}
|
||||
/>
|
||||
<AntdModal
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@ import { sliceListPerPage } from 'App/utils';
|
|||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import { Popover, Button } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import GridView from './GridView';
|
||||
import ListView from './ListView';
|
||||
import AddCardSection from '../AddCardSection/AddCardSection';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function MetricsList({
|
||||
siteId,
|
||||
onSelectionChange,
|
||||
inLibrary,
|
||||
}: {
|
||||
siteId,
|
||||
onSelectionChange,
|
||||
inLibrary
|
||||
}: {
|
||||
siteId: string;
|
||||
onSelectionChange?: (selected: any[]) => void;
|
||||
inLibrary?: boolean;
|
||||
|
|
@ -23,28 +22,27 @@ function MetricsList({
|
|||
const { t } = useTranslation();
|
||||
const { metricStore, dashboardStore } = useStore();
|
||||
const metricsSearch = metricStore.filter.query;
|
||||
const listView = inLibrary ? true : metricStore.listView;
|
||||
const [selectedMetrics, setSelectedMetrics] = useState<any>([]);
|
||||
|
||||
const dashboard = dashboardStore.selectedDashboard;
|
||||
const existingCardIds = useMemo(
|
||||
() => dashboard?.widgets?.map((i) => parseInt(i.metricId)),
|
||||
[dashboard],
|
||||
[dashboard]
|
||||
);
|
||||
const cards = useMemo(
|
||||
() =>
|
||||
onSelectionChange
|
||||
? metricStore.filteredCards.filter(
|
||||
(i) => !existingCardIds?.includes(parseInt(i.metricId)),
|
||||
)
|
||||
(i) => !existingCardIds?.includes(parseInt(i.metricId))
|
||||
)
|
||||
: metricStore.filteredCards,
|
||||
[metricStore.filteredCards, existingCardIds, onSelectionChange],
|
||||
[metricStore.filteredCards, existingCardIds, onSelectionChange]
|
||||
);
|
||||
const loading = metricStore.isLoading;
|
||||
|
||||
useEffect(() => {
|
||||
void metricStore.fetchList();
|
||||
}, [metricStore]);
|
||||
}, [metricStore.page, metricStore.filter, metricStore.sort]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onSelectionChange) return;
|
||||
|
|
@ -69,14 +67,8 @@ function MetricsList({
|
|||
metricStore.updateKey('sessionsPage', 1);
|
||||
}, [metricStore]);
|
||||
|
||||
const showOwn = metricStore.filter.showMine;
|
||||
const toggleOwn = () => {
|
||||
metricStore.updateKey('showMine', !showOwn);
|
||||
};
|
||||
|
||||
const isFiltered =
|
||||
metricsSearch !== '' ||
|
||||
(metricStore.filter.type && metricStore.filter.type !== 'all');
|
||||
const isFiltered = metricStore.filter.query !== '' || metricStore.filter.type !== 'all';
|
||||
|
||||
const searchImageDimensions = { width: 60, height: 'auto' };
|
||||
const defaultImageDimensions = { width: 600, height: 'auto' };
|
||||
|
|
@ -86,101 +78,65 @@ function MetricsList({
|
|||
: defaultImageDimensions;
|
||||
|
||||
return (
|
||||
<Loader loading={loading}>
|
||||
<NoContent
|
||||
show={length === 0}
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={emptyImage} size={imageDimensions.width} />
|
||||
<div className="text-center mt-3 text-lg font-medium">
|
||||
{isFiltered
|
||||
? t('No matching results')
|
||||
: t('Unlock insights with data cards')}
|
||||
</div>
|
||||
<NoContent
|
||||
show={!loading && length === 0}
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={emptyImage} size={imageDimensions.width} />
|
||||
<div className="text-center mt-3 text-lg font-medium">
|
||||
{isFiltered
|
||||
? t('No matching results')
|
||||
: t('Unlock insights with data cards')}
|
||||
</div>
|
||||
}
|
||||
subtext={
|
||||
isFiltered ? (
|
||||
''
|
||||
) : (
|
||||
<div className="flex flex-col items-center">
|
||||
<div>
|
||||
{t('Create and customize cards to analyze trends and user behavior effectively.')}
|
||||
</div>
|
||||
<Popover
|
||||
arrow={false}
|
||||
overlayInnerStyle={{ padding: 0, borderRadius: '0.75rem' }}
|
||||
content={<AddCardSection fit inCards />}
|
||||
trigger="click"
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
className="btn-create-card mt-3"
|
||||
>
|
||||
{t('Create Card')}
|
||||
</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
{listView ? (
|
||||
<ListView
|
||||
disableSelection={!onSelectionChange}
|
||||
siteId={siteId}
|
||||
list={cards}
|
||||
inLibrary={inLibrary}
|
||||
selectedList={selectedMetrics}
|
||||
existingCardIds={existingCardIds}
|
||||
toggleSelection={toggleMetricSelection}
|
||||
allSelected={cards.length === selectedMetrics.length}
|
||||
showOwn={showOwn}
|
||||
toggleOwn={toggleOwn}
|
||||
toggleAll={({ target: { checked } }) =>
|
||||
setSelectedMetrics(
|
||||
checked
|
||||
? cards
|
||||
.map((i: any) => i.metricId)
|
||||
.slice(0, 30 - (existingCardIds?.length || 0))
|
||||
: [],
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
subtext={
|
||||
isFiltered ? (
|
||||
''
|
||||
) : (
|
||||
<>
|
||||
<GridView
|
||||
siteId={siteId}
|
||||
list={sliceListPerPage(
|
||||
cards,
|
||||
metricStore.page - 1,
|
||||
metricStore.pageSize,
|
||||
)}
|
||||
selectedList={selectedMetrics}
|
||||
toggleSelection={toggleMetricSelection}
|
||||
/>
|
||||
<div className="w-full flex items-center justify-between py-4 px-6 border-t">
|
||||
<div>
|
||||
{t('Showing')}{' '}
|
||||
<span className="font-medium">
|
||||
{Math.min(cards.length, metricStore.pageSize)}
|
||||
</span>{' '}
|
||||
{t('out of')}
|
||||
<span className="font-medium">{cards.length}</span>
|
||||
{t('cards')}
|
||||
</div>
|
||||
<Pagination
|
||||
page={metricStore.page}
|
||||
total={length}
|
||||
onPageChange={(page) => metricStore.updateKey('page', page)}
|
||||
limit={metricStore.pageSize}
|
||||
debounceRequest={100}
|
||||
/>
|
||||
<div className="flex flex-col items-center">
|
||||
<div>
|
||||
{t('Create and customize cards to analyze trends and user behavior effectively.')}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</NoContent>
|
||||
</Loader>
|
||||
<Popover
|
||||
arrow={false}
|
||||
overlayInnerStyle={{ padding: 0, borderRadius: '0.75rem' }}
|
||||
content={<AddCardSection fit inCards />}
|
||||
trigger="click"
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
className="btn-create-card mt-3"
|
||||
>
|
||||
{t('Create Card')}
|
||||
</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
<ListView
|
||||
loading={loading}
|
||||
disableSelection={!onSelectionChange}
|
||||
siteId={siteId}
|
||||
list={cards}
|
||||
inLibrary={inLibrary}
|
||||
selectedList={selectedMetrics}
|
||||
// existingCardIds={existingCardIds}
|
||||
toggleSelection={toggleMetricSelection}
|
||||
// allSelected={cards.length === selectedMetrics.length}
|
||||
// toggleAll={({ target: { checked } }) =>
|
||||
// setSelectedMetrics(
|
||||
// checked
|
||||
// ? cards
|
||||
// .map((i: any) => i.metricId)
|
||||
// .slice(0, 30 - (existingCardIds?.length || 0))
|
||||
// : []
|
||||
// )
|
||||
// }
|
||||
/>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
HEATMAP,
|
||||
USER_PATH,
|
||||
RETENTION,
|
||||
CATEGORIES,
|
||||
CATEGORIES
|
||||
} from 'App/constants/card';
|
||||
import { clickmapFilter } from 'App/types/filter/newFilter';
|
||||
import { getRE } from 'App/utils';
|
||||
|
|
@ -31,7 +31,7 @@ const handleFilter = (card: Widget, filterType?: string) => {
|
|||
FilterKey.ERRORS,
|
||||
FilterKey.FETCH,
|
||||
`${TIMESERIES}_4xx_requests`,
|
||||
`${TIMESERIES}_slow_network_requests`,
|
||||
`${TIMESERIES}_slow_network_requests`
|
||||
].includes(metricOf);
|
||||
}
|
||||
if (filterType === CATEGORIES.web_analytics) {
|
||||
|
|
@ -41,7 +41,7 @@ const handleFilter = (card: Widget, filterType?: string) => {
|
|||
FilterKey.REFERRER,
|
||||
FilterKey.USERID,
|
||||
FilterKey.LOCATION,
|
||||
FilterKey.USER_DEVICE,
|
||||
FilterKey.USER_DEVICE
|
||||
].includes(metricOf);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -75,58 +75,42 @@ interface MetricFilter {
|
|||
query?: string;
|
||||
showMine?: boolean;
|
||||
type?: string;
|
||||
dashboard?: [];
|
||||
// dashboard?: [];
|
||||
}
|
||||
|
||||
export default class MetricStore {
|
||||
isLoading: boolean = false;
|
||||
|
||||
isSaving: boolean = false;
|
||||
|
||||
metrics: Widget[] = [];
|
||||
|
||||
instance = new Widget();
|
||||
|
||||
page: number = 1;
|
||||
|
||||
total: number = 0;
|
||||
pageSize: number = 10;
|
||||
|
||||
metricsSearch: string = '';
|
||||
|
||||
sort: any = { by: 'desc' };
|
||||
|
||||
filter: MetricFilter = { type: 'all', dashboard: [], query: '' };
|
||||
|
||||
sort: any = { columnKey: '', field: '', order: false };
|
||||
filter: any = { type: '', query: '' };
|
||||
sessionsPage: number = 1;
|
||||
|
||||
sessionsPageSize: number = 10;
|
||||
|
||||
listView?: boolean = true;
|
||||
|
||||
clickMapFilter: boolean = false;
|
||||
|
||||
clickMapSearch = '';
|
||||
|
||||
clickMapLabel = '';
|
||||
|
||||
cardCategory: string | null = CATEGORIES.product_analytics;
|
||||
|
||||
focusedSeriesName: string | null = null;
|
||||
|
||||
disabledSeries: string[] = [];
|
||||
|
||||
drillDown = false;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
get sortedWidgets() {
|
||||
return [...this.metrics].sort((a, b) =>
|
||||
this.sort.by === 'desc'
|
||||
? b.lastModified - a.lastModified
|
||||
: a.lastModified - b.lastModified,
|
||||
);
|
||||
}
|
||||
// get sortedWidgets() {
|
||||
// return [...this.metrics].sort((a, b) =>
|
||||
// this.sort.by === 'desc'
|
||||
// ? b.lastModified - a.lastModified
|
||||
// : a.lastModified - b.lastModified
|
||||
// );
|
||||
// }
|
||||
|
||||
get filteredCards() {
|
||||
const filterRE = this.filter.query ? getRE(this.filter.query, 'i') : null;
|
||||
|
|
@ -138,7 +122,7 @@ export default class MetricStore {
|
|||
(card) =>
|
||||
(this.filter.showMine
|
||||
? card.owner ===
|
||||
JSON.parse(localStorage.getItem('user')!).account.email
|
||||
JSON.parse(localStorage.getItem('user')!).account.email
|
||||
: true) &&
|
||||
handleFilter(card, this.filter.type) &&
|
||||
(!dbIds.length ||
|
||||
|
|
@ -147,13 +131,13 @@ export default class MetricStore {
|
|||
.some((id) => dbIds.includes(id))) &&
|
||||
// @ts-ignore
|
||||
(!filterRE ||
|
||||
['name', 'owner'].some((key) => filterRE.test(card[key]))),
|
||||
)
|
||||
.sort((a, b) =>
|
||||
this.sort.by === 'desc'
|
||||
? b.lastModified - a.lastModified
|
||||
: a.lastModified - b.lastModified,
|
||||
['name', 'owner'].some((key) => filterRE.test(card[key])))
|
||||
);
|
||||
// .sort((a, b) =>
|
||||
// this.sort.by === 'desc'
|
||||
// ? b.lastModified - a.lastModified
|
||||
// : a.lastModified - b.lastModified
|
||||
// );
|
||||
}
|
||||
|
||||
// State Actions
|
||||
|
|
@ -182,6 +166,7 @@ export default class MetricStore {
|
|||
}
|
||||
|
||||
updateKey(key: string, value: any) {
|
||||
console.log('key', key, value);
|
||||
// @ts-ignore
|
||||
this[key] = value;
|
||||
|
||||
|
|
@ -207,7 +192,7 @@ export default class MetricStore {
|
|||
this.instance.series[i].filter.eventsOrderSupport = [
|
||||
'then',
|
||||
'or',
|
||||
'and',
|
||||
'and'
|
||||
];
|
||||
});
|
||||
if (type === HEATMAP && 'series' in obj) {
|
||||
|
|
@ -254,7 +239,7 @@ export default class MetricStore {
|
|||
namesMap: {},
|
||||
avg: 0,
|
||||
percentiles: [],
|
||||
values: [],
|
||||
values: []
|
||||
};
|
||||
const obj: any = { metricType: value, data: defaultData };
|
||||
obj.series = this.instance.series;
|
||||
|
|
@ -311,7 +296,7 @@ export default class MetricStore {
|
|||
if (obj.series[0] && obj.series[0].filter.filters.length < 1) {
|
||||
obj.series[0].filter.addFilter({
|
||||
...clickmapFilter,
|
||||
value: [''],
|
||||
value: ['']
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -341,7 +326,7 @@ export default class MetricStore {
|
|||
updateInList(metric: Widget) {
|
||||
// @ts-ignore
|
||||
const index = this.metrics.findIndex(
|
||||
(m: Widget) => m[Widget.ID_KEY] === metric[Widget.ID_KEY],
|
||||
(m: Widget) => m[Widget.ID_KEY] === metric[Widget.ID_KEY]
|
||||
);
|
||||
if (index >= 0) {
|
||||
this.metrics[index] = metric;
|
||||
|
|
@ -358,12 +343,6 @@ export default class MetricStore {
|
|||
this.metrics = this.metrics.filter((m) => m[Widget.ID_KEY] !== id);
|
||||
}
|
||||
|
||||
get paginatedList(): Widget[] {
|
||||
const start = (this.page - 1) * this.pageSize;
|
||||
const end = start + this.pageSize;
|
||||
return this.metrics.slice(start, end);
|
||||
}
|
||||
|
||||
// API Communication
|
||||
async save(metric: Widget): Promise<Widget> {
|
||||
this.isSaving = true;
|
||||
|
|
@ -396,16 +375,27 @@ export default class MetricStore {
|
|||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
fetchList() {
|
||||
async fetchList() {
|
||||
this.setLoading(true);
|
||||
return metricService
|
||||
.getMetrics()
|
||||
.then((metrics: any[]) => {
|
||||
this.setMetrics(metrics.map((m) => new Widget().fromJson(m)));
|
||||
})
|
||||
.finally(() => {
|
||||
this.setLoading(false);
|
||||
});
|
||||
try {
|
||||
const resp = await metricService
|
||||
.getMetricsPaginated({
|
||||
page: this.page,
|
||||
limit: this.pageSize,
|
||||
sort: {
|
||||
field: this.sort.field,
|
||||
order: this.sort.order === 'ascend' ? 'asc' : 'desc'
|
||||
},
|
||||
filter: {
|
||||
query: this.filter.query,
|
||||
type: this.filter.type === 'all' ? '' : this.filter.type,
|
||||
}
|
||||
});
|
||||
this.total = resp.total;
|
||||
this.setMetrics(resp.list.map((m) => new Widget().fromJson(m)));
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetch(id: string, period?: any) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,17 @@ export default class MetricService {
|
|||
.then((response: { data: any }) => response.data || []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all metrics paginated.
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getMetricsPaginated(params: any): Promise<any> {
|
||||
return this.client
|
||||
.post('/cards/search', params)
|
||||
.then((response: { json: () => any }) => response.json())
|
||||
.then((response: { data: any }) => response.data || []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a metric by metricId.
|
||||
* @param metricId
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue