change(ui): add filter by click selectors
This commit is contained in:
parent
f78dbb7a6a
commit
ade9253091
8 changed files with 67 additions and 24 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue