feat(ui) - cards - metric search and other changes

This commit is contained in:
Shekar Siri 2023-01-02 13:43:56 +01:00
parent 9c86c8f4fc
commit 6ff4d74131
9 changed files with 72 additions and 24 deletions

View file

@ -5,6 +5,7 @@ import MetricTypeItem, { MetricType } from '../MetricTypeItem/MetricTypeItem';
import { TYPES, LIBRARY } from 'App/constants/card';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { dashboardMetricCreate, metricCreate, withSiteId } from 'App/routes';
import { useStore } from 'App/mstore';
interface Props extends RouteComponentProps {
dashboardId: number;
@ -12,13 +13,16 @@ interface Props extends RouteComponentProps {
}
function MetricTypeList(props: Props) {
const { dashboardId, siteId, history } = props;
const { metricStore } = useStore();
const { hideModal } = useModal();
const { showModal } = useModal();
const onClick = ({ slug }: MetricType) => {
hideModal();
if (slug === LIBRARY) {
return showModal(<MetricsLibraryModal siteId={siteId} dashboardId={dashboardId} />, { right: true, width: 800 });
return showModal(<MetricsLibraryModal siteId={siteId} dashboardId={dashboardId} />, { right: true, width: 800, onClose: () => {
metricStore.updateKey('metricsSearch', '')
} });
}
// TODO redirect to card builder with metricType query param

View file

@ -1,7 +1,7 @@
import Modal from 'App/components/Modal/Modal';
import React, { useEffect, useMemo, useState } from 'react';
import MetricsList from '../MetricsList';
import { Button } from 'UI';
import { Button, Icon } from 'UI';
import { useModal } from 'App/components/Modal';
import { useStore } from 'App/mstore';
import { observer, useObserver } from 'mobx-react-lite';
@ -23,9 +23,22 @@ function MetricsLibraryModal(props: Props) {
setSelectedList(list);
};
const onChange = ({ target: { value } }: any) => {
metricStore.updateKey('metricsSearch', value)
};
return (
<>
<Modal.Header title="Cards Library" />
<Modal.Header title="Cards Library">
<div className="flex items-center justify-between px-4 pt-4">
<div className="text-lg flex items-center font-medium">
<div>Cards Library</div>
</div>
<div>
<MetricSearch onChange={onChange} />
</div>
</div>
</Modal.Header>
<Modal.Content className="p-4 pb-20">
<div className="border">
<MetricsList siteId={siteId} onSelectionChange={onSelectionChange} />
@ -40,6 +53,20 @@ function MetricsLibraryModal(props: Props) {
export default observer(MetricsLibraryModal);
function MetricSearch({ onChange }: any) {
return (
<div className="relative">
<Icon name="search" className="absolute top-0 bottom-0 ml-2 m-auto" size="16" />
<input
name="dashboardsSearch"
className="bg-white p-2 border border-borderColor-gray-light-shade rounded w-full pl-10"
placeholder="Filter by title or owner"
onChange={onChange}
/>
</div>
);
}
function SelectedContent({ dashboardId, selected }: any) {
const { hideModal } = useModal();
const { metricStore, dashboardStore } = useStore();

View file

@ -37,7 +37,7 @@ function WidgetChart(props: Props) {
const drillDownFilter = dashboardStore.drillDownFilter;
const colors = Styles.customMetricColors;
const [loading, setLoading] = useState(true)
const isOverviewWidget = metric.metricType === 'predefined' && metric.viewType === 'overview';
const isOverviewWidget = metric.metricType === WEB_VITALS;
const params = { density: isOverviewWidget ? 7 : 70 }
const metricParams = { ...params }
const prevMetricRef = useRef<any>();

View file

@ -10,7 +10,7 @@ import Select from 'Shared/Select';
import { withSiteId, dashboardMetricDetails, metricDetails } from 'App/routes';
import MetricTypeDropdown from './components/MetricTypeDropdown';
import MetricSubtypeDropdown from './components/MetricSubtypeDropdown';
import { TIMESERIES, TABLE, CLICKMAP, FUNNEL, ERRORS } from 'App/constants/card';
import { TIMESERIES, TABLE, CLICKMAP, FUNNEL, ERRORS, RESOURCE_MONITORING } from 'App/constants/card';
import { clickmapFilter } from 'App/types/filter/newFilter';
import { renderClickmapThumbnail } from './renderMap'
@ -41,7 +41,6 @@ function WidgetForm(props: Props) {
const cannotSaveFunnel = isFunnel && (!metric.series[0] || eventsLength <= 1);
const writeOption = ({ value, name }: any) => {
console.log(name, value)
value = Array.isArray(value) ? value : value.value;
const obj: any = { [name]: value };
@ -69,9 +68,10 @@ function WidgetForm(props: Props) {
obj['viewType'] = 'table';
} else if (value === FUNNEL) {
obj['metricOf'] = 'sessionCount';
} else if (value === ERRORS) {
} else if (value === ERRORS || value === RESOURCE_MONITORING) {
obj['viewType'] = 'chart';
}
}
if (metric.metricType === CLICKMAP && value !== CLICKMAP) {
metric.series[0].filter.removeFilter(0)
}

View file

@ -21,7 +21,9 @@ function Modal({ component, className = 'bg-white', props, hideModal }: Props) {
document.querySelector('body').style.overflow = 'visible';
}
});
});return component ? (
});
return component ? (
ReactDOM.createPortal(
<ModalOverlay hideModal={hideModal} left={!props.right} right={props.right}>
<div
@ -38,8 +40,12 @@ function Modal({ component, className = 'bg-white', props, hideModal }: Props) {
);
}
Modal.Header = ({ title }: { title: string }) => {
return (
Modal.Header = ({ title, children }: { title?: string, children?: any }) => {
return !!children ? (
<div>
{children}
</div>
): (
<div className="text-lg flex items-center p-4 font-medium">
<div>{title}</div>
</div>
@ -47,11 +53,22 @@ Modal.Header = ({ title }: { title: string }) => {
};
Modal.Content = ({ children, className = 'p-4' }: { children: any; className?: string }) => {
return <div className={cn('overflow-y-auto relative', className)} style={{ height: 'calc(100vh - 52px)'}}>{children}</div>;
return (
<div
className={cn('overflow-y-auto relative', className)}
style={{ height: 'calc(100vh - 52px)' }}
>
{children}
</div>
);
};
Modal.Footer = ({ children, className = ''} : any) => {
return <div className={cn('absolute bottom-0 w-full left-0 right-0', className)} style={{ }}>{children}</div>;
}
Modal.Footer = ({ children, className = '' }: any) => {
return (
<div className={cn('absolute bottom-0 w-full left-0 right-0', className)} style={{}}>
{children}
</div>
);
};
export default Modal;

View file

@ -146,12 +146,6 @@ export const TYPES: CardType[] = [
description: 'Find the adoption of your all features in your app.',
slug: WEB_VITALS,
subTypes: [
{
title: 'Resources Count By Type',
slug: FilterKey.RESOURCES_COUNT_BY_TYPE,
description: '',
},
{ title: 'Resources Loading Time', slug: FilterKey.RESOURCES_LOADING_TIME, description: '' },
{
title: 'CPU Load',
slug: FilterKey.AVG_CPU,

View file

@ -3,6 +3,7 @@ import Widget from './types/widget';
import { metricService, errorService } from 'App/services';
import { toast } from 'react-toastify';
import Error from './types/error';
import { TIMESERIES, TABLE, CLICKMAP, FUNNEL, ERRORS, RESOURCE_MONITORING, PERFORMANCE, WEB_VITALS } from 'App/constants/card';
export default class MetricStore {
isLoading: boolean = false;
@ -58,7 +59,11 @@ export default class MetricStore {
}
changeType(value: string) {
this.instance.update({ metricType: value})
const obj: any = { metricType: value};
if (value === ERRORS || value === RESOURCE_MONITORING || value === PERFORMANCE || value === WEB_VITALS) {
obj['viewType'] = 'chart';
}
this.instance.update(obj)
}
reset(id: string) {

View file

@ -7,6 +7,7 @@ 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";
import { WEB_VITALS } from "App/constants/card";
export default class Widget {
public static get ID_KEY():string { return "metricId" }
@ -134,7 +135,7 @@ export default class Widget {
thumbnail: this.thumbnail,
config: {
...this.config,
col: (this.metricType === 'funnel' || this.metricOf === FilterKey.ERRORS || this.metricOf === FilterKey.SESSIONS) ? 4 : 2
col: (this.metricType === 'funnel' || this.metricOf === FilterKey.ERRORS || this.metricOf === FilterKey.SESSIONS) ? 4 : (this.metricType === WEB_VITALS ? 1 : 2)
},
}
}

View file

@ -269,7 +269,7 @@ export enum FilterKey {
IMPACTED_SESSIONS_BY_SLOW_PAGES = 'impactedSessionsBySlowPages',
// Resources
BREAKDOWN_OF_LOADED_RESOURCES = 'breakdownOfLoadedResources',
BREAKDOWN_OF_LOADED_RESOURCES = 'resourcesCountByType',
MISSING_RESOURCES = 'missingResources',
RESOURCE_TYPE_VS_RESPONSE_END = 'resourceTypeVsResponseEnd',
RESOURCE_FETCH_TIME = 'resourceFetchTime',