mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-01 12:29:35 +01:00
Added comparison tables.
This commit is contained in:
parent
626fe14fc2
commit
b7a7d4de4d
24
README.md
24
README.md
@ -1,27 +1,25 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://github.com/shubhusion/umami/blob/master/src/assets/logo-white.svg" alt="Umami Logo" width="100">
|
<img src="https://umami.is/images/umami-logo.png" alt="Umami Logo" width="100">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Umami</h1>
|
<h1 align="center">Umami</h1>
|
||||||
|
|
||||||
<p align="center"><b>Empowering insights. Preserving privacy.</b></p>
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<i>Umami is a simple, fast, privacy-focused alternative to Google Analytics.</i>
|
<i>Umami is a simple, fast, privacy-focused alternative to Google Analytics.</i>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/umami-software/umami/releases">
|
<a href="https://github.com/umami-software/umami/releases">
|
||||||
<img src="https://img.shields.io/github/release/umami-software/umami.svg" alt="GitHub Release">
|
<img src="https://img.shields.io/github/release/umami-software/umami.svg" alt="GitHub Release" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/umami-software/umami/blob/master/LICENSE">
|
<a href="https://github.com/umami-software/umami/blob/master/LICENSE">
|
||||||
<img src="https://img.shields.io/github/license/umami-software/umami.svg" alt="MIT License">
|
<img src="https://img.shields.io/github/license/umami-software/umami.svg" alt="MIT License" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/umami-software/umami/actions">
|
<a href="https://github.com/umami-software/umami/actions">
|
||||||
<img src="https://img.shields.io/github/actions/workflow/status/umami-software/umami/ci.yml" alt="Build Status">
|
<img src="https://img.shields.io/github/actions/workflow/status/umami-software/umami/ci.yml" alt="Build Status" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://analytics.umami.is/share/LGazGOecbDtaIwDr/umami.is" style="text-decoration: none;">
|
<a href="https://analytics.umami.is/share/LGazGOecbDtaIwDr/umami.is" style="text-decoration: none;">
|
||||||
<img src="https://img.shields.io/badge/Try%20Demo%20Now-Click%20Here-brightgreen" alt="Umami Demo">
|
<img src="https://img.shields.io/badge/Try%20Demo%20Now-Click%20Here-brightgreen" alt="Umami Demo" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -128,20 +126,20 @@ docker compose up --force-recreate
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📞 Contact
|
## 🛟 Support
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/umami-software/umami">
|
<a href="https://github.com/umami-software/umami">
|
||||||
<img src="https://img.shields.io/badge/GitHub--blue?style=social&logo=github" alt="GitHub">
|
<img src="https://img.shields.io/badge/GitHub--blue?style=social&logo=github" alt="GitHub" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://twitter.com/umami_software">
|
<a href="https://twitter.com/umami_software">
|
||||||
<img src="https://img.shields.io/badge/Twitter--blue?style=social&logo=twitter" alt="Twitter">
|
<img src="https://img.shields.io/badge/Twitter--blue?style=social&logo=twitter" alt="Twitter" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://linkedin.com/company/umami-software">
|
<a href="https://linkedin.com/company/umami-software">
|
||||||
<img src="https://img.shields.io/badge/LinkedIn--blue?style=social&logo=linkedin" alt="LinkedIn">
|
<img src="https://img.shields.io/badge/LinkedIn--blue?style=social&logo=linkedin" alt="LinkedIn" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.com/invite/4dz4zcXYrQ">
|
<a href="https://umami.is/discord">
|
||||||
<img src="https://img.shields.io/badge/Discord--blue?style=social&logo=discord" alt="Discord">
|
<img src="https://img.shields.io/badge/Discord--blue?style=social&logo=discord" alt="Discord" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -64,9 +64,9 @@
|
|||||||
".next/cache"
|
".next/cache"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clickhouse/client": "^1.0.1",
|
"@clickhouse/client": "^1.0.2",
|
||||||
"@fontsource/inter": "^4.5.15",
|
"@fontsource/inter": "^4.5.15",
|
||||||
"@prisma/client": "5.13.0",
|
"@prisma/client": "5.14.0",
|
||||||
"@prisma/extension-read-replicas": "^0.3.0",
|
"@prisma/extension-read-replicas": "^0.3.0",
|
||||||
"@react-spring/web": "^9.7.3",
|
"@react-spring/web": "^9.7.3",
|
||||||
"@tanstack/react-query": "^5.28.6",
|
"@tanstack/react-query": "^5.28.6",
|
||||||
@ -102,7 +102,7 @@
|
|||||||
"next-basics": "^0.39.0",
|
"next-basics": "^0.39.0",
|
||||||
"node-fetch": "^3.2.8",
|
"node-fetch": "^3.2.8",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prisma": "5.13.0",
|
"prisma": "5.14.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-basics": "^0.123.0",
|
"react-basics": "^0.123.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
|
@ -70,7 +70,7 @@ export function WebsiteMetricsBar({
|
|||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{ label: formatMessage(labels.previousPeriod), value: 'prev' },
|
{ label: formatMessage(labels.previousPeriod), value: 'prev' },
|
||||||
{ label: formatMessage(labels.yearOverYear), value: 'yoy' },
|
{ label: formatMessage(labels.previousYear), value: 'yoy' },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -4,4 +4,11 @@
|
|||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: var(--base800);
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import SideNav from 'components/layout/SideNav';
|
import SideNav from 'components/layout/SideNav';
|
||||||
import { useMessages, useNavigation } from 'components/hooks';
|
import { useDateRange, useMessages, useNavigation } from 'components/hooks';
|
||||||
import PagesTable from 'components/metrics/PagesTable';
|
import PagesTable from 'components/metrics/PagesTable';
|
||||||
import ReferrersTable from 'components/metrics/ReferrersTable';
|
import ReferrersTable from 'components/metrics/ReferrersTable';
|
||||||
import BrowsersTable from 'components/metrics/BrowsersTable';
|
import BrowsersTable from 'components/metrics/BrowsersTable';
|
||||||
@ -13,11 +14,12 @@ import LanguagesTable from 'components/metrics/LanguagesTable';
|
|||||||
import EventsTable from 'components/metrics/EventsTable';
|
import EventsTable from 'components/metrics/EventsTable';
|
||||||
import QueryParametersTable from 'components/metrics/QueryParametersTable';
|
import QueryParametersTable from 'components/metrics/QueryParametersTable';
|
||||||
import { Grid, GridRow } from 'components/layout/Grid';
|
import { Grid, GridRow } from 'components/layout/Grid';
|
||||||
import styles from './WebsiteCompareTables.module.css';
|
|
||||||
import { useContext, useState } from 'react';
|
|
||||||
import MetricsTable from 'components/metrics/MetricsTable';
|
import MetricsTable from 'components/metrics/MetricsTable';
|
||||||
import FilterLink from 'components/common/FilterLink';
|
import useStore from 'store/websites';
|
||||||
import { WebsiteContext } from '../WebsiteProvider';
|
import { getCompareDate } from 'lib/date';
|
||||||
|
import { formatNumber } from 'lib/format';
|
||||||
|
import ChangeLabel from 'components/metrics/ChangeLabel';
|
||||||
|
import styles from './WebsiteCompareTables.module.css';
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
url: PagesTable,
|
url: PagesTable,
|
||||||
@ -36,14 +38,15 @@ const views = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
||||||
const { domain } = useContext(WebsiteContext);
|
|
||||||
const [data, setData] = useState([]);
|
const [data, setData] = useState([]);
|
||||||
|
const { dateRange } = useDateRange(websiteId);
|
||||||
|
const dateCompare = useStore(state => state[websiteId]?.dateCompare);
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const {
|
const {
|
||||||
renderUrl,
|
renderUrl,
|
||||||
query: { view },
|
query: { view },
|
||||||
} = useNavigation();
|
} = useNavigation();
|
||||||
const Component: typeof MetricsTable = views[view] || (() => null);
|
const Component: typeof MetricsTable = views[view || 'url'] || (() => null);
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -108,27 +111,48 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const renderLabel = ({ x, y }, index) => {
|
const renderChange = ({ x, y }) => {
|
||||||
return (
|
const prev = data.find(d => d.x === x)?.y;
|
||||||
<FilterLink
|
const value = y - prev;
|
||||||
id={view}
|
const change = Math.abs(((y - prev) / prev) * 100);
|
||||||
value={x}
|
|
||||||
label={!x && formatMessage(labels.none)}
|
return !isNaN(change) && <ChangeLabel value={value}>{formatNumber(change)}%</ChangeLabel>;
|
||||||
externalUrl={
|
};
|
||||||
view === 'url' ? `${domain.startsWith('http') ? domain : `https://${domain}`}${x}` : null
|
|
||||||
}
|
const { startDate, endDate } = getCompareDate(
|
||||||
>
|
dateCompare,
|
||||||
{y} : {data[index]?.y} !
|
dateRange.startDate,
|
||||||
</FilterLink>
|
dateRange.endDate,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
startAt: startDate.getTime(),
|
||||||
|
endAt: endDate.getTime(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid className={styles.container}>
|
<Grid className={styles.container}>
|
||||||
<GridRow columns="compare">
|
<GridRow columns="compare">
|
||||||
<SideNav className={styles.nav} items={items} selectedKey={view} shallow={true} />
|
<SideNav className={styles.nav} items={items} selectedKey={view} shallow={true} />
|
||||||
<Component websiteId={websiteId} limit={20} showMore={false} onDataLoad={setData} />
|
<div>
|
||||||
<Component websiteId={websiteId} limit={20} showMore={false} renderLabel={renderLabel} />
|
<div className={styles.title}>{formatMessage(labels.previous)}</div>
|
||||||
|
<Component
|
||||||
|
websiteId={websiteId}
|
||||||
|
limit={20}
|
||||||
|
showMore={false}
|
||||||
|
onDataLoad={setData}
|
||||||
|
params={params}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className={styles.title}> {formatMessage(labels.current)}</div>
|
||||||
|
<Component
|
||||||
|
websiteId={websiteId}
|
||||||
|
limit={20}
|
||||||
|
showMore={false}
|
||||||
|
renderChange={renderChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</GridRow>
|
</GridRow>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,7 @@ import { useFilterParams } from '../useFilterParams';
|
|||||||
|
|
||||||
export function useWebsiteMetrics(
|
export function useWebsiteMetrics(
|
||||||
websiteId: string,
|
websiteId: string,
|
||||||
query: { type: string; limit: number; search: string },
|
queryParams: { type: string; limit: number; search: string; startAt?: number; endAt?: number },
|
||||||
options?: Omit<UseQueryOptions & { onDataLoad?: (data: any) => void }, 'queryKey' | 'queryFn'>,
|
options?: Omit<UseQueryOptions & { onDataLoad?: (data: any) => void }, 'queryKey' | 'queryFn'>,
|
||||||
) {
|
) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
@ -16,17 +16,17 @@ export function useWebsiteMetrics(
|
|||||||
{
|
{
|
||||||
websiteId,
|
websiteId,
|
||||||
...params,
|
...params,
|
||||||
...query,
|
...queryParams,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const filters = { ...params };
|
const filters = { ...params };
|
||||||
|
|
||||||
filters[query.type] = undefined;
|
filters[queryParams.type] = undefined;
|
||||||
|
|
||||||
const data = await get(`/websites/${websiteId}/metrics`, {
|
const data = await get(`/websites/${websiteId}/metrics`, {
|
||||||
...filters,
|
...filters,
|
||||||
...query,
|
...queryParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
options?.onDataLoad?.(data);
|
options?.onDataLoad?.(data);
|
||||||
|
@ -253,8 +253,10 @@ export const labels = defineMessages({
|
|||||||
defaultMessage: 'Understand how users nagivate through your website.',
|
defaultMessage: 'Understand how users nagivate through your website.',
|
||||||
},
|
},
|
||||||
compare: { id: 'label.compare', defaultMessage: 'Compare' },
|
compare: { id: 'label.compare', defaultMessage: 'Compare' },
|
||||||
|
current: { id: 'label.current', defaultMessage: 'Current' },
|
||||||
|
previous: { id: 'label.previous', defaultMessage: 'Previous' },
|
||||||
previousPeriod: { id: 'label.previous-period', defaultMessage: 'Previous period' },
|
previousPeriod: { id: 'label.previous-period', defaultMessage: 'Previous period' },
|
||||||
yearOverYear: { id: 'label.year-over-year', defaultMessage: 'Year over year' },
|
previousYear: { id: 'label.previous-year', defaultMessage: 'Previous year' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const messages = defineMessages({
|
export const messages = defineMessages({
|
||||||
|
31
src/components/metrics/ChangeLabel.module.css
Normal file
31
src/components/metrics/ChangeLabel.module.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0.1em 0.5em;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: var(--base500);
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.positive {
|
||||||
|
color: var(--green700);
|
||||||
|
background: var(--green100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.negative {
|
||||||
|
color: var(--red700);
|
||||||
|
background: var(--red100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.neutral {
|
||||||
|
color: var(--base700);
|
||||||
|
background: var(--base100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.new {
|
||||||
|
color: var(--blue900);
|
||||||
|
background: var(--blue100);
|
||||||
|
}
|
44
src/components/metrics/ChangeLabel.tsx
Normal file
44
src/components/metrics/ChangeLabel.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { Icon, Icons } from 'react-basics';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import styles from './ChangeLabel.module.css';
|
||||||
|
|
||||||
|
export function ChangeLabel({
|
||||||
|
value,
|
||||||
|
size,
|
||||||
|
reverseColors,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
value: number;
|
||||||
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
||||||
|
reverseColors?: boolean;
|
||||||
|
showPercentage?: boolean;
|
||||||
|
className?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
}) {
|
||||||
|
const positive = value * (reverseColors ? -1 : 1) >= 0;
|
||||||
|
const negative = value * (reverseColors ? -1 : 1) < 0;
|
||||||
|
const isNew = isNaN(value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(styles.label, className, {
|
||||||
|
[styles.positive]: positive,
|
||||||
|
[styles.negative]: negative,
|
||||||
|
[styles.neutral]: value === 0,
|
||||||
|
[styles.new]: isNew,
|
||||||
|
})}
|
||||||
|
title={value.toString()}
|
||||||
|
>
|
||||||
|
{!isNew && (
|
||||||
|
<Icon rotate={value === 0 ? 0 : positive || reverseColors ? -45 : 45} size={size}>
|
||||||
|
<Icons.ArrowRight />
|
||||||
|
</Icon>
|
||||||
|
)}
|
||||||
|
{children || value}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChangeLabel;
|
@ -72,9 +72,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
width: 50px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
text-align: end;
|
text-align: end;
|
||||||
margin-inline-end: 10px;
|
margin-inline-end: 5px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ export interface ListTableProps {
|
|||||||
metric?: string;
|
metric?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
renderLabel?: (row: any, index: number) => ReactNode;
|
renderLabel?: (row: any, index: number) => ReactNode;
|
||||||
|
renderChange?: (row: any, index: number) => ReactNode;
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
virtualize?: boolean;
|
virtualize?: boolean;
|
||||||
showPercentage?: boolean;
|
showPercentage?: boolean;
|
||||||
@ -27,6 +28,7 @@ export function ListTable({
|
|||||||
metric,
|
metric,
|
||||||
className,
|
className,
|
||||||
renderLabel,
|
renderLabel,
|
||||||
|
renderChange,
|
||||||
animate = true,
|
animate = true,
|
||||||
virtualize = false,
|
virtualize = false,
|
||||||
showPercentage = true,
|
showPercentage = true,
|
||||||
@ -45,6 +47,7 @@ export function ListTable({
|
|||||||
percent={percent}
|
percent={percent}
|
||||||
animate={animate && !virtualize}
|
animate={animate && !virtualize}
|
||||||
showPercentage={showPercentage}
|
showPercentage={showPercentage}
|
||||||
|
change={renderChange ? renderChange(row, index) : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -78,7 +81,7 @@ export function ListTable({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimatedRow = ({ label, value = 0, percent, animate, showPercentage = true }) => {
|
const AnimatedRow = ({ label, value = 0, percent, change, animate, showPercentage = true }) => {
|
||||||
const props = useSpring({
|
const props = useSpring({
|
||||||
width: percent,
|
width: percent,
|
||||||
y: value,
|
y: value,
|
||||||
@ -90,6 +93,7 @@ const AnimatedRow = ({ label, value = 0, percent, animate, showPercentage = true
|
|||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
<div className={styles.label}>{label}</div>
|
<div className={styles.label}>{label}</div>
|
||||||
<div className={styles.value}>
|
<div className={styles.value}>
|
||||||
|
{change}
|
||||||
<animated.div className={styles.value} title={props?.y as any}>
|
<animated.div className={styles.value} title={props?.y as any}>
|
||||||
{props.y?.to(formatLongNumber)}
|
{props.y?.to(formatLongNumber)}
|
||||||
</animated.div>
|
</animated.div>
|
||||||
|
@ -35,29 +35,3 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
color: var(--base800);
|
color: var(--base800);
|
||||||
}
|
}
|
||||||
|
|
||||||
.change {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 700;
|
|
||||||
padding: 0.1em 0.5em;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: var(--base500);
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.change.positive {
|
|
||||||
color: var(--green700);
|
|
||||||
background: var(--green100);
|
|
||||||
}
|
|
||||||
|
|
||||||
.change.negative {
|
|
||||||
color: var(--red700);
|
|
||||||
background: var(--red100);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Icon, Icons } from 'react-basics';
|
|
||||||
import { useSpring, animated } from '@react-spring/web';
|
import { useSpring, animated } from '@react-spring/web';
|
||||||
import { formatNumber } from 'lib/format';
|
import { formatNumber } from 'lib/format';
|
||||||
|
import ChangeLabel from 'components/metrics/ChangeLabel';
|
||||||
import styles from './MetricCard.module.css';
|
import styles from './MetricCard.module.css';
|
||||||
|
|
||||||
export interface MetricCardProps {
|
export interface MetricCardProps {
|
||||||
@ -31,30 +31,19 @@ export const MetricCard = ({
|
|||||||
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
|
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
|
||||||
const changeProps = useSpring({ x: Number(change) || 0, from: { x: 0 } });
|
const changeProps = useSpring({ x: Number(change) || 0, from: { x: 0 } });
|
||||||
const prevProps = useSpring({ x: Number(value - change) || 0, from: { x: 0 } });
|
const prevProps = useSpring({ x: Number(value - change) || 0, from: { x: 0 } });
|
||||||
const positive = change * (reverseColors ? -1 : 1) >= 0;
|
|
||||||
const negative = change * (reverseColors ? -1 : 1) < 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.card, className, showPrevious && styles.compare)}>
|
<div className={classNames(styles.card, className, showPrevious && styles.compare)}>
|
||||||
{showLabel && <div className={styles.label}>{label}</div>}
|
{showLabel && <div className={styles.label}>{label}</div>}
|
||||||
<animated.div className={styles.value} title={props?.x as any}>
|
<animated.div className={styles.value} title={value.toString()}>
|
||||||
{props?.x?.to(x => format(x))}
|
{props?.x?.to(x => format(x))}
|
||||||
</animated.div>
|
</animated.div>
|
||||||
{showChange && (
|
{showChange && (
|
||||||
<div
|
<ChangeLabel className={styles.change} value={change} reverseColors={reverseColors}>
|
||||||
className={classNames(styles.change, {
|
<animated.span title={change.toString()}>
|
||||||
[styles.positive]: positive,
|
|
||||||
[styles.negative]: negative,
|
|
||||||
[styles.hide]: ~~change === 0,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Icon rotate={positive || reverseColors ? -45 : 45} size={showPrevious ? 'md' : 'xs'}>
|
|
||||||
<Icons.ArrowRight />
|
|
||||||
</Icon>
|
|
||||||
<animated.span title={changeProps?.x as any}>
|
|
||||||
{changeProps?.x?.to(x => format(Math.abs(x)))}
|
{changeProps?.x?.to(x => format(Math.abs(x)))}
|
||||||
</animated.span>
|
</animated.span>
|
||||||
</div>
|
</ChangeLabel>
|
||||||
)}
|
)}
|
||||||
{showPrevious && (
|
{showPrevious && (
|
||||||
<animated.div className={classNames(styles.value, styles.prev)} title={prevProps?.x as any}>
|
<animated.div className={classNames(styles.value, styles.prev)} title={prevProps?.x as any}>
|
||||||
|
@ -27,6 +27,7 @@ export interface MetricsTableProps extends ListTableProps {
|
|||||||
onSearch?: (search: string) => void;
|
onSearch?: (search: string) => void;
|
||||||
allowSearch?: boolean;
|
allowSearch?: boolean;
|
||||||
showMore?: boolean;
|
showMore?: boolean;
|
||||||
|
params?: { [key: string]: any };
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ export function MetricsTable({
|
|||||||
delay = null,
|
delay = null,
|
||||||
allowSearch = false,
|
allowSearch = false,
|
||||||
showMore = true,
|
showMore = true,
|
||||||
|
params,
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: MetricsTableProps) {
|
}: MetricsTableProps) {
|
||||||
@ -51,7 +53,7 @@ export function MetricsTable({
|
|||||||
|
|
||||||
const { data, isLoading, isFetched, error } = useWebsiteMetrics(
|
const { data, isLoading, isFetched, error } = useWebsiteMetrics(
|
||||||
websiteId,
|
websiteId,
|
||||||
{ type, limit, search },
|
{ type, limit, search, ...params },
|
||||||
{
|
{
|
||||||
retryDelay: delay || DEFAULT_ANIMATION_DURATION,
|
retryDelay: delay || DEFAULT_ANIMATION_DURATION,
|
||||||
onDataLoad,
|
onDataLoad,
|
||||||
|
@ -55,7 +55,7 @@ export function PagesTable({ allowFilter, ...props }: PagesTableProps) {
|
|||||||
type={view}
|
type={view}
|
||||||
metric={formatMessage(labels.views)}
|
metric={formatMessage(labels.views)}
|
||||||
dataFilter={emptyFilter}
|
dataFilter={emptyFilter}
|
||||||
renderLabel={props.renderLabel || renderLink}
|
renderLabel={renderLink}
|
||||||
>
|
>
|
||||||
{allowFilter && <FilterButtons items={buttons} selectedKey={view} onSelect={handleSelect} />}
|
{allowFilter && <FilterButtons items={buttons} selectedKey={view} onSelect={handleSelect} />}
|
||||||
</MetricsTable>
|
</MetricsTable>
|
||||||
|
@ -46,7 +46,7 @@ export function PageviewsChart({ data, unit, isLoading, ...props }: PageviewsCha
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
label: `${formatMessage(labels.visits)} (VS)`,
|
label: `${formatMessage(labels.visits)} (${formatMessage(labels.previous)})`,
|
||||||
data: data.compare.pageviews,
|
data: data.compare.pageviews,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
backgroundColor: '#8601B0',
|
backgroundColor: '#8601B0',
|
||||||
@ -55,7 +55,7 @@ export function PageviewsChart({ data, unit, isLoading, ...props }: PageviewsCha
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
label: `${formatMessage(labels.visitors)} (VS)`,
|
label: `${formatMessage(labels.visitors)} (${formatMessage(labels.previous)})`,
|
||||||
data: data.compare.sessions,
|
data: data.compare.sessions,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
backgroundColor: '#f15bb5',
|
backgroundColor: '#f15bb5',
|
||||||
|
@ -87,7 +87,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
|||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
if (!process.env.DISABLE_BOT_CHECK && isbot(req.headers['user-agent'])) {
|
if (!process.env.DISABLE_BOT_CHECK && isbot(req.headers['user-agent'])) {
|
||||||
return ok(res);
|
return ok(res, { beep: 'boop' });
|
||||||
}
|
}
|
||||||
|
|
||||||
await useValidate(schema, req, res);
|
await useValidate(schema, req, res);
|
||||||
|
123
yarn.lock
123
yarn.lock
@ -1206,17 +1206,17 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@clickhouse/client-common@1.0.1":
|
"@clickhouse/client-common@1.0.2":
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.0.1.tgz#c7dde5eafaad8189649373ecc23354c7a32847b3"
|
resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.0.2.tgz#0fe0a4b33101c08d85c1279e4d74b2a92d42d996"
|
||||||
integrity sha512-3L6e0foP6VOktScoi6XWMjJyOpKCWgLUYgPVxP2c7gm6Kotq+iRmmmXtXTSg7B7uozcLZycTtPfIw2d80SYsYw==
|
integrity sha512-5oI2URFsXlzoysv5lAxoTUAnAHjXnaJ+1Jz3HUARR06Hkbr1sN0pGxfGwgjEd8E/lI4+UNdNEZicG2rlFnWSaA==
|
||||||
|
|
||||||
"@clickhouse/client@^1.0.1":
|
"@clickhouse/client@^1.0.2":
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.0.1.tgz#364db28d9ef9beaf19104f962c2b06090cb10468"
|
resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.0.2.tgz#7d9675e697ce697f1e6777f4c66ca6d3384e7325"
|
||||||
integrity sha512-fluUNnE2R7COJ6rn6DorYfi4D+AQn3t2qeBtEq37bQV3pD4EbKrBfKAwJ13e1lmMWdQ2B9bJUTMqGsRIDdWhJw==
|
integrity sha512-PaK0GLjIrlCpXevrp9gliOrurna6MjMMFBgZhDh6Zup8IuJCjQru4LkNsWUl3hJ2nua6+Ygag14iB8ILbeaIjg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@clickhouse/client-common" "1.0.1"
|
"@clickhouse/client-common" "1.0.2"
|
||||||
|
|
||||||
"@colors/colors@1.5.0":
|
"@colors/colors@1.5.0":
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
@ -2089,51 +2089,51 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||||
|
|
||||||
"@prisma/client@5.13.0":
|
"@prisma/client@5.14.0":
|
||||||
version "5.13.0"
|
version "5.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.13.0.tgz#b9f1d0983d714e982675201d8222a9ecb4bdad4a"
|
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.14.0.tgz#dadca5bb1137ddcebb454bbdaf89423823d3363f"
|
||||||
integrity sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==
|
integrity sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg==
|
||||||
|
|
||||||
"@prisma/debug@5.13.0":
|
"@prisma/debug@5.14.0":
|
||||||
version "5.13.0"
|
version "5.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.13.0.tgz#d88b0f6fafa0c216e20e284ed9fc30f1cbe45786"
|
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.14.0.tgz#1227c705893c38284f7c63d72441480ebaa12605"
|
||||||
integrity sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==
|
integrity sha512-iq56qBZuFfX3fCxoxT8gBX33lQzomBU0qIUaEj1RebsKVz1ob/BVH1XSBwwwvRVtZEV1b7Fxx2eVu34Ge/mg3w==
|
||||||
|
|
||||||
"@prisma/engines-version@5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b":
|
"@prisma/engines-version@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48":
|
||||||
version "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b"
|
version "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz#a72a4fb83ba1fd01ad45f795aa55168f60d34723"
|
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48.tgz#019c3c75a5c3276e580685fe48cdbfd181176858"
|
||||||
integrity sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==
|
integrity sha512-ip6pNkRo1UxWv+6toxNcYvItNYaqQjXdFNGJ+Nuk2eYtRoEdoF13wxo7/jsClJFFenMPVNVqXQDV0oveXnR1cA==
|
||||||
|
|
||||||
"@prisma/engines@5.13.0":
|
"@prisma/engines@5.14.0":
|
||||||
version "5.13.0"
|
version "5.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.13.0.tgz#8994ebf7b4e35aee7746a8465ec22738379bcab6"
|
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.14.0.tgz#2ee91dd2220a726c27c906fbea788bbb3efdac6e"
|
||||||
integrity sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==
|
integrity sha512-lgxkKZ6IEygVcw6IZZUlPIfLQ9hjSYAtHjZ5r64sCLDgVzsPFCi2XBBJgzPMkOQ5RHzUD4E/dVdpn9+ez8tk1A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/debug" "5.13.0"
|
"@prisma/debug" "5.14.0"
|
||||||
"@prisma/engines-version" "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b"
|
"@prisma/engines-version" "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48"
|
||||||
"@prisma/fetch-engine" "5.13.0"
|
"@prisma/fetch-engine" "5.14.0"
|
||||||
"@prisma/get-platform" "5.13.0"
|
"@prisma/get-platform" "5.14.0"
|
||||||
|
|
||||||
"@prisma/extension-read-replicas@^0.3.0":
|
"@prisma/extension-read-replicas@^0.3.0":
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/extension-read-replicas/-/extension-read-replicas-0.3.0.tgz#2842a7c928f957c1dd58a6256104797596d43426"
|
resolved "https://registry.yarnpkg.com/@prisma/extension-read-replicas/-/extension-read-replicas-0.3.0.tgz#2842a7c928f957c1dd58a6256104797596d43426"
|
||||||
integrity sha512-F9+rSmYday6GT2qjhJtkZcBOpLO5LtpvFcMGqrBDHf+78LEdSuxfFjOxYlNuqk4B+th62yxpbhfpmB9/Mca14Q==
|
integrity sha512-F9+rSmYday6GT2qjhJtkZcBOpLO5LtpvFcMGqrBDHf+78LEdSuxfFjOxYlNuqk4B+th62yxpbhfpmB9/Mca14Q==
|
||||||
|
|
||||||
"@prisma/fetch-engine@5.13.0":
|
"@prisma/fetch-engine@5.14.0":
|
||||||
version "5.13.0"
|
version "5.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz#9b6945c7b38bb59e840f8905b20ea7a3d059ca55"
|
resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.14.0.tgz#45297c118d4ec3fea55129886edd5a429da1f6da"
|
||||||
integrity sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==
|
integrity sha512-VrheA9y9DMURK5vu8OJoOgQpxOhas3qF0IBHJ8G/0X44k82kc8E0w98HCn2nhnbOOMwbWsJWXfLC2/F8n5u0gQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/debug" "5.13.0"
|
"@prisma/debug" "5.14.0"
|
||||||
"@prisma/engines-version" "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b"
|
"@prisma/engines-version" "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48"
|
||||||
"@prisma/get-platform" "5.13.0"
|
"@prisma/get-platform" "5.14.0"
|
||||||
|
|
||||||
"@prisma/get-platform@5.13.0":
|
"@prisma/get-platform@5.14.0":
|
||||||
version "5.13.0"
|
version "5.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.13.0.tgz#99ef909a52b9d79b64d72d2d3d8210c4892b6572"
|
resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.14.0.tgz#69112d3dde61905f59a65ed818f153e153ca40f0"
|
||||||
integrity sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==
|
integrity sha512-/yAyBvcEjRv41ynZrhdrPtHgk47xLRRq/o5eWGcUpBJ1YrUZTYB8EoPiopnP7iQrMATK8stXQdPOoVlrzuTQZw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/debug" "5.13.0"
|
"@prisma/debug" "5.14.0"
|
||||||
|
|
||||||
"@react-spring/animated@~9.7.3":
|
"@react-spring/animated@~9.7.3":
|
||||||
version "9.7.3"
|
version "9.7.3"
|
||||||
@ -8659,12 +8659,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
|
|||||||
ansi-styles "^5.0.0"
|
ansi-styles "^5.0.0"
|
||||||
react-is "^18.0.0"
|
react-is "^18.0.0"
|
||||||
|
|
||||||
prisma@5.13.0:
|
prisma@5.14.0:
|
||||||
version "5.13.0"
|
version "5.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.13.0.tgz#1f06e20ccfb6038ad68869e6eacd3b346f9d0851"
|
resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.14.0.tgz#ffc4696a43b044b636c3303b7aa98c13c2ade4dd"
|
||||||
integrity sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==
|
integrity sha512-gCNZco7y5XtjrnQYeDJTiVZmT/ncqCr5RY1/Cf8X2wgLRmyh9ayPAGBNziI4qEE4S6SxCH5omQLVo9lmURaJ/Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/engines" "5.13.0"
|
"@prisma/engines" "5.14.0"
|
||||||
|
|
||||||
process@^0.11.10:
|
process@^0.11.10:
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
@ -9590,16 +9590,7 @@ string-length@^4.0.1:
|
|||||||
char-regex "^1.0.2"
|
char-regex "^1.0.2"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
|
||||||
dependencies:
|
|
||||||
emoji-regex "^8.0.0"
|
|
||||||
is-fullwidth-code-point "^3.0.0"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
|
|
||||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@ -9672,14 +9663,7 @@ string.prototype.trimstart@^1.0.8:
|
|||||||
define-properties "^1.2.1"
|
define-properties "^1.2.1"
|
||||||
es-object-atoms "^1.0.0"
|
es-object-atoms "^1.0.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
|
||||||
dependencies:
|
|
||||||
ansi-regex "^5.0.1"
|
|
||||||
|
|
||||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
@ -10440,7 +10424,7 @@ which@^2.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe "^2.0.0"
|
isexe "^2.0.0"
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
@ -10458,15 +10442,6 @@ wrap-ansi@^6.2.0:
|
|||||||
string-width "^4.1.0"
|
string-width "^4.1.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
wrap-ansi@^7.0.0:
|
|
||||||
version "7.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
|
||||||
dependencies:
|
|
||||||
ansi-styles "^4.0.0"
|
|
||||||
string-width "^4.1.0"
|
|
||||||
strip-ansi "^6.0.0"
|
|
||||||
|
|
||||||
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
|
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||||
|
Loading…
Reference in New Issue
Block a user