change(ui): add filter by click selectors

This commit is contained in:
sylenien 2022-12-21 10:59:45 +01:00 committed by Shekar Siri
parent f78dbb7a6a
commit ade9253091
8 changed files with 67 additions and 24 deletions

View file

@ -7,7 +7,9 @@ import { setCustomSession } from 'App/duck/sessions'
function ClickMapCard({ setCustomSession, visitedEvents }: any) {
const { metricStore } = useStore();
const onMarkerClick = (s: string) => console.log(s)
const onMarkerClick = (s: string, innerText: string) => {
metricStore.changeClickMapSearch(s, innerText)
}
React.useEffect(() => {
if (metricStore.instance.data.mobsUrl) {

View file

@ -30,7 +30,7 @@ interface Props {
function WidgetChart(props: Props) {
const { isWidget = false, metric, isTemplate } = props;
const { dashboardStore, metricStore } = useStore();
const { dashboardStore, metricStore, sessionStore } = useStore();
const _metric: any = metricStore.instance;
const period = dashboardStore.period;
const drillDownPeriod = dashboardStore.drillDownPeriod;
@ -180,7 +180,6 @@ function WidgetChart(props: Props) {
}
}
if (metricType === CLICKMAP) {
console.log(props.isPreview)
if (!props.isPreview) {
return (
<div>

View file

@ -10,6 +10,7 @@ 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";
interface Props {
className?: string;
@ -21,9 +22,9 @@ function WidgetSessions(props: Props) {
const isMounted = useIsMounted();
const [loading, setLoading] = useState(false);
const filteredSessions = getListSessionsBySeries(data, activeSeries);
const { dashboardStore, metricStore } = useStore();
const filter = useObserver(() => dashboardStore.drillDownFilter);
const widget: any = useObserver(() => metricStore.instance);
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' }]);
@ -50,30 +51,60 @@ function WidgetSessions(props: Props) {
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);
useEffect(() => {
debounceRequest(widget.metricId, {
...filter,
series: widget.toJsonDrilldown(),
page: metricStore.sessionsPage,
limit: metricStore.sessionsPageSize,
});
}, [filter.startTimestamp, filter.endTimestamp, filter.filters, depsString, metricStore.sessionsPage]);
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.toJsonDrilldown(),
page: metricStore.sessionsPage,
limit: metricStore.sessionsPageSize,
});
}
}, [filter.startTimestamp, filter.endTimestamp, filter.filters, depsString, metricStore.sessionsPage, metricStore.clickMapSearch]);
return useObserver(() => (
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-2xl">Sessions</h2>
<h2 className="text-xl">{metricStore.clickMapSearch ? 'Clicks' : 'Sessions'}</h2>
<div className="ml-2 color-gray-medium">
{metricStore.clickMapLabel ? `"${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 !== '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 />
@ -118,7 +149,7 @@ function WidgetSessions(props: Props) {
</Loader>
</div>
</div>
));
);
}
const getListSessionsBySeries = (data: any, seriesId: any) => {

View file

@ -20,6 +20,9 @@ export default class MetricStore {
sessionsPageSize: number = 10;
listView?: boolean = false
clickMapSearch = ''
clickMapLabel = ''
constructor() {
makeAutoObservable(this);
}
@ -38,6 +41,11 @@ export default class MetricStore {
this[key] = value;
}
changeClickMapSearch(val: string, label: string) {
this.clickMapSearch = val
this.clickMapLabel = label
}
merge(object: any) {
Object.assign(this.instance, object);
this.instance.updateKey('hasChanged', true);
@ -83,12 +91,12 @@ export default class MetricStore {
}
// API Communication
save(metric: Widget, dashboardId?: string): Promise<any> {
save(metric: Widget): Promise<any> {
const wasCreating = !metric.exists();
this.isSaving = true;
return new Promise((resolve, reject) => {
metricService
.saveMetric(metric, dashboardId)
.saveMetric(metric)
.then((metric: any) => {
const _metric = new Widget().fromJson(metric);
if (wasCreating) {

View file

@ -94,7 +94,7 @@ export default class SessionStore {
getSessions(filter: any): Promise<any> {
return new Promise((resolve, reject) => {
sessionService
.getSessions(filter.toJson())
.getSessions(filter.toJson?.() || filter)
.then((response: any) => {
resolve({
sessions: response.sessions.map((session: any) => new Session().fromJson(session)),

View file

@ -6,6 +6,7 @@ import Funnelissue from 'App/mstore/types/funnelIssue';
import { issueOptions } from 'App/constants/filterOptions';
import { FilterKey } from 'Types/filter/filterType';
import Period, { LAST_24_HOURS } from 'Types/app/period';
import { metricService } from "App/services";
export default class Widget {
public static get ID_KEY():string { return "metricId" }

View file

@ -176,11 +176,11 @@ export default class Screen {
return this.getElementsFromInternalPoint(this.getInternalViewportCoordinates(point));
}
getElementBySelector(selector: string): Element | null {
getElementBySelector(selector: string) {
if (!selector) return null;
try {
const safeSelector = selector.replace(/:/g, '\\\\3A ').replace(/\//g, '\\/');
return this.document?.querySelector(safeSelector) || null;
return this.document?.querySelector<HTMLElement>(safeSelector) || null;
} catch (e) {
console.error("Can not select element. ", e)
return null

View file

@ -39,7 +39,7 @@ export default class TargetMarker {
private clickMapOverlay: HTMLDivElement
private clickContainers: HTMLDivElement[] = []
private smallClicks: HTMLDivElement[] = []
private onMarkerClick: (selector: string) => void
private onMarkerClick: (selector: string, innerText: string) => void
static INITIAL_STATE: State = {
markedTargets: null,
activeTargetIndex: 0
@ -182,7 +182,8 @@ export default class TargetMarker {
border.onclick = (e) => {
e.stopPropagation()
this.onMarkerClick?.(s.selector)
const innerText = el.innerText.length > 25 ? `${el.innerText.slice(0, 20)}...` : el.innerText
this.onMarkerClick?.(s.selector, innerText)
this.clickContainers.forEach(container => {
if (container.id === containerId) {
container.style.visibility = "visible"
@ -201,6 +202,7 @@ export default class TargetMarker {
overlay.onclick = (e) => {
e.stopPropagation()
this.onMarkerClick('', '')
this.clickContainers.forEach(container => {
container.style.visibility = "hidden"
})