fix ui: more different cards...

This commit is contained in:
nick-delirium 2024-05-02 16:28:44 +02:00
parent b01a38ecc9
commit ac661524aa
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
7 changed files with 389 additions and 17 deletions

View file

@ -1,3 +1,4 @@
import { Segmented } from 'antd';
import { Angry, ArrowDownUp, Mouse, MousePointerClick, Unlink } from 'lucide-react';
import React from 'react';
@ -6,13 +7,44 @@ import React from 'react';
import ExCard from './ExCard';
function ExampleCount() {
return <ExCard title={'Sessions by'}>
<Frustrations />
</ExCard>;
const TYPES = {
Frustrations: 0,
Errors: 1,
Users: 2
}
function Frustrations() {
function ExampleCount() {
const [type, setType] = React.useState(TYPES.Frustrations);
const el = {
[TYPES.Frustrations]: <Frustrations />,
[TYPES.Errors]: <Errors />,
[TYPES.Users]: <Users />
};
return (
<ExCard
title={
<div className={'flex items-center gap-2'}>
<div>Sessions by</div>
<div className={'font-normal'}>
<Segmented
options={[
{ label: 'Frustrations', value: '0' },
{ label: 'Errors', value: '1' },
{ label: 'Users', value: '2'}
]}
onChange={(v) => setType(Number(v))}
/>
</div>
</div>
}
>
{el[type]}
</ExCard>
);
}
export function Frustrations() {
const rows = [
{
label: 'Rage Clicks',
@ -46,12 +78,13 @@ function Frustrations() {
},
];
const lineWidth = 140
return (
<div className={'flex gap-2 flex-col'}>
<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'
'flex items-center gap-2 border-b border-dotted py-2 last:border-0 first:pt-0 last:pb-0'
}
>
<Circle badgeType={0}>{r.icon}</Circle>
@ -60,7 +93,7 @@ function Frustrations() {
<div
style={{
height: 2,
width: 140 * (0.01 * r.progress),
width: lineWidth * (0.01 * r.progress),
background: '#394EFF',
}}
className={'rounded-l'}
@ -68,7 +101,7 @@ function Frustrations() {
<div
style={{
height: 2,
width: 140-(140 * (0.01 * r.progress)),
width: lineWidth - lineWidth * (0.01 * r.progress),
background: '#E2E4F6',
}}
className={'rounded-r'}
@ -81,7 +114,7 @@ function Frustrations() {
);
}
function Errors() {
export function Errors() {
const rows = [
{
label: 'HTTP response status code (404 Not Found)',
@ -114,9 +147,89 @@ function Errors() {
icon: <div className={'text-red text-xs'}>XHR</div>,
},
];
const lineWidth = 270
return (
<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>{r.label}</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>
)
}
function Circle({
export function Users() {
const rows = [
{
label: 'pedro@mycompany.com',
value: '9.5K',
},
{
label: 'mauricio@mycompany.com',
value: '2.5K',
},
{
label: 'alex@mycompany.com',
value: '405',
},
{
label: 'jose@mycompany.com',
value: '150',
},
{
label: 'maria@mycompany.com',
value: '123',
}
]
return (
<div className={'flex gap-1 flex-col'}>
{rows.map((r) => (
<div
className={
'flex items-center gap-2 border-b border-dotted py-2 last:border-0 first:pt-0 last:pb-0'
}
>
<Circle badgeType={2}>{r.label[0].toUpperCase()}</Circle>
<div className={'ml-2'}>
<div>{r.label}</div>
</div>
<div className={'min-w-8 ml-auto'}>{r.value}</div>
</div>
))}
</div>
)
}
export function Circle({
children,
badgeType,
}: {
@ -124,14 +237,17 @@ function Circle({
badgeType: 0 | 1 | 2;
}) {
const colors = {
// frustrations
0: '#FFFBE6',
// errors
1: '#FFF1F0',
// users and domains
2: '#EBF4F5',
};
return (
<div
className={'p-2 rounded-full'}
className={'w-8 h-8 flex items-center justify-center rounded-full'}
style={{ background: colors[badgeType] }}
>
{children}

View file

@ -0,0 +1,102 @@
import { GitCommitHorizontal } from 'lucide-react';
import React from 'react';
import ExCard from './ExCard';
function PerfBreakdown() {
const rows = [
['5K', '1K'],
['4K', '750'],
['3K', '500'],
['2K', '250'],
['1K', '0'],
];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May'];
const values = [
[3, 1, 9],
[2, 4, 10],
[3, 6, 2],
[7, 4, 1],
[5, 3, 4],
];
const bgs = ['#E2E4F6', '#A7BFFF', '#394EFF'];
return (
<ExCard title={'Breakdown'}>
<div className={'relative'}>
<div className={'flex flex-col gap-4'}>
{rows.map((r) => (
<div className={'flex items-center gap-2'}>
<div className={'text-gray-dark'}>{r[0]}</div>
<div className="border-t border-dotted border-gray-lighter w-full"></div>
<div className={'text-gray-dark min-w-8'}>{r[1]}</div>
</div>
))}
</div>
<div className={'px-4 flex items-center justify-around w-full'}>
{months.map((m, i) => (
<div className={'text-gray-dark relative'}>
<span>{m}</span>
<div
className={'absolute flex flex-col'}
style={{ bottom: 30, left: 0, width: 24 }}
>
{values[i].map((v, bg) => (
<div
style={{
width: '100%',
height: v * 9 + 'px',
background: bgs[bg],
}}
/>
))}
</div>
</div>
))}
</div>
<div
style={{
position: 'absolute',
top: 30,
left: 30,
zIndex: 99,
width: 308,
overflow: 'hidden',
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="332"
height="37"
viewBox="0 0 332 37"
fill="none"
>
<path
d="M1 30.8715L4.66667 26.964C8.33333 23.0566 15.6667 15.2417 23 9.74387C30.3333 4.24605 37.6667 1.06529 45 1.54812C52.3333 2.03094 59.6667 6.17735 67 10.8175C74.3333 15.4577 81.6667 20.5916 89 19.6024C96.3333 18.6133 103.667 11.5009 111 7.69717C118.333 3.89339 125.667 3.39814 133 8.24328C140.333 13.0884 147.667 23.274 155 28.5047C162.333 33.7354 169.667 34.0114 177 33.4739C184.333 32.9365 191.667 31.5856 199 28.7677C206.333 25.9499 213.667 21.665 221 18.723C228.333 15.781 235.667 14.182 243 10.7612C250.333 7.34035 257.667 2.09783 265 3.39238C272.333 4.68693 279.667 12.5186 287 14.2932C294.333 16.0679 301.667 11.7856 309 14.3106C316.333 16.8356 323.667 26.1678 327.333 30.8339L331 35.5"
stroke="#6A8CFF"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</div>
</div>
<div className={'flex gap-4 justify-center'}>
<div className={'flex gap-2 items-center'}>
<div className={'w-4 h-4 rounded-full bg-[#E2E4F6]'} />
<div className={'text-disabled-text'}>XHR</div>
</div>
<div className={'flex gap-2 items-center'}>
<div className={'w-4 h-4 rounded-full bg-[#A7BFFF]'} />
<div className={'text-disabled-text'}>Other</div>
</div>
<div className={'flex gap-2 items-center'}>
<GitCommitHorizontal size={14} strokeWidth={1} color={'#6A8CFF'} />
<div className={'text-disabled-text'}>Response End</div>
</div>
</div>
</ExCard>
);
}
export default PerfBreakdown;

View file

@ -0,0 +1,15 @@
import React from 'react'
import ExCard from "./ExCard";
import { Errors } from "./Count";
function SessionsByErrors() {
return (
<ExCard
title={'Sessions by Errors'}
>
<Errors />
</ExCard>
);
}
export default SessionsByErrors

View file

@ -0,0 +1,15 @@
import React from 'react'
import ExCard from "./ExCard";
import { Frustrations } from "./Count";
function SessionsByIssues() {
return (
<ExCard
title={'Sessions by Issues'}
>
<Frustrations />
</ExCard>
);
}
export default SessionsByIssues

View file

@ -0,0 +1,84 @@
import { LinkOutlined } from '@ant-design/icons';
import React from 'react';
import { Circle } from './Count';
import ExCard from './ExCard';
function SlowestDomain() {
const rows = [
{
label: 'kroger.com',
value: '28,162 ms',
progress: 97,
icon: <LinkOutlined size={12} />,
},
{
label: 'instacart.com',
value: '3,165 ms',
progress: 60,
icon: <LinkOutlined size={12} />,
},
{
label: 'gifs.eco.br',
value: '1,503 ms',
progress: 40,
icon: <LinkOutlined size={12} />,
},
{
label: 'cdn.byintera.com',
value: '512 ms',
progress: 10,
icon: <LinkOutlined size={12} />,
},
{
label: 'analytics.twitter.com',
value: '110 ms',
progress: 5,
icon: <LinkOutlined size={12} />,
},
];
const lineWidth = 240;
return (
<ExCard title={'Slowest Domain'}>
<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={2}>
{r.icon}
</Circle>
<div className={'ml-2 flex flex-col gap-0'}>
<div>{r.label}</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>
);
}
export default SlowestDomain;

View file

@ -13,6 +13,7 @@ function ExampleTrend() {
title={
<div className={'flex items-center gap-2'}>
<div>Trend</div>
<div className={'font-normal'}>
<Segmented
options={[
{ label: 'Single-Series', value: 'single' },
@ -20,6 +21,7 @@ function ExampleTrend() {
]}
onChange={(v) => setIsMulti(v === 'multi')}
/>
</div>
</div>
}
>

View file

@ -7,9 +7,27 @@ import ExampleCount from "./Examples/Count";
import ExampleFunnel from './Examples/Funnel';
import ExamplePath from './Examples/Path';
import PerfBreakdown from "./Examples/PerfBreakdown";
import SessionsByErrors from "./Examples/SessionsByErrors";
import SessionsByIssues from "./Examples/SessionsByIssues";
import SlowestDomain from "./Examples/SlowestDomain";
import ExampleTrend from './Examples/Trend';
function NewDashboardModal(props: { onClose: () => void; open: boolean }) {
const initial = 'performance-monitoring' //'product-analytics';
const [selected, setSelected] = React.useState(initial);
let item;
switch (selected) {
case 'product-analytics':
item = <ProductAnalytics />
break;
case 'performance-monitoring':
item = <PerformanceMonitoring />
break;
default:
item = <div>under construction</div>
break;
}
return (
<Modal onClose={props.onClose} open={props.open} size={'xlarge'}>
<Modal.Content className={'bg-[#FAFAFA]'}>
@ -62,13 +80,11 @@ function NewDashboardModal(props: { onClose: () => void; open: boolean }) {
value: 'core-web-vitals',
},
]}
onChange={(v) => setSelected(v)}
/>
<div className={'mt-2 w-full flex flex-wrap gap-2 overflow-scroll'}>
<ExampleFunnel />
<ExamplePath />
<ExampleTrend />
<ExampleCount />
<div style={{ maxHeight: 'calc(100vh - 210px)'}} className={'mt-2 w-full flex flex-wrap gap-2 overflow-scroll'}>
{item}
</div>
</div>
</div>
@ -77,4 +93,26 @@ function NewDashboardModal(props: { onClose: () => void; open: boolean }) {
);
}
function ProductAnalytics() {
return (
<>
<ExampleFunnel />
<ExamplePath />
<ExampleTrend />
<ExampleCount />
</>
)
}
function PerformanceMonitoring() {
return (
<>
<PerfBreakdown />
<SlowestDomain />
<SessionsByErrors />
<SessionsByIssues />
</>
)
}
export default NewDashboardModal;