fix(ui) - widget sessions pagination
This commit is contained in:
parent
92674c3e6d
commit
bd10225364
1 changed files with 173 additions and 150 deletions
|
|
@ -10,168 +10,191 @@ import { debounce } from 'App/utils';
|
|||
import useIsMounted from 'App/hooks/useIsMounted';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import { numberWithCommas } from 'App/utils';
|
||||
import { CLICKMAP } from "App/constants/card";
|
||||
import { CLICKMAP } from 'App/constants/card';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
className?: string;
|
||||
}
|
||||
function WidgetSessions(props: Props) {
|
||||
const { className = '' } = props;
|
||||
const [activeSeries, setActiveSeries] = useState('all');
|
||||
const [data, setData] = useState<any>([]);
|
||||
const isMounted = useIsMounted();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const filteredSessions = getListSessionsBySeries(data, activeSeries);
|
||||
const { dashboardStore, metricStore, sessionStore } = useStore();
|
||||
const filter = dashboardStore.drillDownFilter;
|
||||
const widget = metricStore.instance;
|
||||
const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm');
|
||||
const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm');
|
||||
const [seriesOptions, setSeriesOptions] = useState([{ label: 'All', value: 'all' }]);
|
||||
const { className = '' } = props;
|
||||
const [activeSeries, setActiveSeries] = useState('all');
|
||||
const [data, setData] = useState<any>([]);
|
||||
const isMounted = useIsMounted();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const filteredSessions = getListSessionsBySeries(data, activeSeries);
|
||||
const { dashboardStore, metricStore, sessionStore } = useStore();
|
||||
const filter = dashboardStore.drillDownFilter;
|
||||
const widget = metricStore.instance;
|
||||
const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm');
|
||||
const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm');
|
||||
const [seriesOptions, setSeriesOptions] = useState([{ label: 'All', value: 'all' }]);
|
||||
|
||||
const writeOption = ({ value }: any) => setActiveSeries(value.value);
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
const seriesOptions = data.map((item: any) => ({
|
||||
label: item.seriesName,
|
||||
value: item.seriesId,
|
||||
}));
|
||||
setSeriesOptions([{ label: 'All', value: 'all' }, ...seriesOptions]);
|
||||
}, [data]);
|
||||
const writeOption = ({ value }: any) => setActiveSeries(value.value);
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
const seriesOptions = data.map((item: any) => ({
|
||||
label: item.seriesName,
|
||||
value: item.seriesId,
|
||||
}));
|
||||
setSeriesOptions([{ label: 'All', value: 'all' }, ...seriesOptions]);
|
||||
}, [data]);
|
||||
|
||||
const fetchSessions = (metricId: any, filter: any) => {
|
||||
if (!isMounted()) return;
|
||||
setLoading(true);
|
||||
delete filter.eventsOrderSupport;
|
||||
widget
|
||||
.fetchSessions(metricId, filter)
|
||||
.then((res: any) => {
|
||||
setData(res);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
const fetchClickmapSessions = (customFilters: Record<string, any>) => {
|
||||
sessionStore.getSessions(customFilters)
|
||||
.then(data => {
|
||||
setData([{ ...data, seriesId: 1 , seriesName: "Clicks" }])
|
||||
})
|
||||
const fetchSessions = (metricId: any, filter: any) => {
|
||||
if (!isMounted()) return;
|
||||
setLoading(true);
|
||||
delete filter.eventsOrderSupport;
|
||||
widget
|
||||
.fetchSessions(metricId, filter)
|
||||
.then((res: any) => {
|
||||
setData(res);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
const fetchClickmapSessions = (customFilters: Record<string, any>) => {
|
||||
sessionStore.getSessions(customFilters).then((data) => {
|
||||
setData([{ ...data, seriesId: 1, seriesName: 'Clicks' }]);
|
||||
});
|
||||
};
|
||||
const debounceRequest: any = React.useCallback(debounce(fetchSessions, 1000), []);
|
||||
const debounceClickMapSearch = React.useCallback(debounce(fetchClickmapSessions, 1000), []);
|
||||
|
||||
const depsString = JSON.stringify(widget.series);
|
||||
|
||||
const loadData = () => {
|
||||
if (widget.metricType === CLICKMAP && metricStore.clickMapSearch) {
|
||||
const clickFilter = {
|
||||
value: [metricStore.clickMapSearch],
|
||||
type: 'CLICK',
|
||||
operator: 'onSelector',
|
||||
isEvent: true,
|
||||
// @ts-ignore
|
||||
filters: [],
|
||||
};
|
||||
const timeRange = {
|
||||
rangeValue: dashboardStore.drillDownPeriod.rangeValue,
|
||||
startDate: dashboardStore.drillDownPeriod.start,
|
||||
endDate: dashboardStore.drillDownPeriod.end,
|
||||
};
|
||||
const customFilter = {
|
||||
...filter,
|
||||
...timeRange,
|
||||
filters: [...sessionStore.userFilter.filters, clickFilter],
|
||||
};
|
||||
debounceClickMapSearch(customFilter);
|
||||
} else {
|
||||
debounceRequest(widget.metricId, {
|
||||
...filter,
|
||||
series: widget.series.map((s) => s.toJson()),
|
||||
page: metricStore.sessionsPage,
|
||||
limit: metricStore.sessionsPageSize,
|
||||
});
|
||||
}
|
||||
const debounceRequest: any = React.useCallback(debounce(fetchSessions, 1000), []);
|
||||
const debounceClickMapSearch = React.useCallback(debounce(fetchClickmapSessions, 1000), [])
|
||||
};
|
||||
useEffect(() => {
|
||||
metricStore.updateKey('sessionsPage', 1);
|
||||
loadData();
|
||||
}, [
|
||||
filter.startTimestamp,
|
||||
filter.endTimestamp,
|
||||
filter.filters,
|
||||
depsString,
|
||||
metricStore.clickMapSearch,
|
||||
activeSeries,
|
||||
]);
|
||||
useEffect(loadData, [metricStore.sessionsPage]);
|
||||
|
||||
const depsString = JSON.stringify(widget.series);
|
||||
|
||||
const loadData = () => {
|
||||
if (widget.metricType === CLICKMAP && metricStore.clickMapSearch) {
|
||||
const clickFilter = {
|
||||
value: [
|
||||
metricStore.clickMapSearch
|
||||
],
|
||||
type: "CLICK",
|
||||
operator: "onSelector",
|
||||
isEvent: true,
|
||||
// @ts-ignore
|
||||
"filters": []
|
||||
}
|
||||
const timeRange = {
|
||||
rangeValue: dashboardStore.drillDownPeriod.rangeValue,
|
||||
startDate: dashboardStore.drillDownPeriod.start,
|
||||
endDate: dashboardStore.drillDownPeriod.end,
|
||||
}
|
||||
const customFilter = { ...filter, ...timeRange, filters: [ ...sessionStore.userFilter.filters, clickFilter]}
|
||||
debounceClickMapSearch(customFilter)
|
||||
} else {
|
||||
debounceRequest(widget.metricId, {
|
||||
...filter,
|
||||
series: widget.series.map(s => s.toJson()),
|
||||
page: metricStore.sessionsPage,
|
||||
limit: metricStore.sessionsPageSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
metricStore.updateKey('sessionsPage', 1);
|
||||
loadData();
|
||||
}, [filter.startTimestamp, filter.endTimestamp, filter.filters, depsString, metricStore.clickMapSearch, activeSeries]);
|
||||
useEffect(loadData, [metricStore.sessionsPage]);
|
||||
|
||||
return (
|
||||
<div className={cn(className, "bg-white p-3 pb-0 rounded border")}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-baseline">
|
||||
<h2 className="text-xl">{metricStore.clickMapSearch ? 'Clicks' : 'Sessions'}</h2>
|
||||
<div className="ml-2 color-gray-medium">
|
||||
{metricStore.clickMapLabel ? `on "${metricStore.clickMapLabel}" ` : null}
|
||||
between <span className="font-medium color-gray-darkest">{startTime}</span> and{' '}
|
||||
<span className="font-medium color-gray-darkest">{endTime}</span>{' '}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{widget.metricType !== 'table' && widget.metricType !== CLICKMAP && (
|
||||
<div className="flex items-center ml-6">
|
||||
<span className="mr-2 color-gray-medium">Filter by Series</span>
|
||||
<Select options={seriesOptions} defaultValue={'all'} onChange={writeOption} plain />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Loader loading={loading}>
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex items-center justify-center flex-col">
|
||||
<AnimatedSVG name={ICONS.NO_SESSIONS} size={170} />
|
||||
<div className="mt-2" />
|
||||
<div className="text-center text-gray-600">No relevant sessions found for the selected time period.</div>
|
||||
</div>
|
||||
}
|
||||
show={filteredSessions.sessions.length === 0}
|
||||
>
|
||||
{filteredSessions.sessions.map((session: any) => (
|
||||
<React.Fragment key={session.sessionId}>
|
||||
<SessionItem session={session} />
|
||||
<div className="border-b" />
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
<div className="flex items-center justify-between p-5">
|
||||
<div>
|
||||
Showing <span className="font-medium">{(metricStore.sessionsPage - 1) * metricStore.sessionsPageSize + 1}</span> to{' '}
|
||||
<span className="font-medium">{(metricStore.sessionsPage - 1) * metricStore.sessionsPageSize + filteredSessions.sessions.length}</span> of{' '}
|
||||
<span className="font-medium">{numberWithCommas(filteredSessions.total)}</span> sessions.
|
||||
</div>
|
||||
<Pagination
|
||||
page={metricStore.sessionsPage}
|
||||
totalPages={Math.ceil(filteredSessions.total / metricStore.sessionsPageSize)}
|
||||
onPageChange={(page: any) => metricStore.updateKey('sessionsPage', page)}
|
||||
limit={metricStore.sessionsPageSize}
|
||||
debounceRequest={500}
|
||||
/>
|
||||
</div>
|
||||
</NoContent>
|
||||
</Loader>
|
||||
</div>
|
||||
return (
|
||||
<div className={cn(className, 'bg-white p-3 pb-0 rounded border')}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-baseline">
|
||||
<h2 className="text-xl">{metricStore.clickMapSearch ? 'Clicks' : 'Sessions'}</h2>
|
||||
<div className="ml-2 color-gray-medium">
|
||||
{metricStore.clickMapLabel ? `on "${metricStore.clickMapLabel}" ` : null}
|
||||
between <span className="font-medium color-gray-darkest">{startTime}</span> and{' '}
|
||||
<span className="font-medium color-gray-darkest">{endTime}</span>{' '}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
{widget.metricType !== 'table' && widget.metricType !== CLICKMAP && (
|
||||
<div className="flex items-center ml-6">
|
||||
<span className="mr-2 color-gray-medium">Filter by Series</span>
|
||||
<Select options={seriesOptions} defaultValue={'all'} onChange={writeOption} plain />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Loader loading={loading}>
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex items-center justify-center flex-col">
|
||||
<AnimatedSVG name={ICONS.NO_SESSIONS} size={170} />
|
||||
<div className="mt-2" />
|
||||
<div className="text-center text-gray-600">
|
||||
No relevant sessions found for the selected time period.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
show={filteredSessions.sessions.length === 0}
|
||||
>
|
||||
{filteredSessions.sessions.map((session: any) => (
|
||||
<React.Fragment key={session.sessionId}>
|
||||
<SessionItem session={session} />
|
||||
<div className="border-b" />
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
<div className="flex items-center justify-between p-5">
|
||||
<div>
|
||||
Showing{' '}
|
||||
<span className="font-medium">
|
||||
{(metricStore.sessionsPage - 1) * metricStore.sessionsPageSize + 1}
|
||||
</span>{' '}
|
||||
to{' '}
|
||||
<span className="font-medium">
|
||||
{(metricStore.sessionsPage - 1) * metricStore.sessionsPageSize +
|
||||
filteredSessions.sessions.length}
|
||||
</span>{' '}
|
||||
of <span className="font-medium">{numberWithCommas(filteredSessions.total)}</span>{' '}
|
||||
sessions.
|
||||
</div>
|
||||
<Pagination
|
||||
page={metricStore.sessionsPage}
|
||||
totalPages={Math.ceil(filteredSessions.total / metricStore.sessionsPageSize)}
|
||||
onPageChange={(page: any) => metricStore.updateKey('sessionsPage', page)}
|
||||
limit={metricStore.sessionsPageSize}
|
||||
debounceRequest={500}
|
||||
/>
|
||||
</div>
|
||||
</NoContent>
|
||||
</Loader>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getListSessionsBySeries = (data: any, seriesId: any) => {
|
||||
const arr: any = { sessions: [], total: 0 };
|
||||
data.forEach((element: any) => {
|
||||
if (seriesId === 'all') {
|
||||
const sessionIds = arr.sessions.map((i: any) => i.sessionId);
|
||||
arr.sessions.push(...element.sessions.filter((i: any) => !sessionIds.includes(i.sessionId)));
|
||||
arr.total = element.total;
|
||||
} else {
|
||||
if (element.seriesId === seriesId) {
|
||||
arr.sessions.push(...element.sessions);
|
||||
arr.total = element.total;
|
||||
}
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
const arr = data.reduce(
|
||||
(arr: any, element: any) => {
|
||||
if (seriesId === 'all') {
|
||||
const sessionIds = arr.sessions.map((i: any) => i.sessionId);
|
||||
const sessions = element.sessions.filter((i: any) => !sessionIds.includes(i.sessionId));
|
||||
arr.sessions.push(...sessions);
|
||||
} else if (element.seriesId === seriesId) {
|
||||
const sessionIds = arr.sessions.map((i: any) => i.sessionId);
|
||||
const sessions = element.sessions.filter((i: any) => !sessionIds.includes(i.sessionId));
|
||||
const duplicates = element.sessions.length - sessions.length;
|
||||
arr.sessions.push(...sessions);
|
||||
arr.total = element.total - duplicates;
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
{ sessions: [] }
|
||||
);
|
||||
arr.total = seriesId === 'all' ? Math.max(...data.map((i: any) => i.total)) : data.find((i: any) => i.seriesId === seriesId).total;
|
||||
return arr;
|
||||
};
|
||||
|
||||
export default observer(WidgetSessions);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue