Added comparison tables.

This commit is contained in:
Mike Cao 2024-05-26 17:26:15 -07:00
parent 626fe14fc2
commit b7a7d4de4d
18 changed files with 220 additions and 168 deletions

View File

@ -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>

View File

@ -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",

View File

@ -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 (

View File

@ -4,4 +4,11 @@
.nav { .nav {
width: 200px; width: 200px;
margin-top: 40px;
}
.title {
color: var(--base800);
text-align: center;
font-weight: 700;
} }

View File

@ -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>
); );

View File

@ -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);

View File

@ -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({

View 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);
}

View 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;

View File

@ -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;
} }

View File

@ -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>

View File

@ -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;
}

View File

@ -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}>

View File

@ -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,

View File

@ -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>

View File

@ -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',

View File

@ -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
View File

@ -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"