change(ui): slowest domains card change
This commit is contained in:
parent
6fcef5dff5
commit
20ccee48d1
8 changed files with 260 additions and 157 deletions
|
|
@ -4,11 +4,11 @@ import cn from "classnames";
|
|||
|
||||
interface Props {
|
||||
list: any;
|
||||
selected: any;
|
||||
onClickHandler: (event: any, data: any) => void;
|
||||
selected?: any;
|
||||
onClickHandler?: (event: any, data: any) => void;
|
||||
}
|
||||
|
||||
function CardSessionsByList({ list, selected, onClickHandler }: Props) {
|
||||
function CardSessionsByList({ list, selected, onClickHandler = () => null }: Props) {
|
||||
return (
|
||||
<List
|
||||
dataSource={list}
|
||||
|
|
|
|||
|
|
@ -1,88 +1,88 @@
|
|||
import React from 'react';
|
||||
import {Button, Space} from 'antd';
|
||||
import {filtersMap} from 'Types/filter/newFilter';
|
||||
import {Icon} from 'UI';
|
||||
import {Empty} from 'antd';
|
||||
import {ArrowRight} from "lucide-react";
|
||||
import CardSessionsByList from "Components/Dashboard/Widgets/CardSessionsByList";
|
||||
import {useModal} from "Components/ModalContext";
|
||||
import { Button, Space } from 'antd';
|
||||
import { filtersMap } from 'Types/filter/newFilter';
|
||||
import { Icon } from 'UI';
|
||||
import { Empty } from 'antd';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import CardSessionsByList from 'Components/Dashboard/Widgets/CardSessionsByList';
|
||||
import { useModal } from 'Components/ModalContext';
|
||||
|
||||
interface Props {
|
||||
metric?: any;
|
||||
data: any;
|
||||
onClick?: (filters: any) => void;
|
||||
isTemplate?: boolean;
|
||||
metric?: any;
|
||||
data: any;
|
||||
onClick?: (filters: any) => void;
|
||||
isTemplate?: boolean;
|
||||
}
|
||||
|
||||
function SessionsBy(props: Props) {
|
||||
const {metric = {}, data = {values: []}, onClick = () => null, isTemplate} = props;
|
||||
const [selected, setSelected] = React.useState<any>(null);
|
||||
const total = data.values.length
|
||||
const {openModal, closeModal} = useModal();
|
||||
const { metric = {}, data = { values: [] }, onClick = () => null, isTemplate } = props;
|
||||
const [selected, setSelected] = React.useState<any>(null);
|
||||
const total = data.values.length;
|
||||
const { openModal, closeModal } = useModal();
|
||||
|
||||
const onClickHandler = (event: any, data: any) => {
|
||||
const filters = Array<any>();
|
||||
let filter = {...filtersMap[metric.metricOf]};
|
||||
filter.value = [data.name];
|
||||
filter.type = filter.key;
|
||||
delete filter.key;
|
||||
delete filter.operatorOptions;
|
||||
delete filter.category;
|
||||
delete filter.icon;
|
||||
delete filter.label;
|
||||
delete filter.options;
|
||||
const onClickHandler = (event: any, data: any) => {
|
||||
const filters = Array<any>();
|
||||
let filter = { ...filtersMap[metric.metricOf] };
|
||||
filter.value = [data.name];
|
||||
filter.type = filter.key;
|
||||
delete filter.key;
|
||||
delete filter.operatorOptions;
|
||||
delete filter.category;
|
||||
delete filter.icon;
|
||||
delete filter.label;
|
||||
delete filter.options;
|
||||
|
||||
setSelected(data.name)
|
||||
setSelected(data.name);
|
||||
|
||||
filters.push(filter);
|
||||
onClick(filters);
|
||||
}
|
||||
filters.push(filter);
|
||||
onClick(filters);
|
||||
};
|
||||
|
||||
const showMore = () => {
|
||||
openModal(
|
||||
<CardSessionsByList list={data.values} onClickHandler={(e, item) => {
|
||||
closeModal();
|
||||
onClickHandler(null, item)
|
||||
}} selected={selected}/>, {
|
||||
title: metric.name,
|
||||
width: 600,
|
||||
})
|
||||
}
|
||||
const showMore = () => {
|
||||
openModal(
|
||||
<CardSessionsByList list={data.values} onClickHandler={(e, item) => {
|
||||
closeModal();
|
||||
onClickHandler(null, item);
|
||||
}} selected={selected} />, {
|
||||
title: metric.name,
|
||||
width: 600
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{data.values && data.values.length === 0 ? (
|
||||
<Empty
|
||||
image={null}
|
||||
style={{minHeight: 220}}
|
||||
className="flex flex-col items-center justify-center"
|
||||
imageStyle={{height: 60}}
|
||||
description={
|
||||
<div className="flex items-center justify-center">
|
||||
<Icon name="info-circle" className="mr-2" size="18"/>
|
||||
No data for the selected time period
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col justify-between w-full" style={{height: 220}}>
|
||||
<CardSessionsByList list={data.values.slice(0, 3)}
|
||||
selected={selected}
|
||||
onClickHandler={onClickHandler}/>
|
||||
{total > 3 && (
|
||||
<div className="flex">
|
||||
<Button type="link" onClick={showMore}>
|
||||
<Space className='flex font-medium gap-1'>
|
||||
{total - 5} More
|
||||
<ArrowRight size={16}/>
|
||||
</Space>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
return (
|
||||
<div>
|
||||
{data.values && data.values.length === 0 ? (
|
||||
<Empty
|
||||
image={null}
|
||||
style={{ minHeight: 220 }}
|
||||
className="flex flex-col items-center justify-center"
|
||||
imageStyle={{ height: 60 }}
|
||||
description={
|
||||
<div className="flex items-center justify-center">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
No data for the selected time period
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col justify-between w-full" style={{ height: 220 }}>
|
||||
<CardSessionsByList list={data.values.slice(0, 3)}
|
||||
selected={selected}
|
||||
onClickHandler={onClickHandler} />
|
||||
{total > 3 && (
|
||||
<div className="flex">
|
||||
<Button type="link" onClick={showMore}>
|
||||
<Space className="flex font-medium gap-1">
|
||||
{total - 5} More
|
||||
<ArrowRight size={16} />
|
||||
</Space>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SessionsBy;
|
||||
|
|
|
|||
|
|
@ -1,38 +1,83 @@
|
|||
import React from 'react';
|
||||
import { NoContent } from 'UI';
|
||||
import { Icon, NoContent } from 'UI';
|
||||
import { Styles } from '../../common';
|
||||
import { numberWithCommas } from 'App/utils';
|
||||
import Bar from './Bar';
|
||||
import { NO_METRIC_DATA } from 'App/constants/messages'
|
||||
import { NO_METRIC_DATA } from 'App/constants/messages';
|
||||
import CardSessionsByList from 'Components/Dashboard/Widgets/CardSessionsByList';
|
||||
import { List, Progress, Typography } from 'antd';
|
||||
import cn from 'classnames';
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
metric?: any
|
||||
data: any;
|
||||
}
|
||||
|
||||
function SlowestDomains(props: Props) {
|
||||
const { data, metric } = props;
|
||||
const firstAvg = metric.data.chart[0] && metric.data.chart[0].value;
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={ metric.data.chart.length === 0 }
|
||||
style={{ minHeight: 220 }}
|
||||
title={NO_METRIC_DATA}
|
||||
>
|
||||
<div className="w-full" style={{ height: '240px' }}>
|
||||
{metric.data.chart.map((item, i) =>
|
||||
<Bar
|
||||
key={i}
|
||||
className="mb-2"
|
||||
avg={numberWithCommas(Math.round(item.value))}
|
||||
width={Math.round((item.value * 100) / firstAvg) - 10}
|
||||
domain={item.domain}
|
||||
color={Styles.colors[i]}
|
||||
const { data } = props;
|
||||
// TODO - move this to the store
|
||||
const highest = data.chart[0].value;
|
||||
const list = data.chart.slice(0, 4).map((item: any) => ({
|
||||
name: item.domain,
|
||||
icon: <Icon name="link-45deg" size={24} />,
|
||||
value: Math.round(item.value) + 'ms',
|
||||
progress: Math.round((item.value * 100) / highest)
|
||||
}));
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={list.length === 0}
|
||||
style={{ minHeight: 220 }}
|
||||
title={NO_METRIC_DATA}
|
||||
>
|
||||
<div className="w-full" style={{ height: '240px' }}>
|
||||
<List
|
||||
dataSource={list}
|
||||
split={false}
|
||||
renderItem={(row: any) => (
|
||||
<List.Item
|
||||
key={row.domain}
|
||||
// onClick={(e) => onClickHandler(e, row)} // Remove onClick handler to disable click interaction
|
||||
style={{
|
||||
borderBottom: '1px dotted rgba(0, 0, 0, 0.05)',
|
||||
padding: '4px 10px',
|
||||
lineHeight: '1px'
|
||||
}}
|
||||
className={cn('rounded')} // Remove hover:bg-active-blue and cursor-pointer
|
||||
>
|
||||
<List.Item.Meta
|
||||
className="m-0"
|
||||
avatar={row.icon ? row.icon : null}
|
||||
title={(
|
||||
<div className="m-0">
|
||||
<div className="flex justify-between m-0 p-0">
|
||||
<Typography.Text>{row.name}</Typography.Text>
|
||||
<Typography.Text type="secondary"> {row.value}</Typography.Text>
|
||||
</div>
|
||||
|
||||
<Progress
|
||||
percent={row.progress}
|
||||
showInfo={false}
|
||||
strokeColor={{
|
||||
'0%': '#394EFF',
|
||||
'100%': '#394EFF'
|
||||
}}
|
||||
size={['small', 2]}
|
||||
style={{
|
||||
padding: '0 0px',
|
||||
margin: '0 0px',
|
||||
height: 4
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default SlowestDomains;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import CallsWithErrorsExample
|
|||
from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/CallsWithErrorsExample";
|
||||
import SessionsPerBrowserExample
|
||||
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsPerBrowserExample';
|
||||
import SlowestDomains
|
||||
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/SlowestDomains';
|
||||
|
||||
export const CARD_CATEGORY = {
|
||||
PRODUCT_ANALYTICS: 'product-analytics',
|
||||
|
|
@ -256,7 +258,7 @@ export const CARD_LIST: CardType[] = [
|
|||
},
|
||||
|
||||
{
|
||||
title: 'Sessions per Browser',
|
||||
title: 'Sessions by Browser & Version',
|
||||
key: FilterKey.SESSIONS_PER_BROWSER,
|
||||
cardType: PERFORMANCE,
|
||||
metricOf: FilterKey.SESSIONS_PER_BROWSER,
|
||||
|
|
@ -271,8 +273,8 @@ export const CARD_LIST: CardType[] = [
|
|||
cardType: PERFORMANCE,
|
||||
metricOf: FilterKey.SLOWEST_DOMAINS,
|
||||
category: CARD_CATEGORIES[1].key,
|
||||
data: generateRandomBarsData(),
|
||||
example: Bars,
|
||||
// data: generateRandomBarsData(),
|
||||
example: SlowestDomains,
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
|
||||
import { Circle } from '../Count';
|
||||
import ExCard from '../ExCard';
|
||||
import ByComponent from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/Component';
|
||||
|
||||
function ByUrl(props: any) {
|
||||
const [mode, setMode] = React.useState(0);
|
||||
|
|
@ -47,57 +48,62 @@ function ByUrl(props: any) {
|
|||
|
||||
const lineWidth = 240;
|
||||
return (
|
||||
<ExCard
|
||||
{...props}
|
||||
title={
|
||||
<div className={'flex gap-2 items-center'}>
|
||||
<div>{props.title}</div>
|
||||
<div className={'font-normal'}><Segmented
|
||||
options={[
|
||||
{ label: 'URL', value: '0' },
|
||||
{ label: 'Page Title', value: '1' },
|
||||
]}
|
||||
onChange={(v) => setMode(Number(v))}
|
||||
size='small'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className={'flex gap-1 flex-col'}>
|
||||
{rows.map((r) => (
|
||||
<div
|
||||
className={
|
||||
'flex items-center gap-2 border-b border-dotted last:border-0 py-2 first:pt-0 last:pb-0'
|
||||
}
|
||||
>
|
||||
<Circle badgeType={1}>{r.icon}</Circle>
|
||||
<div className={'ml-2 flex flex-col gap-0'}>
|
||||
<div>{mode === 0 ? r.label : r.ptitle}</div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div
|
||||
style={{
|
||||
height: 2,
|
||||
width: lineWidth * (0.01 * r.progress),
|
||||
background: '#394EFF',
|
||||
}}
|
||||
className={'rounded-l'}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
height: 2,
|
||||
width: lineWidth - lineWidth * (0.01 * r.progress),
|
||||
background: '#E2E4F6',
|
||||
}}
|
||||
className={'rounded-r'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'min-w-8 ml-auto'}>{r.value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ExCard>
|
||||
<ByComponent
|
||||
{...props}
|
||||
rows={rows}
|
||||
lineWidth={lineWidth}
|
||||
/>
|
||||
// <ExCard
|
||||
// {...props}
|
||||
// title={
|
||||
// <div className={'flex gap-2 items-center'}>
|
||||
// <div>{props.title}</div>
|
||||
// <div className={'font-normal'}><Segmented
|
||||
// options={[
|
||||
// { label: 'URL', value: '0' },
|
||||
// { label: 'Page Title', value: '1' },
|
||||
// ]}
|
||||
// onChange={(v) => setMode(Number(v))}
|
||||
// size='small'
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
// >
|
||||
// <div className={'flex gap-1 flex-col'}>
|
||||
// {rows.map((r) => (
|
||||
// <div
|
||||
// className={
|
||||
// 'flex items-center gap-2 border-b border-dotted last:border-0 py-2 first:pt-0 last:pb-0'
|
||||
// }
|
||||
// >
|
||||
// <Circle badgeType={1}>{r.icon}</Circle>
|
||||
// <div className={'ml-2 flex flex-col gap-0'}>
|
||||
// <div>{mode === 0 ? r.label : r.ptitle}</div>
|
||||
// <div style={{ display: 'flex' }}>
|
||||
// <div
|
||||
// style={{
|
||||
// height: 2,
|
||||
// width: lineWidth * (0.01 * r.progress),
|
||||
// background: '#394EFF',
|
||||
// }}
|
||||
// className={'rounded-l'}
|
||||
// />
|
||||
// <div
|
||||
// style={{
|
||||
// height: 2,
|
||||
// width: lineWidth - lineWidth * (0.01 * r.progress),
|
||||
// background: '#E2E4F6',
|
||||
// }}
|
||||
// className={'rounded-r'}
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className={'min-w-8 ml-auto'}>{r.value}</div>
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
// </ExCard>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import React from 'react';
|
||||
import ByComponent from './Component';
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
|
||||
function SlowestDomains(props: any) {
|
||||
const rows = [
|
||||
{
|
||||
label: 'res.cloudinary.com',
|
||||
value: '500',
|
||||
progress: 75,
|
||||
icon: <LinkOutlined size={12} />
|
||||
},
|
||||
{
|
||||
label: 'mintbase.vercel.app',
|
||||
value: '306',
|
||||
progress: 60,
|
||||
icon: <LinkOutlined size={12} />
|
||||
},
|
||||
{
|
||||
label: 'downloads.intercomcdn.com',
|
||||
value: '198',
|
||||
progress: 30,
|
||||
icon: <LinkOutlined size={12} />
|
||||
},
|
||||
{
|
||||
label: 'static.intercomassets.com',
|
||||
value: '47',
|
||||
progress: 15,
|
||||
icon: <LinkOutlined size={12} />
|
||||
},
|
||||
{
|
||||
label: 'mozbar.moz.com',
|
||||
value: '5',
|
||||
progress: 5,
|
||||
icon: <LinkOutlined size={12} />
|
||||
}
|
||||
];
|
||||
|
||||
const lineWidth = 200;
|
||||
return (
|
||||
<ByComponent
|
||||
{...props}
|
||||
rows={rows}
|
||||
lineWidth={lineWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SlowestDomains;
|
||||
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import { Circle } from './Count';
|
||||
import ExCard from './ExCard';
|
||||
|
||||
// TODO - delete this
|
||||
function SlowestDomain(props: any) {
|
||||
const rows = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ function WidgetPredefinedChart(props: Props) {
|
|||
case FilterKey.SESSIONS_PER_BROWSER:
|
||||
return <SessionsPerBrowser data={data} />
|
||||
case FilterKey.SLOWEST_DOMAINS:
|
||||
return <SlowestDomains data={data} metric={metric} />
|
||||
return <SlowestDomains data={data} />
|
||||
case FilterKey.TIME_TO_RENDER:
|
||||
return <TimeToRender data={data} metric={metric} />
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue