feat(ui) - dashboard improvements - api - integration
This commit is contained in:
parent
7bd6ee3065
commit
7818bc88ee
12 changed files with 173 additions and 32 deletions
|
|
@ -25,7 +25,7 @@ const siteIdRequiredPaths = [
|
|||
'/heatmaps',
|
||||
'/custom_metrics',
|
||||
'/dashboards',
|
||||
'/metrics',
|
||||
'/cards',
|
||||
'/unprocessed',
|
||||
'/notes',
|
||||
// '/custom_metrics/sessions',
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ function DashboardWidgetGrid(props: Props) {
|
|||
>
|
||||
<div className="grid gap-4 grid-cols-4 items-start pb-10" id={props.id}>{smallWidgets.length > 0 ? (
|
||||
<>
|
||||
<div className="font-semibold text-xl py-4 flex items-center gap-2col-span-4">
|
||||
<div className="font-semibold text-xl py-4 flex items-center gap-2 col-span-4">
|
||||
<Icon name="grid-horizontal" size={26} />
|
||||
Web Vitals
|
||||
</div>
|
||||
|
|
@ -82,7 +82,7 @@ function DashboardWidgetGrid(props: Props) {
|
|||
) : null}
|
||||
|
||||
{smallWidgets.length > 0 && regularWidgets.length > 0 ? (
|
||||
<div className="font-semibold text-xl py-4 flex items-center gap-2col-span-4">
|
||||
<div className="font-semibold text-xl py-4 flex items-center gap-2 col-span-4">
|
||||
<Icon name="grid-horizontal" size={26} />
|
||||
All Metrics
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,19 +39,20 @@ function MetricListItem(props: Props) {
|
|||
const path = withSiteId(`/metrics/${metric.metricId}`, siteId);
|
||||
history.push(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="grid grid-cols-12 py-4 border-t select-none items-center hover:bg-active-blue cursor-pointer px-6"
|
||||
onClick={onItemClick}
|
||||
>
|
||||
<div className="col-span-4 flex items-center">
|
||||
<Checkbox
|
||||
{/* <Checkbox
|
||||
name="slack"
|
||||
className="mr-4"
|
||||
type="checkbox"
|
||||
checked={selected}
|
||||
onClick={toggleSelection}
|
||||
/>
|
||||
/> */}
|
||||
<div className="flex items-center">
|
||||
<MetricTypeIcon type={metric.metricType} />
|
||||
<div className="link capitalize-first">{metric.name}</div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import MetricsLibraryModal from '../MetricsLibraryModal';
|
|||
import MetricTypeItem, { MetricType } from '../MetricTypeItem/MetricTypeItem';
|
||||
import { TYPES, LIBRARY } from 'App/constants/card';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { dashboardMetricCreate, withSiteId } from 'App/routes';
|
||||
import { dashboardMetricCreate, metricCreate, withSiteId } from 'App/routes';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
dashboardId: number;
|
||||
|
|
@ -23,7 +23,11 @@ function MetricTypeList(props: Props) {
|
|||
|
||||
// TODO redirect to card builder with metricType query param
|
||||
const path = withSiteId(dashboardMetricCreate(dashboardId + ''), siteId);
|
||||
history.push(path);
|
||||
const queryString = new URLSearchParams({ type: slug }).toString();
|
||||
history.push({
|
||||
pathname: path,
|
||||
search: `?${queryString}`
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Icon, PageTitle, Button, Link } from 'UI';
|
||||
import { Icon, PageTitle, Button, Link, SegmentSelection } from 'UI';
|
||||
import MetricsSearch from '../MetricsSearch';
|
||||
import Select from 'Shared/Select';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -8,6 +8,9 @@ import { useObserver } from 'mobx-react-lite';
|
|||
function MetricViewHeader() {
|
||||
const { metricStore } = useStore();
|
||||
const sort = useObserver(() => metricStore.sort);
|
||||
const listView = useObserver(() => metricStore.listView);
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center mb-4 justify-between px-6">
|
||||
|
|
@ -15,9 +18,21 @@ function MetricViewHeader() {
|
|||
<PageTitle title="Metrics" className="" />
|
||||
</div>
|
||||
<div className="ml-auto flex items-center">
|
||||
<Link to={'/metrics/create'}>
|
||||
{/* <Link to={'/metrics/create'}>
|
||||
<Button variant="primary">Create</Button>
|
||||
</Link>
|
||||
</Link> */}
|
||||
<SegmentSelection
|
||||
name="viewType"
|
||||
className="my-3"
|
||||
primary
|
||||
icons={true}
|
||||
onSelect={ () => metricStore.updateKey('listView', !listView) }
|
||||
value={{ value: listView ? 'list' : 'grid' }}
|
||||
list={ [
|
||||
{ value: 'list', name: '', icon: 'graph-up-arrow' },
|
||||
{ value: 'grid', name: '', icon: 'hash' },
|
||||
]}
|
||||
/>
|
||||
<div className="mx-2">
|
||||
<Select
|
||||
options={[
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
|
||||
}
|
||||
function MetricsGrid(props: Props) {
|
||||
return (
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MetricsGrid;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './MetricsGrid'
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import MetricListItem from '../MetricListItem';
|
||||
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
|
||||
|
||||
interface Props {
|
||||
list: any;
|
||||
siteId: any;
|
||||
selectedList: any;
|
||||
toggleSelection?: (metricId: any) => void;
|
||||
}
|
||||
function GridView(props: Props) {
|
||||
const { siteId, list, selectedList, toggleSelection } = props;
|
||||
return (
|
||||
<div className="grid grid-cols-3 gap-4 m-4">
|
||||
{list.map((metric: any) => (
|
||||
<React.Fragment key={metric.metricId}>
|
||||
<WidgetWrapper
|
||||
key={metric.metricId}
|
||||
widget={metric}
|
||||
active={selectedList.includes(metric.metricId)}
|
||||
isTemplate={true}
|
||||
isWidget={metric.metricType === 'predefined'}
|
||||
onClick={() => toggleSelection(parseInt(metric.metricId))}
|
||||
/>
|
||||
{/* <MetricListItem
|
||||
metric={metric}
|
||||
siteId={siteId}
|
||||
selected={selectedList.includes(parseInt(metric.metricId))}
|
||||
toggleSelection={(e: any) => {
|
||||
e.stopPropagation();
|
||||
toggleSelection(parseInt(metric.metricId));
|
||||
}}
|
||||
/> */}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GridView;
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
import MetricListItem from '../MetricListItem';
|
||||
import { Checkbox } from 'UI';
|
||||
|
||||
interface Props {
|
||||
list: any;
|
||||
siteId: any;
|
||||
selectedList: any;
|
||||
toggleSelection?: (metricId: any) => void;
|
||||
}
|
||||
function ListView(props: Props) {
|
||||
const { siteId, list, selectedList, toggleSelection } = props;
|
||||
return (
|
||||
<div>
|
||||
<div className="grid grid-cols-12 py-2 font-medium px-6">
|
||||
{/* <div className="col-span-4 flex items-center">
|
||||
<Checkbox
|
||||
name="slack"
|
||||
className="mr-4"
|
||||
type="checkbox"
|
||||
checked={false}
|
||||
onClick={() => selectedList(list.map((i: any) => i.metricId))}
|
||||
/>
|
||||
<span>Title</span>
|
||||
</div> */}
|
||||
<div className="col-span-4">Owner</div>
|
||||
<div className="col-span-2">Visibility</div>
|
||||
<div className="col-span-2 text-right">Last Modified</div>
|
||||
</div>
|
||||
{list.map((metric: any) => (
|
||||
<MetricListItem
|
||||
metric={metric}
|
||||
siteId={siteId}
|
||||
selected={selectedList.includes(parseInt(metric.metricId))}
|
||||
toggleSelection={(e: any) => {
|
||||
e.stopPropagation();
|
||||
toggleSelection(parseInt(metric.metricId));
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ListView;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import { observer, useObserver } from 'mobx-react-lite';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { NoContent, Pagination, Icon, Checkbox } from 'UI';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -6,6 +6,8 @@ import { filterList } from 'App/utils';
|
|||
import MetricListItem from '../MetricListItem';
|
||||
import { sliceListPerPage } from 'App/utils';
|
||||
import Widget from 'App/mstore/types/widget';
|
||||
import GridView from './GridView';
|
||||
import ListView from './ListView';
|
||||
|
||||
function MetricsList({
|
||||
siteId,
|
||||
|
|
@ -17,6 +19,7 @@ function MetricsList({
|
|||
const { metricStore } = useStore();
|
||||
const metrics = metricStore.sortedWidgets;
|
||||
const metricsSearch = metricStore.metricsSearch;
|
||||
const listView = useObserver(() => metricStore.listView);
|
||||
const [selectedMetrics, setSelectedMetrics] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -61,6 +64,22 @@ function MetricsList({
|
|||
</div>
|
||||
}
|
||||
>
|
||||
{listView ? (
|
||||
<ListView
|
||||
siteId={siteId}
|
||||
list={sliceListPerPage(list, metricStore.page - 1, metricStore.pageSize)}
|
||||
selectedList={selectedMetrics}
|
||||
toggleSelection={toggleMetricSelection}
|
||||
/>
|
||||
) : (
|
||||
<GridView
|
||||
siteId={siteId}
|
||||
list={sliceListPerPage(list, metricStore.page - 1, metricStore.pageSize)}
|
||||
selectedList={selectedMetrics}
|
||||
toggleSelection={toggleMetricSelection}
|
||||
/>
|
||||
)}
|
||||
{/*
|
||||
<div className="mt-3 rounded bg-white">
|
||||
<div className="grid grid-cols-12 py-2 font-medium px-6">
|
||||
<div className="col-span-4 flex items-center">
|
||||
|
|
@ -78,20 +97,21 @@ function MetricsList({
|
|||
<div className="col-span-2 text-right">Last Modified</div>
|
||||
</div>
|
||||
|
||||
{sliceListPerPage(list, metricStore.page - 1, metricStore.pageSize).map((metric: any) => (
|
||||
<React.Fragment key={metric.metricId}>
|
||||
<MetricListItem
|
||||
metric={metric}
|
||||
siteId={siteId}
|
||||
selected={selectedMetrics.includes(parseInt(metric.metricId))}
|
||||
toggleSelection={(e: any) => {
|
||||
e.stopPropagation();
|
||||
toggleMetricSelection(parseInt(metric.metricId));
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
{sliceListPerPage(list, metricStore.page - 1, metricStore.pageSize).map((metric: any) => (
|
||||
// <React.Fragment key={metric.metricId}>
|
||||
// <MetricListItem
|
||||
// metric={metric}
|
||||
// siteId={siteId}
|
||||
// selected={selectedMetrics.includes(parseInt(metric.metricId))}
|
||||
// toggleSelection={(e: any) => {
|
||||
// e.stopPropagation();
|
||||
// toggleMetricSelection(parseInt(metric.metricId));
|
||||
// }}
|
||||
// />
|
||||
// </React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
*/}
|
||||
|
||||
<div className="w-full flex items-center justify-between py-4 px-6 border-t">
|
||||
<div className="text-disabled-text">
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ export default class MetricStore {
|
|||
|
||||
sessionsPage: number = 1;
|
||||
sessionsPageSize: number = 10;
|
||||
listView?: boolean = false
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default class MetricService {
|
|||
* @returns {Promise<any>}
|
||||
*/
|
||||
getMetrics(): Promise<any> {
|
||||
return this.client.get('/metrics')
|
||||
return this.client.post('/cards/search', { limit: 100 })
|
||||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data || []);
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ export default class MetricService {
|
|||
* @returns {Promise<any>}
|
||||
*/
|
||||
getMetric(metricId: string): Promise<any> {
|
||||
return this.client.get('/metrics/' + metricId)
|
||||
return this.client.get('/cards/' + metricId)
|
||||
.then(fetchErrorCheck)
|
||||
.then((response: { data: any; }) => response.data || {});
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ export default class MetricService {
|
|||
const data = metric.toJson()
|
||||
const isCreating = !data[Widget.ID_KEY];
|
||||
const method = isCreating ? 'post' : 'put';
|
||||
const url = isCreating ? '/metrics' : '/metrics/' + data[Widget.ID_KEY];
|
||||
const url = isCreating ? '/cards' : '/cards/' + data[Widget.ID_KEY];
|
||||
return this.client[method](url, data)
|
||||
.then(fetchErrorCheck)
|
||||
.then((response: { data: any; }) => response.data || {})
|
||||
|
|
@ -55,7 +55,7 @@ export default class MetricService {
|
|||
* @returns {Promise<any>}
|
||||
*/
|
||||
deleteMetric(metricId: string): Promise<any> {
|
||||
return this.client.delete('/metrics/' + metricId)
|
||||
return this.client.delete('/cards/' + metricId)
|
||||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data);
|
||||
}
|
||||
|
|
@ -66,13 +66,13 @@ export default class MetricService {
|
|||
* @returns {Promise<any>}
|
||||
*/
|
||||
getTemplates(): Promise<any> {
|
||||
return this.client.get('/metrics/templates')
|
||||
return this.client.get('/cards/templates')
|
||||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data || []);
|
||||
}
|
||||
|
||||
getMetricChartData(metric: Widget, data: any, isWidget: boolean = false): Promise<any> {
|
||||
const path = isWidget ? `/metrics/${metric.metricId}/chart` : `/metrics/try`;
|
||||
const path = isWidget ? `/cards/${metric.metricId}/chart` : `/cards/try`;
|
||||
return this.client.post(path, data)
|
||||
.then(fetchErrorCheck)
|
||||
.then((response: { data: any; }) => response.data || {});
|
||||
|
|
@ -84,13 +84,13 @@ export default class MetricService {
|
|||
* @returns
|
||||
*/
|
||||
fetchSessions(metricId: string, filter: any): Promise<any> {
|
||||
return this.client.post(metricId ? `/metrics/${metricId}/sessions` : '/metrics/try/sessions', filter)
|
||||
return this.client.post(metricId ? `/cards/${metricId}/sessions` : '/cards/try/sessions', filter)
|
||||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data || []);
|
||||
}
|
||||
|
||||
fetchIssues(filter: string): Promise<any> {
|
||||
return this.client.post(`/metrics/try/issues`, filter)
|
||||
return this.client.post(`/cards/try/issues`, filter)
|
||||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data || {});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue