+
{formatMessage(labels.filters)}
{Object.keys(params).map(key => {
if (!params[key]) {
return null;
diff --git a/components/metrics/FilterTags.module.css b/src/components/metrics/FilterTags.module.css
similarity index 63%
rename from components/metrics/FilterTags.module.css
rename to src/components/metrics/FilterTags.module.css
index 1c8458ac..c228dc4e 100644
--- a/components/metrics/FilterTags.module.css
+++ b/src/components/metrics/FilterTags.module.css
@@ -4,19 +4,23 @@
gap: 10px;
}
+.label {
+ font-weight: 700;
+}
+
.tag {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
font-size: var(--font-size-sm);
- border: 1px solid var(--base600);
+ border: 1px solid var(--blue400);
border-radius: var(--border-radius);
- line-height: 30px;
- padding: 0 8px;
+ padding: 8px 16px;
cursor: pointer;
+ background: var(--blue100);
}
.tag:hover {
- background: var(--base75);
+ background: var(--blue200);
}
diff --git a/components/metrics/LanguagesTable.js b/src/components/metrics/LanguagesTable.js
similarity index 81%
rename from components/metrics/LanguagesTable.js
rename to src/components/metrics/LanguagesTable.js
index e90a3425..7d220829 100644
--- a/components/metrics/LanguagesTable.js
+++ b/src/components/metrics/LanguagesTable.js
@@ -1,8 +1,8 @@
import MetricsTable from './MetricsTable';
import { percentFilter } from 'lib/filters';
-import useLanguageNames from 'hooks/useLanguageNames';
-import useLocale from 'hooks/useLocale';
-import useMessages from 'hooks/useMessages';
+import useLanguageNames from 'components/hooks/useLanguageNames';
+import useLocale from 'components/hooks/useLocale';
+import useMessages from 'components/hooks/useMessages';
export function LanguagesTable({ websiteId, onDataLoad, ...props }) {
const { formatMessage, labels } = useMessages();
diff --git a/components/metrics/Legend.js b/src/components/metrics/Legend.js
similarity index 89%
rename from components/metrics/Legend.js
rename to src/components/metrics/Legend.js
index 91135acb..ce8a3381 100644
--- a/components/metrics/Legend.js
+++ b/src/components/metrics/Legend.js
@@ -2,8 +2,8 @@ import { useEffect } from 'react';
import { StatusLight } from 'react-basics';
import { colord } from 'colord';
import classNames from 'classnames';
-import useLocale from 'hooks/useLocale';
-import useForceUpdate from 'hooks/useForceUpdate';
+import useLocale from 'components/hooks/useLocale';
+import useForceUpdate from 'components/hooks/useForceUpdate';
import styles from './Legend.module.css';
export function Legend({ chart }) {
@@ -22,7 +22,7 @@ export function Legend({ chart }) {
useEffect(() => {
forceUpdate();
- }, [locale]);
+ }, [locale, forceUpdate]);
if (!chart?.legend?.legendItems.find(({ text }) => text)) {
return null;
diff --git a/components/metrics/Legend.module.css b/src/components/metrics/Legend.module.css
similarity index 100%
rename from components/metrics/Legend.module.css
rename to src/components/metrics/Legend.module.css
diff --git a/components/metrics/DataTable.js b/src/components/metrics/ListTable.js
similarity index 94%
rename from components/metrics/DataTable.js
rename to src/components/metrics/ListTable.js
index e2e9462d..33ca59eb 100644
--- a/components/metrics/DataTable.js
+++ b/src/components/metrics/ListTable.js
@@ -5,10 +5,10 @@ import { useSpring, animated, config } from 'react-spring';
import classNames from 'classnames';
import Empty from 'components/common/Empty';
import { formatNumber, formatLongNumber } from 'lib/format';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import styles from './DataTable.module.css';
-export function DataTable({
+export function ListTable({
data = [],
title,
metric,
@@ -94,7 +94,7 @@ const AnimatedRow = ({
`${n}%`) }} />
- {props.width.to(n => `${n.toFixed(0)}%`)}
+ {props.width.to(n => `${n?.toFixed?.(0)}%`)}
)}
@@ -102,4 +102,4 @@ const AnimatedRow = ({
);
};
-export default DataTable;
+export default ListTable;
diff --git a/components/metrics/MetricCard.js b/src/components/metrics/MetricCard.js
similarity index 100%
rename from components/metrics/MetricCard.js
rename to src/components/metrics/MetricCard.js
diff --git a/components/metrics/MetricCard.module.css b/src/components/metrics/MetricCard.module.css
similarity index 100%
rename from components/metrics/MetricCard.module.css
rename to src/components/metrics/MetricCard.module.css
diff --git a/components/metrics/MetricsBar.js b/src/components/metrics/MetricsBar.js
similarity index 100%
rename from components/metrics/MetricsBar.js
rename to src/components/metrics/MetricsBar.js
diff --git a/components/metrics/MetricsBar.module.css b/src/components/metrics/MetricsBar.module.css
similarity index 100%
rename from components/metrics/MetricsBar.module.css
rename to src/components/metrics/MetricsBar.module.css
diff --git a/components/metrics/MetricsTable.js b/src/components/metrics/MetricsTable.js
similarity index 80%
rename from components/metrics/MetricsTable.js
rename to src/components/metrics/MetricsTable.js
index 50262798..6521c415 100644
--- a/components/metrics/MetricsTable.js
+++ b/src/components/metrics/MetricsTable.js
@@ -3,17 +3,17 @@ import { Loading, Icon, Text, Button } from 'react-basics';
import Link from 'next/link';
import firstBy from 'thenby';
import classNames from 'classnames';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { percentFilter } from 'lib/filters';
-import useDateRange from 'hooks/useDateRange';
-import usePageQuery from 'hooks/usePageQuery';
+import useDateRange from 'components/hooks/useDateRange';
+import usePageQuery from 'components/hooks/usePageQuery';
import ErrorMessage from 'components/common/ErrorMessage';
-import DataTable from './DataTable';
+import ListTable from './ListTable';
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
import Icons from 'components/icons';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import styles from './MetricsTable.module.css';
-import useLocale from 'hooks/useLocale';
+import useLocale from 'components/hooks/useLocale';
export function MetricsTable({
websiteId,
@@ -34,6 +34,7 @@ export function MetricsTable({
} = usePageQuery();
const { formatMessage, labels } = useMessages();
const { get, useQuery } = useApi();
+ const { dir } = useLocale();
const { data, isLoading, isFetched, error } = useQuery(
[
@@ -53,21 +54,18 @@ export function MetricsTable({
city,
},
],
- () =>
- get(`/websites/${websiteId}/metrics`, {
+ () => {
+ const filters = { url, title, referrer, os, browser, device, country, region, city };
+
+ filters[type] = undefined;
+
+ return get(`/websites/${websiteId}/metrics`, {
type,
startAt: +startDate,
endAt: +endDate,
- url,
- title,
- referrer,
- os,
- browser,
- device,
- country,
- region,
- city,
- }),
+ ...filters,
+ });
+ },
{ onSuccess: onDataLoad, retryDelay: delay || DEFAULT_ANIMATION_DURATION },
);
@@ -97,14 +95,13 @@ export function MetricsTable({
return items.sort(firstBy('y', -1).thenBy('x'));
}
return [];
- }, [data, error, dataFilter, filterOptions]);
- const { dir } = useLocale();
+ }, [data, error, dataFilter, filterOptions, limit]);
return (
{!data && isLoading && !isFetched &&
}
{error &&
}
- {data && !error &&
}
+ {data && !error &&
}
{data && !error && limit && (
diff --git a/components/metrics/MetricsTable.module.css b/src/components/metrics/MetricsTable.module.css
similarity index 100%
rename from components/metrics/MetricsTable.module.css
rename to src/components/metrics/MetricsTable.module.css
diff --git a/components/metrics/OSTable.js b/src/components/metrics/OSTable.js
similarity index 93%
rename from components/metrics/OSTable.js
rename to src/components/metrics/OSTable.js
index e4bc3234..90135124 100644
--- a/components/metrics/OSTable.js
+++ b/src/components/metrics/OSTable.js
@@ -1,6 +1,6 @@
import MetricsTable from './MetricsTable';
import FilterLink from 'components/common/FilterLink';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import { useRouter } from 'next/router';
export function OSTable({ websiteId, ...props }) {
diff --git a/components/metrics/PagesTable.js b/src/components/metrics/PagesTable.js
similarity index 91%
rename from components/metrics/PagesTable.js
rename to src/components/metrics/PagesTable.js
index 47e70318..f310643e 100644
--- a/components/metrics/PagesTable.js
+++ b/src/components/metrics/PagesTable.js
@@ -1,8 +1,8 @@
import FilterLink from 'components/common/FilterLink';
import FilterButtons from 'components/common/FilterButtons';
import MetricsTable from './MetricsTable';
-import useMessages from 'hooks/useMessages';
-import usePageQuery from 'hooks/usePageQuery';
+import useMessages from 'components/hooks/useMessages';
+import usePageQuery from 'components/hooks/usePageQuery';
import { emptyFilter } from 'lib/filters';
export function PagesTable({ websiteId, showFilters, ...props }) {
diff --git a/components/metrics/PageviewsChart.js b/src/components/metrics/PageviewsChart.js
similarity index 90%
rename from components/metrics/PageviewsChart.js
rename to src/components/metrics/PageviewsChart.js
index 1b481c48..096278f3 100644
--- a/components/metrics/PageviewsChart.js
+++ b/src/components/metrics/PageviewsChart.js
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import BarChart from './BarChart';
-import { useLocale, useTheme, useMessages } from 'hooks';
+import { useLocale, useTheme, useMessages } from 'components/hooks';
import { renderDateLabels, renderStatusTooltipPopup } from 'lib/charts';
export function PageviewsChart({ websiteId, data, unit, loading, ...props }) {
@@ -25,7 +25,7 @@ export function PageviewsChart({ websiteId, data, unit, loading, ...props }) {
...colors.chart.views,
},
];
- }, [data, locale, colors]);
+ }, [data, colors, formatMessage, labels]);
return (
{
- return regions[x] ? `${regions[x]}, ${countryNames[x.split('-')[0]]}` : x;
+ const renderLabel = (code, country) => {
+ const region = code.includes('-') ? code : `${country}-${code}`;
+ return regions[region] ? `${regions[region]}, ${countryNames[country]}` : region;
};
- const renderLink = ({ x: code }) => {
+ const renderLink = ({ x: code, country }) => {
return (
-
-
+
+
);
};
diff --git a/components/metrics/ScreenTable.js b/src/components/metrics/ScreenTable.js
similarity index 87%
rename from components/metrics/ScreenTable.js
rename to src/components/metrics/ScreenTable.js
index f8ef5f2e..8ed5c05a 100644
--- a/components/metrics/ScreenTable.js
+++ b/src/components/metrics/ScreenTable.js
@@ -1,5 +1,5 @@
import MetricsTable from './MetricsTable';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function ScreenTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
diff --git a/components/pages/console/TestConsole.js b/src/components/pages/console/TestConsole.js
similarity index 99%
rename from components/pages/console/TestConsole.js
rename to src/components/pages/console/TestConsole.js
index 060314fd..71eb27b4 100644
--- a/components/pages/console/TestConsole.js
+++ b/src/components/pages/console/TestConsole.js
@@ -3,7 +3,7 @@ import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import EventsChart from 'components/metrics/EventsChart';
import WebsiteChart from 'components/pages/websites/WebsiteChart';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
diff --git a/components/pages/console/TestConsole.module.css b/src/components/pages/console/TestConsole.module.css
similarity index 100%
rename from components/pages/console/TestConsole.module.css
rename to src/components/pages/console/TestConsole.module.css
diff --git a/components/pages/dashboard/Dashboard.js b/src/components/pages/dashboard/Dashboard.js
similarity index 93%
rename from components/pages/dashboard/Dashboard.js
rename to src/components/pages/dashboard/Dashboard.js
index 601e741b..8248cc81 100644
--- a/components/pages/dashboard/Dashboard.js
+++ b/src/components/pages/dashboard/Dashboard.js
@@ -7,10 +7,10 @@ import WebsiteChartList from 'components/pages/websites/WebsiteChartList';
import DashboardSettingsButton from 'components/pages/dashboard/DashboardSettingsButton';
import DashboardEdit from 'components/pages/dashboard/DashboardEdit';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import useDashboard from 'store/dashboard';
-import useMessages from 'hooks/useMessages';
-import useLocale from 'hooks/useLocale';
+import useMessages from 'components/hooks/useMessages';
+import useLocale from 'components/hooks/useLocale';
export function Dashboard() {
const { formatMessage, labels, messages } = useMessages();
diff --git a/components/pages/dashboard/DashboardEdit.js b/src/components/pages/dashboard/DashboardEdit.js
similarity index 98%
rename from components/pages/dashboard/DashboardEdit.js
rename to src/components/pages/dashboard/DashboardEdit.js
index 1810ee9b..4eb259d6 100644
--- a/components/pages/dashboard/DashboardEdit.js
+++ b/src/components/pages/dashboard/DashboardEdit.js
@@ -4,7 +4,7 @@ import classNames from 'classnames';
import { Button } from 'react-basics';
import { firstBy } from 'thenby';
import useDashboard, { saveDashboard } from 'store/dashboard';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import styles from './DashboardEdit.module.css';
const dragId = 'dashboard-website-ordering';
diff --git a/components/pages/dashboard/DashboardEdit.module.css b/src/components/pages/dashboard/DashboardEdit.module.css
similarity index 100%
rename from components/pages/dashboard/DashboardEdit.module.css
rename to src/components/pages/dashboard/DashboardEdit.module.css
diff --git a/components/pages/dashboard/DashboardSettingsButton.js b/src/components/pages/dashboard/DashboardSettingsButton.js
similarity index 86%
rename from components/pages/dashboard/DashboardSettingsButton.js
rename to src/components/pages/dashboard/DashboardSettingsButton.js
index 99dab14f..eeb1f947 100644
--- a/components/pages/dashboard/DashboardSettingsButton.js
+++ b/src/components/pages/dashboard/DashboardSettingsButton.js
@@ -1,7 +1,7 @@
-import { TooltipPopup, Icon, Text, Flexbox, Popup, Item, Button } from 'react-basics';
+import { TooltipPopup, Icon, Text, Flexbox, Button } from 'react-basics';
import Icons from 'components/icons';
import { saveDashboard } from 'store/dashboard';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function DashboardSettingsButton() {
const { formatMessage, labels } = useMessages();
diff --git a/components/pages/dashboard/DashboardSettingsButton.module.css b/src/components/pages/dashboard/DashboardSettingsButton.module.css
similarity index 100%
rename from components/pages/dashboard/DashboardSettingsButton.module.css
rename to src/components/pages/dashboard/DashboardSettingsButton.module.css
diff --git a/components/pages/event-data/EventDataMetricsBar.js b/src/components/pages/event-data/EventDataMetricsBar.js
similarity index 94%
rename from components/pages/event-data/EventDataMetricsBar.js
rename to src/components/pages/event-data/EventDataMetricsBar.js
index 90f065d5..5d984bea 100644
--- a/components/pages/event-data/EventDataMetricsBar.js
+++ b/src/components/pages/event-data/EventDataMetricsBar.js
@@ -1,7 +1,7 @@
import { Column, Row } from 'react-basics';
-import { useApi, useDateRange } from 'hooks';
+import { useApi, useDateRange } from 'components/hooks';
import MetricCard from 'components/metrics/MetricCard';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
import MetricsBar from 'components/metrics/MetricsBar';
import styles from './EventDataMetricsBar.module.css';
diff --git a/components/pages/event-data/EventDataMetricsBar.module.css b/src/components/pages/event-data/EventDataMetricsBar.module.css
similarity index 100%
rename from components/pages/event-data/EventDataMetricsBar.module.css
rename to src/components/pages/event-data/EventDataMetricsBar.module.css
diff --git a/components/pages/event-data/EventDataTable.js b/src/components/pages/event-data/EventDataTable.js
similarity index 94%
rename from components/pages/event-data/EventDataTable.js
rename to src/components/pages/event-data/EventDataTable.js
index b0d11b9d..c79916ce 100644
--- a/components/pages/event-data/EventDataTable.js
+++ b/src/components/pages/event-data/EventDataTable.js
@@ -1,6 +1,6 @@
import Link from 'next/link';
import { GridTable, GridColumn } from 'react-basics';
-import { useMessages, usePageQuery } from 'hooks';
+import { useMessages, usePageQuery } from 'components/hooks';
import Empty from 'components/common/Empty';
import { DATA_TYPES } from 'lib/constants';
diff --git a/components/pages/event-data/EventDataValueTable.js b/src/components/pages/event-data/EventDataValueTable.js
similarity index 96%
rename from components/pages/event-data/EventDataValueTable.js
rename to src/components/pages/event-data/EventDataValueTable.js
index 69ed10a7..75c11e32 100644
--- a/components/pages/event-data/EventDataValueTable.js
+++ b/src/components/pages/event-data/EventDataValueTable.js
@@ -1,5 +1,5 @@
import { GridTable, GridColumn, Button, Icon, Text } from 'react-basics';
-import { useMessages, usePageQuery } from 'hooks';
+import { useMessages, usePageQuery } from 'components/hooks';
import Link from 'next/link';
import Icons from 'components/icons';
import PageHeader from 'components/layout/PageHeader';
diff --git a/components/pages/login/LoginForm.js b/src/components/pages/login/LoginForm.js
similarity index 94%
rename from components/pages/login/LoginForm.js
rename to src/components/pages/login/LoginForm.js
index 67057759..797eea14 100644
--- a/components/pages/login/LoginForm.js
+++ b/src/components/pages/login/LoginForm.js
@@ -10,10 +10,10 @@ import {
Icon,
} from 'react-basics';
import { useRouter } from 'next/router';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { setUser } from 'store/app';
import { setClientAuthToken } from 'lib/client';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import Logo from 'assets/logo.svg';
import styles from './LoginForm.module.css';
diff --git a/components/pages/login/LoginForm.module.css b/src/components/pages/login/LoginForm.module.css
similarity index 100%
rename from components/pages/login/LoginForm.module.css
rename to src/components/pages/login/LoginForm.module.css
diff --git a/components/pages/login/LoginLayout.js b/src/components/pages/login/LoginLayout.js
similarity index 86%
rename from components/pages/login/LoginLayout.js
rename to src/components/pages/login/LoginLayout.js
index fc8ad461..d1bf47bb 100644
--- a/components/pages/login/LoginLayout.js
+++ b/src/components/pages/login/LoginLayout.js
@@ -1,5 +1,5 @@
import Head from 'next/head';
-import useLocale from 'hooks/useLocale';
+import useLocale from 'components/hooks/useLocale';
import styles from './LoginLayout.module.css';
export function LoginLayout({ children }) {
diff --git a/components/pages/login/LoginLayout.module.css b/src/components/pages/login/LoginLayout.module.css
similarity index 100%
rename from components/pages/login/LoginLayout.module.css
rename to src/components/pages/login/LoginLayout.module.css
diff --git a/components/pages/realtime/RealtimeCountries.js b/src/components/pages/realtime/RealtimeCountries.js
similarity index 78%
rename from components/pages/realtime/RealtimeCountries.js
rename to src/components/pages/realtime/RealtimeCountries.js
index 62964eab..7a61651a 100644
--- a/components/pages/realtime/RealtimeCountries.js
+++ b/src/components/pages/realtime/RealtimeCountries.js
@@ -1,9 +1,9 @@
import { useCallback } from 'react';
import { useRouter } from 'next/router';
-import DataTable from 'components/metrics/DataTable';
-import useLocale from 'hooks/useLocale';
-import useCountryNames from 'hooks/useCountryNames';
-import useMessages from 'hooks/useMessages';
+import ListTable from 'components/metrics/ListTable';
+import useLocale from 'components/hooks/useLocale';
+import useCountryNames from 'components/hooks/useCountryNames';
+import useMessages from 'components/hooks/useMessages';
import classNames from 'classnames';
import styles from './RealtimeCountries.module.css';
@@ -24,7 +24,7 @@ export function RealtimeCountries({ data }) {
);
return (
- __id);
diff --git a/components/pages/realtime/RealtimePage.module.css b/src/components/pages/realtime/RealtimePage.module.css
similarity index 100%
rename from components/pages/realtime/RealtimePage.module.css
rename to src/components/pages/realtime/RealtimePage.module.css
diff --git a/components/pages/realtime/RealtimeUrls.js b/src/components/pages/realtime/RealtimeUrls.js
similarity index 94%
rename from components/pages/realtime/RealtimeUrls.js
rename to src/components/pages/realtime/RealtimeUrls.js
index 18d8f2f6..674858b2 100644
--- a/components/pages/realtime/RealtimeUrls.js
+++ b/src/components/pages/realtime/RealtimeUrls.js
@@ -2,9 +2,9 @@ import { useMemo, useState } from 'react';
import { ButtonGroup, Button, Flexbox } from 'react-basics';
import firstBy from 'thenby';
import { percentFilter } from 'lib/filters';
-import DataTable from 'components/metrics/DataTable';
+import ListTable from 'components/metrics/ListTable';
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function RealtimeUrls({ websiteDomain, data = {} }) {
const { formatMessage, labels } = useMessages();
@@ -82,7 +82,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
{filter === FILTER_REFERRERS && (
-
)}
{filter === FILTER_PAGES && (
-
-
- {({ value, label }) => {
- return - {label}
;
- }}
-
+ {allowFilterSelect && (
+
+ {({ value, label }) => {
+ return - {label}
;
+ }}
+
+ )}
{value => {
return - {formatValue(value, name)}
;
diff --git a/components/pages/reports/FieldFilterForm.module.css b/src/components/pages/reports/FieldFilterForm.module.css
similarity index 100%
rename from components/pages/reports/FieldFilterForm.module.css
rename to src/components/pages/reports/FieldFilterForm.module.css
diff --git a/components/pages/reports/FieldSelectForm.js b/src/components/pages/reports/FieldSelectForm.js
similarity index 94%
rename from components/pages/reports/FieldSelectForm.js
rename to src/components/pages/reports/FieldSelectForm.js
index 434a5ae7..e7661511 100644
--- a/components/pages/reports/FieldSelectForm.js
+++ b/src/components/pages/reports/FieldSelectForm.js
@@ -1,5 +1,5 @@
import { Menu, Item, Form, FormRow } from 'react-basics';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
import styles from './FieldSelectForm.module.css';
export default function FieldSelectForm({ items, onSelect, showType = true }) {
diff --git a/components/pages/reports/FieldSelectForm.module.css b/src/components/pages/reports/FieldSelectForm.module.css
similarity index 100%
rename from components/pages/reports/FieldSelectForm.module.css
rename to src/components/pages/reports/FieldSelectForm.module.css
diff --git a/components/pages/reports/FilterSelectForm.js b/src/components/pages/reports/FilterSelectForm.js
similarity index 89%
rename from components/pages/reports/FilterSelectForm.js
rename to src/components/pages/reports/FilterSelectForm.js
index 38094bca..5265c741 100644
--- a/components/pages/reports/FilterSelectForm.js
+++ b/src/components/pages/reports/FilterSelectForm.js
@@ -1,7 +1,7 @@
import { useState } from 'react';
import FieldSelectForm from './FieldSelectForm';
import FieldFilterForm from './FieldFilterForm';
-import { useApi } from 'hooks';
+import { useApi } from 'components/hooks';
import { Loading } from 'react-basics';
function useValues(websiteId, type) {
@@ -18,7 +18,7 @@ function useValues(websiteId, type) {
return { data, error, isLoading };
}
-export default function FilterSelectForm({ websiteId, items, onSelect }) {
+export default function FilterSelectForm({ websiteId, items, onSelect, allowFilterSelect }) {
const [field, setField] = useState();
const { data, isLoading } = useValues(websiteId, field?.name);
@@ -37,6 +37,7 @@ export default function FilterSelectForm({ websiteId, items, onSelect }) {
type={field?.type}
values={data}
onSelect={onSelect}
+ allowFilterSelect={allowFilterSelect}
/>
);
}
diff --git a/components/pages/reports/ParameterList.js b/src/components/pages/reports/ParameterList.js
similarity index 95%
rename from components/pages/reports/ParameterList.js
rename to src/components/pages/reports/ParameterList.js
index a097cbdc..bf77dd9d 100644
--- a/components/pages/reports/ParameterList.js
+++ b/src/components/pages/reports/ParameterList.js
@@ -1,7 +1,7 @@
import { Icon, TooltipPopup } from 'react-basics';
import Icons from 'components/icons';
import Empty from 'components/common/Empty';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
import styles from './ParameterList.module.css';
export function ParameterList({ items = [], children, onRemove }) {
diff --git a/components/pages/reports/ParameterList.module.css b/src/components/pages/reports/ParameterList.module.css
similarity index 100%
rename from components/pages/reports/ParameterList.module.css
rename to src/components/pages/reports/ParameterList.module.css
diff --git a/components/pages/reports/PopupForm.js b/src/components/pages/reports/PopupForm.js
similarity index 100%
rename from components/pages/reports/PopupForm.js
rename to src/components/pages/reports/PopupForm.js
diff --git a/components/pages/reports/PopupForm.module.css b/src/components/pages/reports/PopupForm.module.css
similarity index 100%
rename from components/pages/reports/PopupForm.module.css
rename to src/components/pages/reports/PopupForm.module.css
diff --git a/components/pages/reports/Report.js b/src/components/pages/reports/Report.js
similarity index 92%
rename from components/pages/reports/Report.js
rename to src/components/pages/reports/Report.js
index 685ebb9f..de17aca6 100644
--- a/components/pages/reports/Report.js
+++ b/src/components/pages/reports/Report.js
@@ -1,7 +1,7 @@
import { createContext } from 'react';
import Page from 'components/layout/Page';
import styles from './reports.module.css';
-import { useReport } from 'hooks';
+import { useReport } from 'components/hooks';
export const ReportContext = createContext(null);
diff --git a/components/pages/reports/ReportBody.js b/src/components/pages/reports/ReportBody.js
similarity index 100%
rename from components/pages/reports/ReportBody.js
rename to src/components/pages/reports/ReportBody.js
diff --git a/components/pages/reports/ReportDetails.js b/src/components/pages/reports/ReportDetails.js
similarity index 100%
rename from components/pages/reports/ReportDetails.js
rename to src/components/pages/reports/ReportDetails.js
diff --git a/components/pages/reports/ReportHeader.js b/src/components/pages/reports/ReportHeader.js
similarity index 96%
rename from components/pages/reports/ReportHeader.js
rename to src/components/pages/reports/ReportHeader.js
index 394b1951..e81d6ece 100644
--- a/components/pages/reports/ReportHeader.js
+++ b/src/components/pages/reports/ReportHeader.js
@@ -2,7 +2,7 @@ import { useContext } from 'react';
import { useRouter } from 'next/router';
import { Icon, LoadingButton, InlineEditField, useToasts } from 'react-basics';
import PageHeader from 'components/layout/PageHeader';
-import { useMessages, useApi } from 'hooks';
+import { useMessages, useApi } from 'components/hooks';
import { ReportContext } from './Report';
import styles from './ReportHeader.module.css';
import reportStyles from './reports.module.css';
@@ -66,7 +66,7 @@ export function ReportHeader({ icon }) {
}>
diff --git a/components/pages/reports/ReportHeader.module.css b/src/components/pages/reports/ReportHeader.module.css
similarity index 100%
rename from components/pages/reports/ReportHeader.module.css
rename to src/components/pages/reports/ReportHeader.module.css
diff --git a/components/pages/reports/ReportMenu.js b/src/components/pages/reports/ReportMenu.js
similarity index 100%
rename from components/pages/reports/ReportMenu.js
rename to src/components/pages/reports/ReportMenu.js
diff --git a/components/pages/reports/ReportTemplates.js b/src/components/pages/reports/ReportTemplates.js
similarity index 91%
rename from components/pages/reports/ReportTemplates.js
rename to src/components/pages/reports/ReportTemplates.js
index 0f5e710d..2b934434 100644
--- a/components/pages/reports/ReportTemplates.js
+++ b/src/components/pages/reports/ReportTemplates.js
@@ -6,7 +6,7 @@ import Funnel from 'assets/funnel.svg';
import Lightbulb from 'assets/lightbulb.svg';
import Magnet from 'assets/magnet.svg';
import styles from './ReportTemplates.module.css';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
function ReportItem({ title, description, url, icon }) {
return (
@@ -30,7 +30,7 @@ function ReportItem({ title, description, url, icon }) {
);
}
-export function ReportTemplates() {
+export function ReportTemplates({ showHeader = true }) {
const { formatMessage, labels } = useMessages();
const reports = [
@@ -56,7 +56,7 @@ export function ReportTemplates() {
return (
-
+ {showHeader && }
{reports.map(({ title, description, url, icon }) => {
return (
diff --git a/components/pages/reports/ReportTemplates.module.css b/src/components/pages/reports/ReportTemplates.module.css
similarity index 100%
rename from components/pages/reports/ReportTemplates.module.css
rename to src/components/pages/reports/ReportTemplates.module.css
diff --git a/components/pages/reports/ReportsPage.js b/src/components/pages/reports/ReportsPage.js
similarity index 85%
rename from components/pages/reports/ReportsPage.js
rename to src/components/pages/reports/ReportsPage.js
index 95959832..bbb15a36 100644
--- a/components/pages/reports/ReportsPage.js
+++ b/src/components/pages/reports/ReportsPage.js
@@ -1,13 +1,13 @@
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
-import { useMessages, useReports } from 'hooks';
+import { useMessages, useReports } from 'components/hooks';
import Link from 'next/link';
import { Button, Icon, Icons, Text } from 'react-basics';
import ReportsTable from './ReportsTable';
export function ReportsPage() {
- const { formatMessage, labels, messages } = useMessages();
+ const { formatMessage, labels } = useMessages();
const {
reports,
error,
@@ -47,9 +47,7 @@ export function ReportsPage() {
showDomain={true}
/>
)}
- {!hasData && (
-
- )}
+ {!hasData &&
}
);
}
diff --git a/components/pages/reports/ReportsTable.js b/src/components/pages/reports/ReportsTable.js
similarity index 95%
rename from components/pages/reports/ReportsTable.js
rename to src/components/pages/reports/ReportsTable.js
index 98f5267a..4073fbec 100644
--- a/components/pages/reports/ReportsTable.js
+++ b/src/components/pages/reports/ReportsTable.js
@@ -1,8 +1,8 @@
import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm';
import LinkButton from 'components/common/LinkButton';
import SettingsTable from 'components/common/SettingsTable';
-import { useMessages } from 'hooks';
-import useUser from 'hooks/useUser';
+import { useMessages } from 'components/hooks';
+import useUser from 'components/hooks/useUser';
import { useState } from 'react';
import { Button, Flexbox, Icon, Icons, Modal, Text } from 'react-basics';
diff --git a/components/pages/reports/event-data/EventDataParameters.js b/src/components/pages/reports/event-data/EventDataParameters.js
similarity index 98%
rename from components/pages/reports/event-data/EventDataParameters.js
rename to src/components/pages/reports/event-data/EventDataParameters.js
index 8d4dbb62..a01a2972 100644
--- a/components/pages/reports/event-data/EventDataParameters.js
+++ b/src/components/pages/reports/event-data/EventDataParameters.js
@@ -1,5 +1,5 @@
import { useContext, useRef } from 'react';
-import { useApi, useMessages } from 'hooks';
+import { useApi, useMessages } from 'components/hooks';
import { Form, FormRow, FormButtons, SubmitButton, PopupTrigger, Icon, Popup } from 'react-basics';
import { ReportContext } from 'components/pages/reports/Report';
import Empty from 'components/common/Empty';
diff --git a/components/pages/reports/event-data/EventDataParameters.module.css b/src/components/pages/reports/event-data/EventDataParameters.module.css
similarity index 100%
rename from components/pages/reports/event-data/EventDataParameters.module.css
rename to src/components/pages/reports/event-data/EventDataParameters.module.css
diff --git a/components/pages/reports/event-data/EventDataReport.js b/src/components/pages/reports/event-data/EventDataReport.js
similarity index 100%
rename from components/pages/reports/event-data/EventDataReport.js
rename to src/components/pages/reports/event-data/EventDataReport.js
diff --git a/components/pages/reports/event-data/EventDataTable.js b/src/components/pages/reports/event-data/EventDataTable.js
similarity index 92%
rename from components/pages/reports/event-data/EventDataTable.js
rename to src/components/pages/reports/event-data/EventDataTable.js
index 81b9cad1..b6450261 100644
--- a/components/pages/reports/event-data/EventDataTable.js
+++ b/src/components/pages/reports/event-data/EventDataTable.js
@@ -1,6 +1,6 @@
import { useContext } from 'react';
import { GridTable, GridColumn } from 'react-basics';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
import { ReportContext } from '../Report';
export function EventDataTable() {
diff --git a/components/pages/reports/funnel/FunnelChart.js b/src/components/pages/reports/funnel/FunnelChart.js
similarity index 92%
rename from components/pages/reports/funnel/FunnelChart.js
rename to src/components/pages/reports/funnel/FunnelChart.js
index c35afe4e..829a3008 100644
--- a/components/pages/reports/funnel/FunnelChart.js
+++ b/src/components/pages/reports/funnel/FunnelChart.js
@@ -1,7 +1,7 @@
import { useCallback, useContext, useMemo } from 'react';
import { Loading, StatusLight } from 'react-basics';
-import useMessages from 'hooks/useMessages';
-import useTheme from 'hooks/useTheme';
+import useMessages from 'components/hooks/useMessages';
+import useTheme from 'components/hooks/useTheme';
import BarChart from 'components/metrics/BarChart';
import { formatLongNumber } from 'lib/format';
import styles from './FunnelChart.module.css';
@@ -52,7 +52,7 @@ export function FunnelChart({ className, loading }) {
...colors.chart.visitors,
},
];
- }, [data]);
+ }, [data, colors, formatMessage, labels]);
if (loading) {
return
;
diff --git a/components/pages/reports/funnel/FunnelChart.module.css b/src/components/pages/reports/funnel/FunnelChart.module.css
similarity index 100%
rename from components/pages/reports/funnel/FunnelChart.module.css
rename to src/components/pages/reports/funnel/FunnelChart.module.css
diff --git a/components/pages/reports/funnel/FunnelParameters.js b/src/components/pages/reports/funnel/FunnelParameters.js
similarity index 98%
rename from components/pages/reports/funnel/FunnelParameters.js
rename to src/components/pages/reports/funnel/FunnelParameters.js
index 03898db3..2c99a032 100644
--- a/components/pages/reports/funnel/FunnelParameters.js
+++ b/src/components/pages/reports/funnel/FunnelParameters.js
@@ -1,5 +1,5 @@
import { useContext, useRef } from 'react';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
import {
Icon,
Form,
diff --git a/components/pages/reports/funnel/FunnelReport.js b/src/components/pages/reports/funnel/FunnelReport.js
similarity index 100%
rename from components/pages/reports/funnel/FunnelReport.js
rename to src/components/pages/reports/funnel/FunnelReport.js
diff --git a/components/pages/reports/funnel/FunnelReport.module.css b/src/components/pages/reports/funnel/FunnelReport.module.css
similarity index 100%
rename from components/pages/reports/funnel/FunnelReport.module.css
rename to src/components/pages/reports/funnel/FunnelReport.module.css
diff --git a/components/pages/reports/funnel/FunnelTable.js b/src/components/pages/reports/funnel/FunnelTable.js
similarity index 77%
rename from components/pages/reports/funnel/FunnelTable.js
rename to src/components/pages/reports/funnel/FunnelTable.js
index 9ae8ab58..5ca2593c 100644
--- a/components/pages/reports/funnel/FunnelTable.js
+++ b/src/components/pages/reports/funnel/FunnelTable.js
@@ -1,13 +1,13 @@
import { useContext } from 'react';
-import DataTable from 'components/metrics/DataTable';
-import { useMessages } from 'hooks';
+import ListTable from 'components/metrics/ListTable';
+import { useMessages } from 'components/hooks';
import { ReportContext } from '../Report';
export function FunnelTable() {
const { report } = useContext(ReportContext);
const { formatMessage, labels } = useMessages();
return (
-
getRandomChars(16);
diff --git a/components/pages/settings/teams/TeamJoinForm.js b/src/components/pages/settings/teams/TeamJoinForm.js
similarity index 91%
rename from components/pages/settings/teams/TeamJoinForm.js
rename to src/components/pages/settings/teams/TeamJoinForm.js
index 34153aa1..23abcf00 100644
--- a/components/pages/settings/teams/TeamJoinForm.js
+++ b/src/components/pages/settings/teams/TeamJoinForm.js
@@ -8,8 +8,8 @@ import {
Button,
SubmitButton,
} from 'react-basics';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
export function TeamJoinForm({ onSave, onClose }) {
const { formatMessage, labels, getMessage } = useMessages();
diff --git a/components/pages/settings/teams/TeamLeaveForm.js b/src/components/pages/settings/teams/TeamLeaveForm.js
similarity index 90%
rename from components/pages/settings/teams/TeamLeaveForm.js
rename to src/components/pages/settings/teams/TeamLeaveForm.js
index 954006ab..8af2932d 100644
--- a/components/pages/settings/teams/TeamLeaveForm.js
+++ b/src/components/pages/settings/teams/TeamLeaveForm.js
@@ -1,6 +1,6 @@
import { Button, Form, FormButtons, SubmitButton } from 'react-basics';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
export function TeamLeaveForm({ teamId, userId, teamName, onSave, onClose }) {
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
diff --git a/components/pages/settings/teams/TeamMemberRemoveButton.js b/src/components/pages/settings/teams/TeamMemberRemoveButton.js
similarity index 74%
rename from components/pages/settings/teams/TeamMemberRemoveButton.js
rename to src/components/pages/settings/teams/TeamMemberRemoveButton.js
index 9f326476..3ec0f8b3 100644
--- a/components/pages/settings/teams/TeamMemberRemoveButton.js
+++ b/src/components/pages/settings/teams/TeamMemberRemoveButton.js
@@ -1,5 +1,5 @@
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
export function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) {
@@ -19,7 +19,11 @@ export function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) {
};
return (
- handleRemoveTeamMember()} disabled={disabled} loading={isLoading}>
+ handleRemoveTeamMember()}
+ disabled={disabled}
+ isLoading={isLoading}
+ >
diff --git a/components/pages/settings/teams/TeamMembers.js b/src/components/pages/settings/teams/TeamMembers.js
similarity index 86%
rename from components/pages/settings/teams/TeamMembers.js
rename to src/components/pages/settings/teams/TeamMembers.js
index 9762ef29..207ad72d 100644
--- a/components/pages/settings/teams/TeamMembers.js
+++ b/src/components/pages/settings/teams/TeamMembers.js
@@ -1,8 +1,8 @@
import { Loading, useToasts } from 'react-basics';
import TeamMembersTable from 'components/pages/settings/teams/TeamMembersTable';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
-import useApiFilter from 'hooks/useApiFilter';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
+import useApiFilter from 'components/hooks/useApiFilter';
export function TeamMembers({ teamId, readOnly }) {
const { showToast } = useToasts();
@@ -33,6 +33,7 @@ export function TeamMembers({ teamId, readOnly }) {
<>
-
- -
- {formatMessage(labels.teams)}
-
- - {values?.name}
-
- }
- />
+
- {formatMessage(labels.details)}
- {formatMessage(labels.members)}
diff --git a/components/pages/settings/teams/TeamWebsiteRemoveButton.js b/src/components/pages/settings/teams/TeamWebsiteRemoveButton.js
similarity index 78%
rename from components/pages/settings/teams/TeamWebsiteRemoveButton.js
rename to src/components/pages/settings/teams/TeamWebsiteRemoveButton.js
index a752313f..c0ddf95c 100644
--- a/components/pages/settings/teams/TeamWebsiteRemoveButton.js
+++ b/src/components/pages/settings/teams/TeamWebsiteRemoveButton.js
@@ -1,5 +1,5 @@
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
export function TeamWebsiteRemoveButton({ teamId, websiteId, onSave }) {
@@ -19,7 +19,7 @@ export function TeamWebsiteRemoveButton({ teamId, websiteId, onSave }) {
};
return (
- handleRemoveTeamMember()} loading={isLoading}>
+ handleRemoveTeamMember()} isLoading={isLoading}>
diff --git a/components/pages/settings/teams/TeamWebsites.js b/src/components/pages/settings/teams/TeamWebsites.js
similarity index 92%
rename from components/pages/settings/teams/TeamWebsites.js
rename to src/components/pages/settings/teams/TeamWebsites.js
index 2ae344f5..27fe2f85 100644
--- a/components/pages/settings/teams/TeamWebsites.js
+++ b/src/components/pages/settings/teams/TeamWebsites.js
@@ -11,9 +11,9 @@ import {
} from 'react-basics';
import TeamWebsitesTable from 'components/pages/settings/teams/TeamWebsitesTable';
import TeamAddWebsiteForm from 'components/pages/settings/teams/TeamAddWebsiteForm';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
-import useApiFilter from 'hooks/useApiFilter';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
+import useApiFilter from 'components/hooks/useApiFilter';
export function TeamWebsites({ teamId }) {
const { showToast } = useToasts();
diff --git a/components/pages/settings/teams/TeamWebsitesTable.js b/src/components/pages/settings/teams/TeamWebsitesTable.js
similarity index 82%
rename from components/pages/settings/teams/TeamWebsitesTable.js
rename to src/components/pages/settings/teams/TeamWebsitesTable.js
index 564c8a78..5ce08f35 100644
--- a/components/pages/settings/teams/TeamWebsitesTable.js
+++ b/src/components/pages/settings/teams/TeamWebsitesTable.js
@@ -1,10 +1,9 @@
-import useMessages from 'hooks/useMessages';
-import useUser from 'hooks/useUser';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
import Link from 'next/link';
import { Button, Icon, Icons, Text } from 'react-basics';
import TeamWebsiteRemoveButton from './TeamWebsiteRemoveButton';
import SettingsTable from 'components/common/SettingsTable';
-import useConfig from 'hooks/useConfig';
export function TeamWebsitesTable({
data = [],
@@ -13,9 +12,9 @@ export function TeamWebsitesTable({
onFilterChange,
onPageChange,
onPageSizeChange,
+ openExternal = false,
}) {
const { formatMessage, labels } = useMessages();
- const { openExternal } = useConfig();
const { user } = useUser();
const columns = [
@@ -55,11 +54,7 @@ export function TeamWebsitesTable({
{canRemove && (
-
+
)}
>
);
diff --git a/components/pages/settings/teams/TeamsList.js b/src/components/pages/settings/teams/TeamsList.js
similarity index 92%
rename from components/pages/settings/teams/TeamsList.js
rename to src/components/pages/settings/teams/TeamsList.js
index 061100f6..76a87b0c 100644
--- a/components/pages/settings/teams/TeamsList.js
+++ b/src/components/pages/settings/teams/TeamsList.js
@@ -4,16 +4,16 @@ import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import TeamAddForm from 'components/pages/settings/teams/TeamAddForm';
import TeamsTable from 'components/pages/settings/teams/TeamsTable';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
-import useUser from 'hooks/useUser';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
import { ROLES } from 'lib/constants';
import { useState } from 'react';
import { Button, Flexbox, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
import TeamJoinForm from './TeamJoinForm';
-import useApiFilter from 'hooks/useApiFilter';
+import useApiFilter from 'components/hooks/useApiFilter';
-export default function TeamsList() {
+export function TeamsList() {
const { user } = useUser();
const { formatMessage, labels, messages } = useMessages();
const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
@@ -114,3 +114,5 @@ export default function TeamsList() {
);
}
+
+export default TeamsList;
diff --git a/components/pages/settings/teams/TeamsTable.js b/src/components/pages/settings/teams/TeamsTable.js
similarity index 95%
rename from components/pages/settings/teams/TeamsTable.js
rename to src/components/pages/settings/teams/TeamsTable.js
index e35fb839..e1710783 100644
--- a/components/pages/settings/teams/TeamsTable.js
+++ b/src/components/pages/settings/teams/TeamsTable.js
@@ -1,7 +1,7 @@
import SettingsTable from 'components/common/SettingsTable';
-import useLocale from 'hooks/useLocale';
-import useMessages from 'hooks/useMessages';
-import useUser from 'hooks/useUser';
+import useLocale from 'components/hooks/useLocale';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
import { ROLES } from 'lib/constants';
import Link from 'next/link';
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
diff --git a/components/pages/settings/teams/WebsiteTags.js b/src/components/pages/settings/teams/WebsiteTags.js
similarity index 100%
rename from components/pages/settings/teams/WebsiteTags.js
rename to src/components/pages/settings/teams/WebsiteTags.js
diff --git a/components/pages/settings/teams/WebsiteTags.module.css b/src/components/pages/settings/teams/WebsiteTags.module.css
similarity index 100%
rename from components/pages/settings/teams/WebsiteTags.module.css
rename to src/components/pages/settings/teams/WebsiteTags.module.css
diff --git a/components/pages/settings/users/UserAddButton.js b/src/components/pages/settings/users/UserAddButton.js
similarity index 92%
rename from components/pages/settings/users/UserAddButton.js
rename to src/components/pages/settings/users/UserAddButton.js
index a461f39d..8b691362 100644
--- a/components/pages/settings/users/UserAddButton.js
+++ b/src/components/pages/settings/users/UserAddButton.js
@@ -1,6 +1,6 @@
import { Button, Icon, Text, Modal, Icons, ModalTrigger } from 'react-basics';
import UserAddForm from './UserAddForm';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function UserAddButton({ onSave }) {
const { formatMessage, labels } = useMessages();
diff --git a/components/pages/settings/users/UserAddForm.js b/src/components/pages/settings/users/UserAddForm.js
similarity index 95%
rename from components/pages/settings/users/UserAddForm.js
rename to src/components/pages/settings/users/UserAddForm.js
index bb408749..38c1bedd 100644
--- a/components/pages/settings/users/UserAddForm.js
+++ b/src/components/pages/settings/users/UserAddForm.js
@@ -10,9 +10,9 @@ import {
SubmitButton,
Button,
} from 'react-basics';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { ROLES } from 'lib/constants';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function UserAddForm({ onSave, onClose }) {
const { post, useMutation } = useApi();
diff --git a/components/pages/settings/users/UserDeleteForm.js b/src/components/pages/settings/users/UserDeleteForm.js
similarity index 91%
rename from components/pages/settings/users/UserDeleteForm.js
rename to src/components/pages/settings/users/UserDeleteForm.js
index bd412e44..5a47fdc1 100644
--- a/components/pages/settings/users/UserDeleteForm.js
+++ b/src/components/pages/settings/users/UserDeleteForm.js
@@ -1,7 +1,7 @@
import { useMutation } from '@tanstack/react-query';
import { Button, Form, FormButtons, SubmitButton } from 'react-basics';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
export function UserDeleteForm({ userId, username, onSave, onClose }) {
const { formatMessage, FormattedMessage, labels, messages } = useMessages();
diff --git a/components/pages/settings/users/UserEditForm.js b/src/components/pages/settings/users/UserEditForm.js
similarity index 95%
rename from components/pages/settings/users/UserEditForm.js
rename to src/components/pages/settings/users/UserEditForm.js
index 887531e8..157c9ad6 100644
--- a/components/pages/settings/users/UserEditForm.js
+++ b/src/components/pages/settings/users/UserEditForm.js
@@ -9,9 +9,9 @@ import {
SubmitButton,
PasswordField,
} from 'react-basics';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { ROLES } from 'lib/constants';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function UserEditForm({ userId, data, onSave }) {
const { formatMessage, labels, messages } = useMessages();
diff --git a/components/pages/settings/users/UserSettings.js b/src/components/pages/settings/users/UserSettings.js
similarity index 74%
rename from components/pages/settings/users/UserSettings.js
rename to src/components/pages/settings/users/UserSettings.js
index d703e964..5fadf1a1 100644
--- a/components/pages/settings/users/UserSettings.js
+++ b/src/components/pages/settings/users/UserSettings.js
@@ -1,12 +1,11 @@
import { useEffect, useState } from 'react';
-import { Breadcrumbs, Item, Tabs, useToasts } from 'react-basics';
-import Link from 'next/link';
-import UserEditForm from 'components/pages/settings/users//UserEditForm';
+import { Item, Tabs, useToasts } from 'react-basics';
+import UserEditForm from 'components/pages/settings/users/UserEditForm';
import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import UserWebsites from './UserWebsites';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function UserSettings({ userId }) {
const { formatMessage, labels, messages } = useMessages();
@@ -44,16 +43,7 @@ export function UserSettings({ userId }) {
return (
-
- -
- {formatMessage(labels.users)}
-
- - {values?.username}
-
- }
- />
+
- {formatMessage(labels.details)}
- {formatMessage(labels.websites)}
diff --git a/src/components/pages/settings/users/UserWebsites.js b/src/components/pages/settings/users/UserWebsites.js
new file mode 100644
index 00000000..14127275
--- /dev/null
+++ b/src/components/pages/settings/users/UserWebsites.js
@@ -0,0 +1,36 @@
+import Page from 'components/layout/Page';
+import useApi from 'components/hooks/useApi';
+import WebsitesTable from 'components/pages/settings/websites/WebsitesTable';
+import useApiFilter from 'components/hooks/useApiFilter';
+
+export function UserWebsites({ userId }) {
+ const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
+ useApiFilter();
+ const { get, useQuery } = useApi();
+ const { data, isLoading, error } = useQuery(
+ ['user:websites', userId, filter, page, pageSize],
+ () =>
+ get(`/users/${userId}/websites`, {
+ filter,
+ page,
+ pageSize,
+ }),
+ );
+ const hasData = data && data.length !== 0;
+
+ return (
+
+ {hasData && (
+
+ )}
+
+ );
+}
+
+export default UserWebsites;
diff --git a/components/pages/settings/users/UsersList.js b/src/components/pages/settings/users/UsersList.js
similarity index 90%
rename from components/pages/settings/users/UsersList.js
rename to src/components/pages/settings/users/UsersList.js
index 614aabef..0bc8612e 100644
--- a/components/pages/settings/users/UsersList.js
+++ b/src/components/pages/settings/users/UsersList.js
@@ -4,10 +4,10 @@ import PageHeader from 'components/layout/PageHeader';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import UsersTable from './UsersTable';
import UserAddButton from './UserAddButton';
-import useApi from 'hooks/useApi';
-import useUser from 'hooks/useUser';
-import useMessages from 'hooks/useMessages';
-import useApiFilter from 'hooks/useApiFilter';
+import useApi from 'components/hooks/useApi';
+import useUser from 'components/hooks/useUser';
+import useMessages from 'components/hooks/useMessages';
+import useApiFilter from 'components/hooks/useApiFilter';
export function UsersList() {
const { formatMessage, labels, messages } = useMessages();
diff --git a/components/pages/settings/users/UsersTable.js b/src/components/pages/settings/users/UsersTable.js
similarity index 93%
rename from components/pages/settings/users/UsersTable.js
rename to src/components/pages/settings/users/UsersTable.js
index f4c9dd77..1a93710d 100644
--- a/components/pages/settings/users/UsersTable.js
+++ b/src/components/pages/settings/users/UsersTable.js
@@ -1,12 +1,12 @@
import { Button, Text, Icon, Icons, ModalTrigger, Modal } from 'react-basics';
import { formatDistance } from 'date-fns';
import Link from 'next/link';
-import useUser from 'hooks/useUser';
+import useUser from 'components/hooks/useUser';
import UserDeleteForm from './UserDeleteForm';
import { ROLES } from 'lib/constants';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
import SettingsTable from 'components/common/SettingsTable';
-import useLocale from 'hooks/useLocale';
+import useLocale from 'components/hooks/useLocale';
export function UsersTable({
data = { data: [] },
@@ -54,7 +54,7 @@ export function UsersTable({
onPageSizeChange={onPageSizeChange}
filterValue={filterValue}
>
- {(row, keys, rowIndex) => {
+ {row => {
return (
<>
diff --git a/components/pages/settings/websites/ShareUrl.js b/src/components/pages/settings/websites/ShareUrl.js
similarity index 94%
rename from components/pages/settings/websites/ShareUrl.js
rename to src/components/pages/settings/websites/ShareUrl.js
index 7f0b352c..a7b7fc2f 100644
--- a/components/pages/settings/websites/ShareUrl.js
+++ b/src/components/pages/settings/websites/ShareUrl.js
@@ -11,8 +11,8 @@ import {
import { useEffect, useMemo, useRef, useState } from 'react';
import { getRandomChars } from 'next-basics';
import { useRouter } from 'next/router';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
const generateId = () => getRandomChars(16);
@@ -31,7 +31,7 @@ export function ShareUrl({ websiteId, data, onSave }) {
`${process.env.analyticsUrl || location.origin}${basePath}/share/${id}/${encodeURIComponent(
name,
)}`,
- [id, name],
+ [id, name, basePath],
);
const handleSubmit = async data => {
diff --git a/components/pages/settings/websites/TrackingCode.js b/src/components/pages/settings/websites/TrackingCode.js
similarity index 53%
rename from components/pages/settings/websites/TrackingCode.js
rename to src/components/pages/settings/websites/TrackingCode.js
index c847ed0d..d22f0d59 100644
--- a/components/pages/settings/websites/TrackingCode.js
+++ b/src/components/pages/settings/websites/TrackingCode.js
@@ -1,15 +1,19 @@
import { TextArea } from 'react-basics';
-import useMessages from 'hooks/useMessages';
-import useConfig from 'hooks/useConfig';
+import useMessages from 'components/hooks/useMessages';
+import useConfig from 'components/hooks/useConfig';
+import { useRouter } from 'next/router';
export function TrackingCode({ websiteId }) {
const { formatMessage, messages } = useMessages();
- const { basePath, trackerScriptName } = useConfig();
+ const { basePath } = useRouter();
+ const config = useConfig();
+
+ const trackerScriptName =
+ config?.trackerScriptName?.split(',')?.map(n => n.trim())?.[0] || 'script.js';
+
const url = trackerScriptName?.startsWith('http')
? trackerScriptName
- : `${location.origin}${basePath}/${
- trackerScriptName?.split(',')?.map(n => n.trim())?.[0] || 'script.js'
- }`;
+ : `${process.env.analyticsUrl || location.origin}${basePath}/${trackerScriptName}`;
const code = ``;
diff --git a/components/pages/settings/websites/WebsiteAddForm.js b/src/components/pages/settings/websites/WebsiteAddForm.js
similarity index 93%
rename from components/pages/settings/websites/WebsiteAddForm.js
rename to src/components/pages/settings/websites/WebsiteAddForm.js
index 77b850b4..371343ba 100644
--- a/components/pages/settings/websites/WebsiteAddForm.js
+++ b/src/components/pages/settings/websites/WebsiteAddForm.js
@@ -7,9 +7,9 @@ import {
Button,
SubmitButton,
} from 'react-basics';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { DOMAIN_REGEX } from 'lib/constants';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function WebsiteAddForm({ onSave, onClose }) {
const { formatMessage, labels, messages } = useMessages();
diff --git a/components/pages/settings/websites/WebsiteData.js b/src/components/pages/settings/websites/WebsiteData.js
similarity index 96%
rename from components/pages/settings/websites/WebsiteData.js
rename to src/components/pages/settings/websites/WebsiteData.js
index a2bc6bfa..08d6702e 100644
--- a/components/pages/settings/websites/WebsiteData.js
+++ b/src/components/pages/settings/websites/WebsiteData.js
@@ -1,7 +1,7 @@
import { Button, Modal, ModalTrigger, ActionForm } from 'react-basics';
import WebsiteDeleteForm from 'components/pages/settings/websites/WebsiteDeleteForm';
import WebsiteResetForm from 'components/pages/settings/websites/WebsiteResetForm';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function WebsiteData({ websiteId, onSave }) {
const { formatMessage, labels, messages } = useMessages();
diff --git a/components/pages/settings/websites/WebsiteDeleteForm.js b/src/components/pages/settings/websites/WebsiteDeleteForm.js
similarity index 92%
rename from components/pages/settings/websites/WebsiteDeleteForm.js
rename to src/components/pages/settings/websites/WebsiteDeleteForm.js
index c9e302fc..1548bddb 100644
--- a/components/pages/settings/websites/WebsiteDeleteForm.js
+++ b/src/components/pages/settings/websites/WebsiteDeleteForm.js
@@ -7,8 +7,8 @@ import {
SubmitButton,
TextField,
} from 'react-basics';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
const CONFIRM_VALUE = 'DELETE';
diff --git a/components/pages/settings/websites/WebsiteEditForm.js b/src/components/pages/settings/websites/WebsiteEditForm.js
similarity index 94%
rename from components/pages/settings/websites/WebsiteEditForm.js
rename to src/components/pages/settings/websites/WebsiteEditForm.js
index 89c62889..18ad0ac9 100644
--- a/components/pages/settings/websites/WebsiteEditForm.js
+++ b/src/components/pages/settings/websites/WebsiteEditForm.js
@@ -1,8 +1,8 @@
import { SubmitButton, Form, FormInput, FormRow, FormButtons, TextField } from 'react-basics';
import { useRef } from 'react';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { DOMAIN_REGEX } from 'lib/constants';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export function WebsiteEditForm({ websiteId, data, onSave }) {
const { formatMessage, labels, messages } = useMessages();
diff --git a/components/pages/settings/websites/WebsiteResetForm.js b/src/components/pages/settings/websites/WebsiteResetForm.js
similarity index 92%
rename from components/pages/settings/websites/WebsiteResetForm.js
rename to src/components/pages/settings/websites/WebsiteResetForm.js
index 5fc0acf7..9886429b 100644
--- a/components/pages/settings/websites/WebsiteResetForm.js
+++ b/src/components/pages/settings/websites/WebsiteResetForm.js
@@ -7,8 +7,8 @@ import {
SubmitButton,
TextField,
} from 'react-basics';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
const CONFIRM_VALUE = 'RESET';
diff --git a/components/pages/settings/websites/WebsiteSettings.js b/src/components/pages/settings/websites/WebsiteSettings.js
similarity index 81%
rename from components/pages/settings/websites/WebsiteSettings.js
rename to src/components/pages/settings/websites/WebsiteSettings.js
index 5a96fa8c..f73e0a87 100644
--- a/components/pages/settings/websites/WebsiteSettings.js
+++ b/src/components/pages/settings/websites/WebsiteSettings.js
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
-import { Breadcrumbs, Item, Tabs, useToasts, Button, Text, Icon, Icons } from 'react-basics';
+import { Item, Tabs, useToasts, Button, Text, Icon, Icons } from 'react-basics';
import { useRouter } from 'next/router';
import Link from 'next/link';
import Page from 'components/layout/Page';
@@ -8,14 +8,12 @@ import WebsiteEditForm from 'components/pages/settings/websites/WebsiteEditForm'
import WebsiteData from 'components/pages/settings/websites/WebsiteData';
import TrackingCode from 'components/pages/settings/websites/TrackingCode';
import ShareUrl from 'components/pages/settings/websites/ShareUrl';
-import useApi from 'hooks/useApi';
-import useMessages from 'hooks/useMessages';
-import useConfig from 'hooks/useConfig';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
-export function WebsiteSettings({ websiteId }) {
+export function WebsiteSettings({ websiteId, openExternal = false }) {
const router = useRouter();
const { formatMessage, labels, messages } = useMessages();
- const { openExternal } = useConfig();
const { get, useQuery } = useApi();
const { showToast } = useToasts();
const { data, isLoading } = useQuery(
@@ -51,16 +49,7 @@ export function WebsiteSettings({ websiteId }) {
return (
-
- -
- {formatMessage(labels.websites)}
-
- - {values?.name}
-
- }
- >
+
-
+
{showCharts && }
) : null;
diff --git a/components/pages/websites/WebsiteDetailsPage.js b/src/components/pages/websites/WebsiteDetailsPage.js
similarity index 93%
rename from components/pages/websites/WebsiteDetailsPage.js
rename to src/components/pages/websites/WebsiteDetailsPage.js
index 9e0519b2..222d94d9 100644
--- a/components/pages/websites/WebsiteDetailsPage.js
+++ b/src/components/pages/websites/WebsiteDetailsPage.js
@@ -3,10 +3,10 @@ import { useRouter } from 'next/router';
import Page from 'components/layout/Page';
import WebsiteChart from 'components/pages/websites/WebsiteChart';
import FilterTags from 'components/metrics/FilterTags';
-import usePageQuery from 'hooks/usePageQuery';
+import usePageQuery from 'components/hooks/usePageQuery';
import WebsiteTableView from './WebsiteTableView';
import WebsiteMenuView from './WebsiteMenuView';
-import { useWebsite } from 'hooks';
+import { useWebsite } from 'components/hooks';
import WebsiteHeader from './WebsiteHeader';
import { WebsiteMetricsBar } from './WebsiteMetricsBar';
@@ -22,12 +22,12 @@ export default function WebsiteDetailsPage({ websiteId }) {
return (
-
-
+
+
{!website && }
{website && (
<>
diff --git a/components/pages/websites/WebsiteEventData.js b/src/components/pages/websites/WebsiteEventData.js
similarity index 79%
rename from components/pages/websites/WebsiteEventData.js
rename to src/components/pages/websites/WebsiteEventData.js
index 7f9a6829..d38ca1ad 100644
--- a/components/pages/websites/WebsiteEventData.js
+++ b/src/components/pages/websites/WebsiteEventData.js
@@ -1,8 +1,8 @@
-import { Flexbox } from 'react-basics';
+import { Flexbox, Loading } from 'react-basics';
import EventDataTable from 'components/pages/event-data/EventDataTable';
import EventDataValueTable from 'components/pages/event-data/EventDataValueTable';
import { EventDataMetricsBar } from 'components/pages/event-data/EventDataMetricsBar';
-import { useDateRange, useApi, usePageQuery } from 'hooks';
+import { useDateRange, useApi, usePageQuery } from 'components/hooks';
import styles from './WebsiteEventData.module.css';
function useData(websiteId, event) {
@@ -28,13 +28,14 @@ export default function WebsiteEventData({ websiteId }) {
const {
query: { event },
} = usePageQuery();
- const { data } = useData(websiteId, event);
+ const { data, isLoading } = useData(websiteId, event);
return (
{!event && }
- {event && }
+ {isLoading && }
+ {event && data && }
);
}
diff --git a/components/pages/websites/WebsiteEventData.module.css b/src/components/pages/websites/WebsiteEventData.module.css
similarity index 100%
rename from components/pages/websites/WebsiteEventData.module.css
rename to src/components/pages/websites/WebsiteEventData.module.css
diff --git a/components/pages/websites/WebsiteEventDataPage.js b/src/components/pages/websites/WebsiteEventDataPage.js
similarity index 100%
rename from components/pages/websites/WebsiteEventDataPage.js
rename to src/components/pages/websites/WebsiteEventDataPage.js
diff --git a/components/pages/websites/WebsiteHeader.js b/src/components/pages/websites/WebsiteHeader.js
similarity index 94%
rename from components/pages/websites/WebsiteHeader.js
rename to src/components/pages/websites/WebsiteHeader.js
index 0790397f..fb4e0986 100644
--- a/components/pages/websites/WebsiteHeader.js
+++ b/src/components/pages/websites/WebsiteHeader.js
@@ -1,11 +1,11 @@
import classNames from 'classnames';
-import { Flexbox, Row, Column, Text, Button, Icon } from 'react-basics';
+import { Row, Column, Text, Button, Icon } from 'react-basics';
import Link from 'next/link';
import { useRouter } from 'next/router';
import Favicon from 'components/common/Favicon';
import ActiveUsers from 'components/metrics/ActiveUsers';
import Icons from 'components/icons';
-import { useMessages, useWebsite } from 'hooks';
+import { useMessages, useWebsite } from 'components/hooks';
import styles from './WebsiteHeader.module.css';
export function WebsiteHeader({ websiteId, showLinks = true, children }) {
diff --git a/components/pages/websites/WebsiteHeader.module.css b/src/components/pages/websites/WebsiteHeader.module.css
similarity index 100%
rename from components/pages/websites/WebsiteHeader.module.css
rename to src/components/pages/websites/WebsiteHeader.module.css
diff --git a/components/pages/websites/WebsiteList.module.css b/src/components/pages/websites/WebsiteList.module.css
similarity index 100%
rename from components/pages/websites/WebsiteList.module.css
rename to src/components/pages/websites/WebsiteList.module.css
diff --git a/components/pages/websites/WebsiteMenuView.js b/src/components/pages/websites/WebsiteMenuView.js
similarity index 96%
rename from components/pages/websites/WebsiteMenuView.js
rename to src/components/pages/websites/WebsiteMenuView.js
index 39adb188..8c74d615 100644
--- a/components/pages/websites/WebsiteMenuView.js
+++ b/src/components/pages/websites/WebsiteMenuView.js
@@ -15,10 +15,10 @@ import ScreenTable from 'components/metrics/ScreenTable';
import EventsTable from 'components/metrics/EventsTable';
import Icons from 'components/icons';
import SideNav from 'components/layout/SideNav';
-import usePageQuery from 'hooks/usePageQuery';
-import useMessages from 'hooks/useMessages';
+import usePageQuery from 'components/hooks/usePageQuery';
+import useMessages from 'components/hooks/useMessages';
import styles from './WebsiteMenuView.module.css';
-import useLocale from 'hooks/useLocale';
+import useLocale from 'components/hooks/useLocale';
const views = {
url: PagesTable,
diff --git a/components/pages/websites/WebsiteMenuView.module.css b/src/components/pages/websites/WebsiteMenuView.module.css
similarity index 100%
rename from components/pages/websites/WebsiteMenuView.module.css
rename to src/components/pages/websites/WebsiteMenuView.module.css
diff --git a/components/pages/websites/WebsiteMetricsBar.js b/src/components/pages/websites/WebsiteMetricsBar.js
similarity index 66%
rename from components/pages/websites/WebsiteMetricsBar.js
rename to src/components/pages/websites/WebsiteMetricsBar.js
index 3683310c..7ba4a801 100644
--- a/components/pages/websites/WebsiteMetricsBar.js
+++ b/src/components/pages/websites/WebsiteMetricsBar.js
@@ -1,20 +1,24 @@
import classNames from 'classnames';
-import { Row, Column } from 'react-basics';
-import { formatShortTime } from 'lib/format';
-import MetricCard from 'components/metrics/MetricCard';
-import RefreshButton from 'components/input/RefreshButton';
+import { useApi, useDateRange, useMessages, usePageQuery, useSticky } from 'components/hooks';
import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
+import MetricCard from 'components/metrics/MetricCard';
import MetricsBar from 'components/metrics/MetricsBar';
-import { useApi, useDateRange, usePageQuery, useMessages, useSticky } from 'hooks';
+import FilterSelectForm from 'components/pages/reports/FilterSelectForm';
+import PopupForm from 'components/pages/reports/PopupForm';
+import { formatShortTime } from 'lib/format';
+import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row } from 'react-basics';
import styles from './WebsiteMetricsBar.module.css';
-export function WebsiteMetricsBar({ websiteId, sticky }) {
+export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
const { formatMessage, labels } = useMessages();
+
const { get, useQuery } = useApi();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange;
const { ref, isSticky } = useSticky({ enabled: sticky });
const {
+ resolveUrl,
+ router,
query: { url, referrer, title, os, browser, device, country, region, city },
} = usePageQuery();
@@ -39,6 +43,17 @@ export function WebsiteMetricsBar({ websiteId, sticky }) {
}),
);
+ const fieldOptions = [
+ { name: 'url', type: 'string', label: formatMessage(labels.url) },
+ { name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
+ { name: 'browser', type: 'string', label: formatMessage(labels.browser) },
+ { name: 'os', type: 'string', label: formatMessage(labels.os) },
+ { name: 'device', type: 'string', label: formatMessage(labels.device) },
+ { name: 'country', type: 'string', label: formatMessage(labels.country) },
+ { name: 'region', type: 'string', label: formatMessage(labels.region) },
+ { name: 'city', type: 'string', label: formatMessage(labels.city) },
+ ];
+
const { pageviews, uniques, bounces, totaltime } = data || {};
const num = Math.min(data && uniques.value, data && bounces.value);
const diffs = data && {
@@ -48,6 +63,40 @@ export function WebsiteMetricsBar({ websiteId, sticky }) {
totaltime: totaltime.value - totaltime.change,
};
+ const handleAddFilter = ({ name, value }) => {
+ router.push(resolveUrl({ [name]: value }));
+ };
+
+ const WebsiteFilterButton = () => {
+ return (
+
+
+
+ {close => {
+ return (
+
+ {
+ handleAddFilter(value);
+ close();
+ }}
+ allowFilterSelect={false}
+ />
+
+ );
+ }}
+
+
+ );
+ };
+
return (
-
+ {showFilter && }
diff --git a/components/pages/websites/WebsiteMetricsBar.module.css b/src/components/pages/websites/WebsiteMetricsBar.module.css
similarity index 100%
rename from components/pages/websites/WebsiteMetricsBar.module.css
rename to src/components/pages/websites/WebsiteMetricsBar.module.css
diff --git a/components/pages/websites/WebsiteReportsPage.js b/src/components/pages/websites/WebsiteReportsPage.js
similarity index 83%
rename from components/pages/websites/WebsiteReportsPage.js
rename to src/components/pages/websites/WebsiteReportsPage.js
index b04c50d1..40631839 100644
--- a/components/pages/websites/WebsiteReportsPage.js
+++ b/src/components/pages/websites/WebsiteReportsPage.js
@@ -1,13 +1,13 @@
-import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import Page from 'components/layout/Page';
+import Empty from 'components/common/Empty';
import ReportsTable from 'components/pages/reports/ReportsTable';
-import { useMessages, useWebsiteReports } from 'hooks';
+import { useMessages, useWebsiteReports } from 'components/hooks';
import Link from 'next/link';
import { Button, Flexbox, Icon, Icons, Text } from 'react-basics';
import WebsiteHeader from './WebsiteHeader';
export function WebsiteReportsPage({ websiteId }) {
- const { formatMessage, labels, messages } = useMessages();
+ const { formatMessage, labels } = useMessages();
const {
reports,
error,
@@ -48,7 +48,7 @@ export function WebsiteReportsPage({ websiteId }) {
filterValue={filter}
/>
)}
- {!hasData && }
+ {!hasData && }
);
}
diff --git a/components/pages/websites/WebsiteTableView.js b/src/components/pages/websites/WebsiteTableView.js
similarity index 100%
rename from components/pages/websites/WebsiteTableView.js
rename to src/components/pages/websites/WebsiteTableView.js
diff --git a/components/pages/websites/WebsiteTableView.module.css b/src/components/pages/websites/WebsiteTableView.module.css
similarity index 100%
rename from components/pages/websites/WebsiteTableView.module.css
rename to src/components/pages/websites/WebsiteTableView.module.css
diff --git a/components/pages/websites/WebsitesPage.js b/src/components/pages/websites/WebsitesPage.js
similarity index 93%
rename from components/pages/websites/WebsitesPage.js
rename to src/components/pages/websites/WebsitesPage.js
index fafc62af..61c29448 100644
--- a/components/pages/websites/WebsitesPage.js
+++ b/src/components/pages/websites/WebsitesPage.js
@@ -2,9 +2,8 @@ import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import WebsiteAddForm from 'components/pages/settings/websites/WebsiteAddForm';
import WebsiteList from 'components/pages/settings/websites/WebsitesList';
-import { useMessages } from 'hooks';
-import useUser from 'hooks/useUser';
-import useConfig from 'hooks/useConfig';
+import { useMessages } from 'components/hooks';
+import useUser from 'components/hooks/useUser';
import { ROLES } from 'lib/constants';
import { useState } from 'react';
import {
@@ -24,8 +23,8 @@ export function WebsitesPage() {
const [tab, setTab] = useState('my-websites');
const [fetch, setFetch] = useState(1);
const { user } = useUser();
- const { cloudMode } = useConfig();
const { showToast } = useToasts();
+ const cloudMode = Boolean(process.env.cloudMode);
const handleSave = async () => {
setFetch(fetch + 1);
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 00000000..f2ef13ca
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,117 @@
+/*
+export * from 'components/common/ConfirmDeleteForm';
+export * from 'components/common/Empty';
+export * from 'components/common/EmptyPlaceholder';
+export * from 'components/common/ErrorBoundary';
+export * from 'components/common/ErrorMessage';
+export * from 'components/common/Favicon';
+export * from 'components/common/FilterButtons';
+export * from 'components/common/FilterLink';
+export * from 'components/common/HamburgerButton';
+export * from 'components/common/HoverTooltip';
+export * from 'components/common/LinkButton';
+export * from 'components/common/MobileMenu';
+export * from 'components/common/Pager';
+export * from 'components/common/SettingsTable';
+export * from 'components/common/UpdateNotice';
+export * from 'components/common/WorldMap';
+
+export * from 'components/hooks/useApi';
+export * from 'components/hooks/useConfig';
+export * from 'components/hooks/useCountryNames';
+export * from 'components/hooks/useDateRange';
+export * from 'components/hooks/useDocumentClick';
+export * from 'components/hooks/useEscapeKey';
+export * from 'components/hooks/useFilters';
+export * from 'components/hooks/useForceUpdate';
+export * from 'components/hooks/useFormat';
+export * from 'components/hooks/useLanguageNames';
+export * from 'components/hooks/useLocale';
+export * from 'components/hooks/useMessages';
+export * from 'components/hooks/usePageQuery';
+export * from 'components/hooks/useReport';
+export * from 'components/hooks/useReports';
+export * from 'components/hooks/useRequireLogin';
+export * from 'components/hooks/useShareToken';
+export * from 'components/hooks/useSticky';
+export * from 'components/hooks/useTheme';
+export * from 'components/hooks/useTimezone';
+export * from 'components/hooks/useUser';
+export * from 'components/hooks/useWebsite';
+export * from 'components/hooks/useWebsiteReports';
+
+export * from 'components/input/DateFilter';
+export * from 'components/input/LanguageButton';
+export * from 'components/input/LogoutButton';
+export * from 'components/input/MonthSelect';
+export * from 'components/input/ProfileButton';
+export * from 'components/input/RefreshButton';
+export * from 'components/input/SettingsButton';
+export * from 'components/input/ThemeButton';
+export * from 'components/input/WebsiteDateFilter';
+export * from 'components/input/WebsiteSelect';
+
+export * from 'components/layout/AppLayout';
+export * from 'components/layout/Footer';
+export * from 'components/layout/Grid';
+export * from 'components/layout/Header';
+export * from 'components/layout/NavBar';
+export * from 'components/layout/NavGroup';
+export * from 'components/layout/Page';
+export * from 'components/layout/PageHeader';
+export * from 'components/layout/ReportsLayout';
+export * from 'components/layout/SettingsLayout';
+export * from 'components/layout/ShareLayout';
+export * from 'components/layout/SideNav';
+*/
+
+export * from 'components/hooks/useApi';
+export * from 'components/hooks/useConfig';
+export * from 'components/hooks/useCountryNames';
+export * from 'components/hooks/useDateRange';
+export * from 'components/hooks/useDocumentClick';
+export * from 'components/hooks/useEscapeKey';
+export * from 'components/hooks/useFilters';
+export * from 'components/hooks/useForceUpdate';
+export * from 'components/hooks/useFormat';
+export * from 'components/hooks/useLanguageNames';
+export * from 'components/hooks/useLocale';
+export * from 'components/hooks/useMessages';
+export * from 'components/hooks/usePageQuery';
+export * from 'components/hooks/useReport';
+export * from 'components/hooks/useReports';
+export * from 'components/hooks/useRequireLogin';
+export * from 'components/hooks/useShareToken';
+export * from 'components/hooks/useSticky';
+export * from 'components/hooks/useTheme';
+export * from 'components/hooks/useTimezone';
+export * from 'components/hooks/useUser';
+export * from 'components/hooks/useWebsite';
+export * from 'components/hooks/useWebsiteReports';
+
+export * from 'components/pages/settings/teams/TeamAddForm';
+export * from 'components/pages/settings/teams/TeamAddWebsiteForm';
+export * from 'components/pages/settings/teams/TeamDeleteForm';
+export * from 'components/pages/settings/teams/TeamEditForm';
+export * from 'components/pages/settings/teams/TeamJoinForm';
+export * from 'components/pages/settings/teams/TeamLeaveForm';
+export * from 'components/pages/settings/teams/TeamMemberRemoveButton';
+export * from 'components/pages/settings/teams/TeamMembers';
+export * from 'components/pages/settings/teams/TeamMembersTable';
+export * from 'components/pages/settings/teams/TeamSettings';
+export * from 'components/pages/settings/teams/TeamsList';
+export * from 'components/pages/settings/teams/TeamsTable';
+export * from 'components/pages/settings/teams/TeamWebsiteRemoveButton';
+export * from 'components/pages/settings/teams/TeamWebsites';
+export * from 'components/pages/settings/teams/TeamWebsitesTable';
+export * from 'components/pages/settings/teams/WebsiteTags';
+
+export * from 'components/pages/settings/websites/ShareUrl';
+export * from 'components/pages/settings/websites/TrackingCode';
+export * from 'components/pages/settings/websites/WebsiteAddForm';
+export * from 'components/pages/settings/websites/WebsiteDeleteForm';
+export * from 'components/pages/settings/websites/WebsiteEditForm';
+export * from 'components/pages/settings/websites/WebsiteResetForm';
+export * from 'components/pages/settings/websites/WebsiteSettings';
+export * from 'components/pages/settings/websites/WebsitesList';
+export * from 'components/pages/settings/websites/WebsitesTable';
diff --git a/lang/am-ET.json b/src/lang/am-ET.json
similarity index 100%
rename from lang/am-ET.json
rename to src/lang/am-ET.json
diff --git a/lang/ar-SA.json b/src/lang/ar-SA.json
similarity index 100%
rename from lang/ar-SA.json
rename to src/lang/ar-SA.json
diff --git a/lang/be-BY.json b/src/lang/be-BY.json
similarity index 100%
rename from lang/be-BY.json
rename to src/lang/be-BY.json
diff --git a/lang/bn-BD.json b/src/lang/bn-BD.json
similarity index 100%
rename from lang/bn-BD.json
rename to src/lang/bn-BD.json
diff --git a/lang/ca-ES.json b/src/lang/ca-ES.json
similarity index 100%
rename from lang/ca-ES.json
rename to src/lang/ca-ES.json
diff --git a/lang/cs-CZ.json b/src/lang/cs-CZ.json
similarity index 100%
rename from lang/cs-CZ.json
rename to src/lang/cs-CZ.json
diff --git a/lang/da-DK.json b/src/lang/da-DK.json
similarity index 100%
rename from lang/da-DK.json
rename to src/lang/da-DK.json
diff --git a/lang/de-CH.json b/src/lang/de-CH.json
similarity index 100%
rename from lang/de-CH.json
rename to src/lang/de-CH.json
diff --git a/lang/de-DE.json b/src/lang/de-DE.json
similarity index 100%
rename from lang/de-DE.json
rename to src/lang/de-DE.json
diff --git a/lang/el-GR.json b/src/lang/el-GR.json
similarity index 100%
rename from lang/el-GR.json
rename to src/lang/el-GR.json
diff --git a/lang/en-GB.json b/src/lang/en-GB.json
similarity index 100%
rename from lang/en-GB.json
rename to src/lang/en-GB.json
diff --git a/lang/en-US.json b/src/lang/en-US.json
similarity index 100%
rename from lang/en-US.json
rename to src/lang/en-US.json
diff --git a/lang/es-ES.json b/src/lang/es-ES.json
similarity index 100%
rename from lang/es-ES.json
rename to src/lang/es-ES.json
diff --git a/lang/es-MX.json b/src/lang/es-MX.json
similarity index 100%
rename from lang/es-MX.json
rename to src/lang/es-MX.json
diff --git a/lang/fa-IR.json b/src/lang/fa-IR.json
similarity index 100%
rename from lang/fa-IR.json
rename to src/lang/fa-IR.json
diff --git a/lang/fi-FI.json b/src/lang/fi-FI.json
similarity index 100%
rename from lang/fi-FI.json
rename to src/lang/fi-FI.json
diff --git a/lang/fo-FO.json b/src/lang/fo-FO.json
similarity index 100%
rename from lang/fo-FO.json
rename to src/lang/fo-FO.json
diff --git a/lang/fr-FR.json b/src/lang/fr-FR.json
similarity index 100%
rename from lang/fr-FR.json
rename to src/lang/fr-FR.json
diff --git a/lang/ga-ES.json b/src/lang/ga-ES.json
similarity index 100%
rename from lang/ga-ES.json
rename to src/lang/ga-ES.json
diff --git a/lang/he-IL.json b/src/lang/he-IL.json
similarity index 100%
rename from lang/he-IL.json
rename to src/lang/he-IL.json
diff --git a/lang/hi-IN.json b/src/lang/hi-IN.json
similarity index 100%
rename from lang/hi-IN.json
rename to src/lang/hi-IN.json
diff --git a/lang/hr-HR.json b/src/lang/hr-HR.json
similarity index 100%
rename from lang/hr-HR.json
rename to src/lang/hr-HR.json
diff --git a/lang/hu-HU.json b/src/lang/hu-HU.json
similarity index 100%
rename from lang/hu-HU.json
rename to src/lang/hu-HU.json
diff --git a/lang/id-ID.json b/src/lang/id-ID.json
similarity index 100%
rename from lang/id-ID.json
rename to src/lang/id-ID.json
diff --git a/lang/it-IT.json b/src/lang/it-IT.json
similarity index 100%
rename from lang/it-IT.json
rename to src/lang/it-IT.json
diff --git a/src/lang/ja-JP.json b/src/lang/ja-JP.json
new file mode 100644
index 00000000..770f6f07
--- /dev/null
+++ b/src/lang/ja-JP.json
@@ -0,0 +1,211 @@
+{
+ "label.access-code": "アクセスコード",
+ "label.actions": "アクション",
+ "label.activity-log": "アクティビティログ",
+ "label.add": "追加",
+ "label.add-description": "説明を追加",
+ "label.add-website": "Webサイトの追加",
+ "label.admin": "管理者",
+ "label.after": "直後",
+ "label.all": "すべて",
+ "label.all-time": "すべての時間帯",
+ "label.analytics": "アナリティクス",
+ "label.average": "平均",
+ "label.average-visit-time": "平均滞在時間",
+ "label.back": "戻る",
+ "label.before": "直前",
+ "label.bounce-rate": "直帰率",
+ "label.breakdown": "故障",
+ "label.browser": "ブラウザ",
+ "label.browsers": "ブラウザ",
+ "label.cancel": "キャンセル",
+ "label.change-password": "パスワードの変更",
+ "label.cities": "都市",
+ "label.city": "都市",
+ "label.clear-all": "すべてクリア",
+ "label.confirm": "確認",
+ "label.confirm-password": "パスワード(確認)",
+ "label.contains": "コンテンツ",
+ "label.continue": "続ける",
+ "label.countries": "国名",
+ "label.country": "国",
+ "label.create-report": "レポートの作成",
+ "label.create-team": "チームの作成",
+ "label.create-user": "ユーザーの作成",
+ "label.created": "作成されました",
+ "label.current-password": "現在のパスワード",
+ "label.custom-range": "範囲指定",
+ "label.dashboard": "ダッシュボード",
+ "label.data": "データ",
+ "label.date": "日付",
+ "label.date-range": "期間",
+ "label.day": "日",
+ "label.default-date-range": "デフォルトの期間",
+ "label.delete": "削除",
+ "label.delete-team": "チームの削除",
+ "label.delete-user": "ユーザーの削除",
+ "label.delete-website": "Webサイトの削除",
+ "label.description": "説明",
+ "label.desktop": "デスクトップ",
+ "label.details": "詳細情報",
+ "label.device": "デバイス",
+ "label.devices": "デバイス",
+ "label.dismiss": "却下",
+ "label.does-not-contain": "を含まない",
+ "label.domain": "ドメイン",
+ "label.dropoff": "切り捨て",
+ "label.edit": "編集",
+ "label.edit-dashboard": "ダッシュボードの編集",
+ "label.enable-share-url": "共有URLを有効にする",
+ "label.event": "イベント",
+ "label.event-data": "イベントデータ",
+ "label.events": "イベント",
+ "label.false": "偽",
+ "label.field": "フィールド",
+ "label.fields": "フィールド",
+ "label.filter-combined": "統合",
+ "label.filter-raw": "RAW",
+ "label.filters": "フィルター",
+ "label.funnel": "分析",
+ "label.greater-than": "超過",
+ "label.greater-than-equals": "以上",
+ "label.insights": "見通し",
+ "label.is": "に等しい",
+ "label.is-not": "に等しくない",
+ "label.is-not-set": "未設定",
+ "label.is-set": "設定済み",
+ "label.join": "参加",
+ "label.join-team": "チームに参加",
+ "label.language": "言語",
+ "label.languages": "言語",
+ "label.laptop": "ノートPC",
+ "label.last-days": "過去{x}日間",
+ "label.last-hours": "過去{x}時間",
+ "label.leave": "離脱",
+ "label.leave-team": "チームを離脱",
+ "label.less-than": "未満",
+ "label.less-than-equals": "以下",
+ "label.login": "ログイン",
+ "label.logout": "ログアウト",
+ "label.max": "最大",
+ "label.members": "メンバー",
+ "label.min": "最小",
+ "label.mobile": "携帯電話",
+ "label.more": "もっと見る",
+ "label.my-websites": "マイWebサイト",
+ "label.name": "名前",
+ "label.new-password": "新しいパスワード",
+ "label.none": "なし",
+ "label.os": "OS",
+ "label.overview": "概要",
+ "label.owner": "所有者",
+ "label.page-of": "ページ {current}/{total}",
+ "label.page-views": "閲覧数",
+ "label.pageTitle": "ページタイトル",
+ "label.pages": "ページ",
+ "label.password": "パスワード",
+ "label.powered-by": "Powered by {name}",
+ "label.profile": "プロフィール",
+ "label.queries": "クエリ",
+ "label.query": "クエリ",
+ "label.query-parameters": "クエリパラメーター",
+ "label.realtime": "リアルタイム",
+ "label.referrer": "リファラー",
+ "label.referrers": "リファラー",
+ "label.refresh": "更新",
+ "label.regenerate": "再生成",
+ "label.region": "地域",
+ "label.regions": "地域",
+ "label.remove": "削除",
+ "label.reports": "レポート",
+ "label.required": "必須",
+ "label.reset": "リセット",
+ "label.reset-website": "Webサイトをリセットする",
+ "label.retention": "保持",
+ "label.role": "ロール",
+ "label.run-query": "クエリ実行",
+ "label.save": "保存",
+ "label.screens": "画面サイズ",
+ "label.select-date": "日付を選択",
+ "label.select-website": "Webサイトを選択",
+ "label.sessions": "セッション",
+ "label.settings": "設定",
+ "label.share-url": "共有URL",
+ "label.single-day": "一日",
+ "label.sum": "合計",
+ "label.tablet": "タブレット",
+ "label.team": "チーム",
+ "label.team-guest": "チームゲスト",
+ "label.team-id": "チームID",
+ "label.team-member": "チームメンバー",
+ "label.team-name": "チーム名",
+ "label.team-owner": "チーム所有者",
+ "label.team-websites": "チームのWebサイト",
+ "label.teams": "チーム",
+ "label.theme": "テーマ",
+ "label.this-month": "今月",
+ "label.this-week": "今週",
+ "label.this-year": "今年",
+ "label.timezone": "タイムゾーン",
+ "label.title": "タイトル",
+ "label.today": "今日",
+ "label.toggle-charts": "グラフを切り替える",
+ "label.total": "累計",
+ "label.total-records": "総記録数",
+ "label.tracking-code": "トラッキングコード",
+ "label.true": "真",
+ "label.type": "種別",
+ "label.unique": "ユニーク",
+ "label.unique-visitors": "ユニーク訪問者数",
+ "label.unknown": "不明",
+ "label.untitled": "無題",
+ "label.url": "URL",
+ "label.urls": "URL",
+ "label.user": "ユーザー",
+ "label.username": "ユーザー名",
+ "label.users": "ユーザー",
+ "label.value": "値",
+ "label.view": "表示",
+ "label.view-details": "詳細を表示",
+ "label.view-only": "表示のみ",
+ "label.views": "表示",
+ "label.visitors": "訪問者",
+ "label.website": "Webサイト",
+ "label.website-id": "WebサイトID",
+ "label.websites": "Webサイト",
+ "label.window": "ウィンドウ",
+ "label.yesterday": "昨日",
+ "message.active-users": "{x} {x, plural, one {アクティブな訪問者} other {アクティブな訪問者}}",
+ "message.confirm-delete": "{target}を削除してもよろしいですか?",
+ "message.confirm-leave": "{target}から離脱してもよろしいですか?",
+ "message.confirm-reset": "{target}をリセットしてもよろしいですか?",
+ "message.delete-account": "このアカウントを削除するには、下のフォームに「{confirmation}」と入力してください。",
+ "message.delete-website": "このWebサイトを削除するには、下のフォームに「{confirmation}」と入力してください。",
+ "message.delete-website-warning": "Webサイトのデータがすべて削除されます。",
+ "message.error": "未知のエラーが発生しました。",
+ "message.event-log": "{url}の{event}",
+ "message.go-to-settings": "設定に移動する",
+ "message.incorrect-username-password": "ユーザー名またはパスワードが間違っています。",
+ "message.invalid-domain": "無効なドメインです。http/httpsを含めないでください。",
+ "message.min-password-length": "最小文字数は{n}文字です",
+ "message.new-version-available": "Umamiの新しいバージョン{version}が利用可能です!",
+ "message.no-data-available": "データがありません。",
+ "message.no-event-data": "イベントデータがありません。",
+ "message.no-match-password": "パスワードが一致しません。",
+ "message.no-results-found": "結果が見つかりません。",
+ "message.no-team-websites": "このチームにはWebサイトがありません。",
+ "message.no-teams": "チームを作成していません。",
+ "message.no-users": "ユーザーが存在しません。",
+ "message.no-websites-configured": "Webサイトが設定されていません。",
+ "message.page-not-found": "ページが見つかりません",
+ "message.reset-website": "このWebサイトをリセットするには、下のフォームに「{confirmation}」と入力してください。",
+ "message.reset-website-warning": "このWebサイトの統計情報はすべて削除されますが、設定はそのまま残ります。",
+ "message.saved": "保存されました。",
+ "message.share-url": "あなたのWebサイトの統計情報は次のURLで公開されています:",
+ "message.team-already-member": "あなたはすでにチームのメンバーです。",
+ "message.team-not-found": "チームが見つかりません。",
+ "message.team-websites-info": "Webサイトはチーム内の誰でも見ることができます。",
+ "message.tracking-code": "このWebサイトの統計情報を追跡するには、HTMLの...セクションに以下のコードを記述します。",
+ "message.user-deleted": "ユーザーが削除されました。",
+ "message.visitor-log": "{os}({device})で{browser}を使用している{country}からの訪問者"
+}
diff --git a/lang/km-KH.json b/src/lang/km-KH.json
similarity index 100%
rename from lang/km-KH.json
rename to src/lang/km-KH.json
diff --git a/lang/ko-KR.json b/src/lang/ko-KR.json
similarity index 100%
rename from lang/ko-KR.json
rename to src/lang/ko-KR.json
diff --git a/lang/lt-LT.json b/src/lang/lt-LT.json
similarity index 100%
rename from lang/lt-LT.json
rename to src/lang/lt-LT.json
diff --git a/lang/mn-MN.json b/src/lang/mn-MN.json
similarity index 100%
rename from lang/mn-MN.json
rename to src/lang/mn-MN.json
diff --git a/lang/ms-MY.json b/src/lang/ms-MY.json
similarity index 100%
rename from lang/ms-MY.json
rename to src/lang/ms-MY.json
diff --git a/lang/my-MM.json b/src/lang/my-MM.json
similarity index 100%
rename from lang/my-MM.json
rename to src/lang/my-MM.json
diff --git a/lang/nb-NO.json b/src/lang/nb-NO.json
similarity index 100%
rename from lang/nb-NO.json
rename to src/lang/nb-NO.json
diff --git a/lang/nl-NL.json b/src/lang/nl-NL.json
similarity index 100%
rename from lang/nl-NL.json
rename to src/lang/nl-NL.json
diff --git a/lang/pl-PL.json b/src/lang/pl-PL.json
similarity index 100%
rename from lang/pl-PL.json
rename to src/lang/pl-PL.json
diff --git a/lang/pt-BR.json b/src/lang/pt-BR.json
similarity index 100%
rename from lang/pt-BR.json
rename to src/lang/pt-BR.json
diff --git a/lang/pt-PT.json b/src/lang/pt-PT.json
similarity index 100%
rename from lang/pt-PT.json
rename to src/lang/pt-PT.json
diff --git a/lang/ro-RO.json b/src/lang/ro-RO.json
similarity index 100%
rename from lang/ro-RO.json
rename to src/lang/ro-RO.json
diff --git a/lang/ru-RU.json b/src/lang/ru-RU.json
similarity index 100%
rename from lang/ru-RU.json
rename to src/lang/ru-RU.json
diff --git a/lang/si-LK.json b/src/lang/si-LK.json
similarity index 100%
rename from lang/si-LK.json
rename to src/lang/si-LK.json
diff --git a/lang/sk-SK.json b/src/lang/sk-SK.json
similarity index 100%
rename from lang/sk-SK.json
rename to src/lang/sk-SK.json
diff --git a/lang/sl-SI.json b/src/lang/sl-SI.json
similarity index 100%
rename from lang/sl-SI.json
rename to src/lang/sl-SI.json
diff --git a/lang/sv-SE.json b/src/lang/sv-SE.json
similarity index 100%
rename from lang/sv-SE.json
rename to src/lang/sv-SE.json
diff --git a/lang/ta-IN.json b/src/lang/ta-IN.json
similarity index 100%
rename from lang/ta-IN.json
rename to src/lang/ta-IN.json
diff --git a/lang/th-TH.json b/src/lang/th-TH.json
similarity index 100%
rename from lang/th-TH.json
rename to src/lang/th-TH.json
diff --git a/lang/tr-TR.json b/src/lang/tr-TR.json
similarity index 100%
rename from lang/tr-TR.json
rename to src/lang/tr-TR.json
diff --git a/lang/uk-UA.json b/src/lang/uk-UA.json
similarity index 100%
rename from lang/uk-UA.json
rename to src/lang/uk-UA.json
diff --git a/lang/ur-PK.json b/src/lang/ur-PK.json
similarity index 100%
rename from lang/ur-PK.json
rename to src/lang/ur-PK.json
diff --git a/lang/vi-VN.json b/src/lang/vi-VN.json
similarity index 100%
rename from lang/vi-VN.json
rename to src/lang/vi-VN.json
diff --git a/lang/zh-CN.json b/src/lang/zh-CN.json
similarity index 100%
rename from lang/zh-CN.json
rename to src/lang/zh-CN.json
diff --git a/lang/zh-TW.json b/src/lang/zh-TW.json
similarity index 100%
rename from lang/zh-TW.json
rename to src/lang/zh-TW.json
diff --git a/lib/auth.ts b/src/lib/auth.ts
similarity index 90%
rename from lib/auth.ts
rename to src/lib/auth.ts
index 10f7fbca..4a42d85d 100644
--- a/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -4,11 +4,12 @@ import debug from 'debug';
import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants';
import { secret } from 'lib/crypto';
import { createSecureToken, ensureArray, getRandomChars, parseToken } from 'next-basics';
-import { getTeamUser, getTeamWebsite, findTeamWebsiteByUserId } from 'queries';
+import { findTeamWebsiteByUserId, getTeamUser, getTeamWebsite } from 'queries';
import { loadWebsite } from './load';
import { Auth } from './types';
const log = debug('umami:auth');
+const cloudMode = process.env.CLOUD_MODE;
export async function setAuthKey(user, expire = 0) {
const authKey = `auth:${getRandomChars(32)}`;
@@ -57,7 +58,15 @@ export async function canViewWebsite({ user, shareToken }: Auth, websiteId: stri
return !!(await findTeamWebsiteByUserId(websiteId, user.id));
}
-export async function canCreateWebsite({ user }: Auth) {
+export async function canCreateWebsite({ user, grant }: Auth) {
+ if (cloudMode) {
+ if (grant?.find(a => a === PERMISSIONS.websiteCreate)) {
+ return true;
+ }
+
+ return false;
+ }
+
if (user.isAdmin) {
return true;
}
@@ -109,7 +118,15 @@ export async function canDeleteReport(auth: Auth, report: Report) {
return canUpdateReport(auth, report);
}
-export async function canCreateTeam({ user }: Auth) {
+export async function canCreateTeam({ user, grant }: Auth) {
+ if (cloudMode) {
+ if (grant?.find(a => a === PERMISSIONS.teamCreate)) {
+ return true;
+ }
+
+ return false;
+ }
+
if (user.isAdmin) {
return true;
}
diff --git a/lib/cache.ts b/src/lib/cache.ts
similarity index 72%
rename from lib/cache.ts
rename to src/lib/cache.ts
index bc46c23d..c54eda2e 100644
--- a/lib/cache.ts
+++ b/src/lib/cache.ts
@@ -2,17 +2,20 @@ import { User, Website } from '@prisma/client';
import redis from '@umami/redis-client';
import { getSession, getUserById, getWebsiteById } from '../queries';
-const { fetchObject, storeObject, deleteObject } = redis;
+const { fetchObject, storeObject, deleteObject, expire } = redis;
async function fetchWebsite(id): Promise {
- return fetchObject(`website:${id}`, () => getWebsiteById(id));
+ return fetchObject(`website:${id}`, () => getWebsiteById(id), 86400);
}
async function storeWebsite(data) {
const { id } = data;
const key = `website:${id}`;
- return storeObject(key, data);
+ const obj = await storeObject(key, data);
+ await expire(key, 86400);
+
+ return obj;
}
async function deleteWebsite(id) {
@@ -20,14 +23,17 @@ async function deleteWebsite(id) {
}
async function fetchUser(id): Promise {
- return fetchObject(`user:${id}`, () => getUserById(id, { includePassword: true }));
+ return fetchObject(`user:${id}`, () => getUserById(id, { includePassword: true }), 86400);
}
async function storeUser(data) {
const { id } = data;
const key = `user:${id}`;
- return storeObject(key, data);
+ const obj = await storeObject(key, data);
+ await expire(key, 86400);
+
+ return obj;
}
async function deleteUser(id) {
@@ -35,14 +41,17 @@ async function deleteUser(id) {
}
async function fetchSession(id) {
- return fetchObject(`session:${id}`, () => getSession(id));
+ return fetchObject(`session:${id}`, () => getSession(id), 86400);
}
async function storeSession(data) {
const { id } = data;
const key = `session:${id}`;
- return storeObject(key, data);
+ const obj = await storeObject(key, data);
+ await expire(key, 86400);
+
+ return obj;
}
async function deleteSession(id) {
diff --git a/lib/charts.js b/src/lib/charts.js
similarity index 100%
rename from lib/charts.js
rename to src/lib/charts.js
diff --git a/lib/clickhouse.ts b/src/lib/clickhouse.ts
similarity index 100%
rename from lib/clickhouse.ts
rename to src/lib/clickhouse.ts
diff --git a/lib/client.ts b/src/lib/client.ts
similarity index 100%
rename from lib/client.ts
rename to src/lib/client.ts
diff --git a/lib/constants.ts b/src/lib/constants.ts
similarity index 100%
rename from lib/constants.ts
rename to src/lib/constants.ts
diff --git a/lib/crypto.js b/src/lib/crypto.js
similarity index 100%
rename from lib/crypto.js
rename to src/lib/crypto.js
diff --git a/lib/data.ts b/src/lib/data.ts
similarity index 100%
rename from lib/data.ts
rename to src/lib/data.ts
diff --git a/lib/date.js b/src/lib/date.ts
similarity index 79%
rename from lib/date.js
rename to src/lib/date.ts
index 49bff897..14f0e13c 100644
--- a/lib/date.js
+++ b/src/lib/date.ts
@@ -29,9 +29,19 @@ import {
max,
min,
isDate,
+ subWeeks,
} from 'date-fns';
import { getDateLocale } from 'lib/lang';
+export const TIME_UNIT = {
+ minute: 'minute',
+ hour: 'hour',
+ day: 'day',
+ week: 'week',
+ month: 'month',
+ year: 'year',
+};
+
const dateFuncs = {
minute: [differenceInMinutes, addMinutes, startOfMinute],
hour: [differenceInHours, addHours, startOfHour],
@@ -81,6 +91,7 @@ export function parseDateRange(value, locale = 'en-US') {
if (!match) return null;
const { num, unit } = match.groups;
+ const selectedUnit = { num, unit };
if (+num === 1) {
switch (unit) {
@@ -90,6 +101,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: endOfDay(now),
unit: 'hour',
value,
+ selectedUnit,
};
case 'week':
return {
@@ -97,6 +109,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: endOfWeek(now, { locale: dateLocale }),
unit: 'day',
value,
+ selectedUnit,
};
case 'month':
return {
@@ -104,6 +117,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: endOfMonth(now),
unit: 'day',
value,
+ selectedUnit,
};
case 'year':
return {
@@ -111,6 +125,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: endOfYear(now),
unit: 'month',
value,
+ selectedUnit,
};
}
}
@@ -123,6 +138,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: subDays(endOfDay(now), 1),
unit: 'hour',
value,
+ selectedUnit,
};
case 'week':
return {
@@ -130,6 +146,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: subDays(endOfWeek(now, { locale: dateLocale }), 1),
unit: 'day',
value,
+ selectedUnit,
};
case 'month':
return {
@@ -137,6 +154,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: subMonths(endOfMonth(now), 1),
unit: 'day',
value,
+ selectedUnit,
};
case 'year':
return {
@@ -144,6 +162,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: subYears(endOfYear(now), 1),
unit: 'month',
value,
+ selectedUnit,
};
}
}
@@ -155,6 +174,7 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: endOfDay(now),
unit,
value,
+ selectedUnit,
};
case 'hour':
return {
@@ -162,6 +182,53 @@ export function parseDateRange(value, locale = 'en-US') {
endDate: endOfHour(now),
unit,
value,
+ selectedUnit,
+ };
+ }
+}
+
+export function incrementDateRange(value, increment) {
+ const { startDate, endDate, selectedUnit } = value;
+
+ const { num, unit } = selectedUnit;
+
+ const sub = num * increment;
+
+ switch (unit) {
+ case 'hour':
+ return {
+ ...value,
+ startDate: subHours(startDate, sub),
+ endDate: subHours(endDate, sub),
+ value: 'range',
+ };
+ case 'day':
+ return {
+ ...value,
+ startDate: subDays(startDate, sub),
+ endDate: subDays(endDate, sub),
+ value: 'range',
+ };
+ case 'week':
+ return {
+ ...value,
+ startDate: subWeeks(startDate, sub),
+ endDate: subWeeks(endDate, sub),
+ value: 'range',
+ };
+ case 'month':
+ return {
+ ...value,
+ startDate: subMonths(startDate, sub),
+ endDate: subMonths(endDate, sub),
+ value: 'range',
+ };
+ case 'year':
+ return {
+ ...value,
+ startDate: subYears(startDate, sub),
+ endDate: subYears(endDate, sub),
+ value: 'range',
};
}
}
@@ -237,7 +304,7 @@ export function getDateLength(startDate, endDate, unit) {
return diff(endDate, startDate) + 1;
}
-export const customFormats = {
+export const CUSTOM_FORMATS = {
'en-US': {
p: 'ha',
pp: 'h:mm:ss',
@@ -252,7 +319,7 @@ export const customFormats = {
export function formatDate(date, str, locale = 'en-US') {
return format(
typeof date === 'string' ? new Date(date) : date,
- customFormats?.[locale]?.[str] || str,
+ CUSTOM_FORMATS?.[locale]?.[str] || str,
{
locale: getDateLocale(locale),
},
diff --git a/lib/db.js b/src/lib/db.js
similarity index 100%
rename from lib/db.js
rename to src/lib/db.js
diff --git a/lib/detect.ts b/src/lib/detect.ts
similarity index 80%
rename from lib/detect.ts
rename to src/lib/detect.ts
index 43dac649..86f812bd 100644
--- a/lib/detect.ts
+++ b/src/lib/detect.ts
@@ -65,19 +65,27 @@ export async function getLocation(ip, req) {
// Cloudflare headers
if (req.headers['cf-ipcountry']) {
+ const country = safeDecodeURIComponent(req.headers['cf-ipcountry']);
+ const subdivision1 = safeDecodeURIComponent(req.headers['cf-region-code']);
+ const city = safeDecodeURIComponent(req.headers['cf-ipcity']);
+
return {
- country: safeDecodeURIComponent(req.headers['cf-ipcountry']),
- subdivision1: safeDecodeURIComponent(req.headers['cf-region-code']),
- city: safeDecodeURIComponent(req.headers['cf-ipcity']),
+ country,
+ subdivision1: subdivision1.includes('-') ? subdivision1 : `${country}-${subdivision1}`,
+ city,
};
}
// Vercel headers
if (req.headers['x-vercel-ip-country']) {
+ const country = safeDecodeURIComponent(req.headers['x-vercel-ip-country']);
+ const subdivision1 = safeDecodeURIComponent(req.headers['x-vercel-ip-country-region']);
+ const city = safeDecodeURIComponent(req.headers['x-vercel-ip-city']);
+
return {
- country: safeDecodeURIComponent(req.headers['x-vercel-ip-country']),
- subdivision1: safeDecodeURIComponent(req.headers['x-vercel-ip-country-region']),
- city: safeDecodeURIComponent(req.headers['x-vercel-ip-city']),
+ country,
+ subdivision1: subdivision1.includes('-') ? subdivision1 : `${country}-${subdivision1}`,
+ city,
};
}
diff --git a/lib/filters.js b/src/lib/filters.js
similarity index 100%
rename from lib/filters.js
rename to src/lib/filters.js
diff --git a/lib/format.js b/src/lib/format.js
similarity index 100%
rename from lib/format.js
rename to src/lib/format.js
diff --git a/lib/kafka.ts b/src/lib/kafka.ts
similarity index 100%
rename from lib/kafka.ts
rename to src/lib/kafka.ts
diff --git a/lib/lang.js b/src/lib/lang.js
similarity index 100%
rename from lib/lang.js
rename to src/lib/lang.js
diff --git a/lib/load.ts b/src/lib/load.ts
similarity index 100%
rename from lib/load.ts
rename to src/lib/load.ts
diff --git a/lib/middleware.ts b/src/lib/middleware.ts
similarity index 74%
rename from lib/middleware.ts
rename to src/lib/middleware.ts
index 414cab23..edf3e929 100644
--- a/lib/middleware.ts
+++ b/src/lib/middleware.ts
@@ -1,19 +1,20 @@
+import redis from '@umami/redis-client';
+import cors from 'cors';
+import debug from 'debug';
+import { getAuthToken, parseShareToken } from 'lib/auth';
+import { ROLES } from 'lib/constants';
+import { isUuid, secret } from 'lib/crypto';
+import { findSession } from 'lib/session';
import {
- createMiddleware,
- unauthorized,
badRequest,
+ createMiddleware,
parseSecureToken,
tooManyRequest,
+ unauthorized,
} from 'next-basics';
-import debug from 'debug';
-import cors from 'cors';
-import redis from '@umami/redis-client';
-import { findSession } from 'lib/session';
-import { getAuthToken, parseShareToken } from 'lib/auth';
-import { secret, isUuid } from 'lib/crypto';
-import { ROLES } from 'lib/constants';
-import { getUserById } from '../queries';
import { NextApiRequestCollect } from 'pages/api/send';
+import { getUserById } from '../queries';
+import { NextApiRequestQueryBody } from './types';
const log = debug('umami:middleware');
@@ -50,7 +51,7 @@ export const useAuth = createMiddleware(async (req, res, next) => {
const shareToken = await parseShareToken(req);
let user = null;
- const { userId, authKey } = payload || {};
+ const { userId, authKey, grant } = payload || {};
if (isUuid(userId)) {
user = await getUserById(userId);
@@ -59,7 +60,7 @@ export const useAuth = createMiddleware(async (req, res, next) => {
}
if (process.env.NODE_ENV === 'development') {
- log({ token, shareToken, payload, user });
+ log({ token, shareToken, payload, user, grant });
}
if (!user?.id && !shareToken) {
@@ -71,7 +72,25 @@ export const useAuth = createMiddleware(async (req, res, next) => {
user.isAdmin = user.role === ROLES.admin;
}
- (req as any).auth = { user, token, shareToken, authKey };
+ (req as any).auth = {
+ user,
+ grant,
+ token,
+ shareToken,
+ authKey,
+ };
+
+ next();
+});
+
+export const useValidate = createMiddleware(async (req: any, res, next) => {
+ try {
+ const { yup } = req as NextApiRequestQueryBody;
+
+ yup[req.method] && yup[req.method].validateSync({ ...req.query, ...req.body });
+ } catch (e: any) {
+ return badRequest(res, e.message);
+ }
next();
});
diff --git a/lib/prisma.ts b/src/lib/prisma.ts
similarity index 98%
rename from lib/prisma.ts
rename to src/lib/prisma.ts
index 12bafa51..a9832c28 100644
--- a/lib/prisma.ts
+++ b/src/lib/prisma.ts
@@ -185,7 +185,7 @@ function getPageFilters(filters: SearchFilter): [
orderBy: string;
},
] {
- const { pageSize = 10, page = 1, orderBy } = filters;
+ const { pageSize = 10, page = 1, orderBy } = filters || {};
return [
{
diff --git a/lib/query.ts b/src/lib/query.ts
similarity index 100%
rename from lib/query.ts
rename to src/lib/query.ts
diff --git a/lib/session.ts b/src/lib/session.ts
similarity index 75%
rename from lib/session.ts
rename to src/lib/session.ts
index 5eb7398a..85c173c5 100644
--- a/lib/session.ts
+++ b/src/lib/session.ts
@@ -1,12 +1,27 @@
-import { secret, uuid, isUuid } from 'lib/crypto';
+import { isUuid, secret, uuid } from 'lib/crypto';
import { getClientInfo, getJsonBody } from 'lib/detect';
import { parseToken } from 'next-basics';
import { CollectRequestBody, NextApiRequestCollect } from 'pages/api/send';
import { createSession } from 'queries';
import cache from './cache';
+import clickhouse from './clickhouse';
import { loadSession, loadWebsite } from './load';
-export async function findSession(req: NextApiRequestCollect) {
+export async function findSession(req: NextApiRequestCollect): Promise<{
+ id: any;
+ websiteId: string;
+ hostname: string;
+ browser: string;
+ os: any;
+ device: string;
+ screen: string;
+ language: string;
+ country: any;
+ subdivision1: any;
+ subdivision2: any;
+ city: any;
+ ownerId: string;
+}> {
const { payload } = getJsonBody(req);
if (!payload) {
@@ -53,6 +68,25 @@ export async function findSession(req: NextApiRequestCollect) {
const sessionId = uuid(websiteId, hostname, ip, userAgent);
+ // Clickhouse does not require session lookup
+ if (clickhouse.enabled) {
+ return {
+ id: sessionId,
+ websiteId,
+ hostname,
+ browser,
+ os: os as any,
+ device,
+ screen,
+ language,
+ country,
+ subdivision1,
+ subdivision2,
+ city,
+ ownerId: website.userId,
+ };
+ }
+
// Find session
let session = await loadSession(sessionId);
diff --git a/lib/sql.ts b/src/lib/sql.ts
similarity index 100%
rename from lib/sql.ts
rename to src/lib/sql.ts
diff --git a/lib/types.ts b/src/lib/types.ts
similarity index 87%
rename from lib/types.ts
rename to src/lib/types.ts
index 3f3ac533..3685753e 100644
--- a/lib/types.ts
+++ b/src/lib/types.ts
@@ -4,20 +4,29 @@ import {
DATA_TYPE,
EVENT_TYPE,
KAFKA_TOPIC,
+ PERMISSIONS,
REPORT_FILTER_TYPES,
+ REPORT_TYPES,
ROLES,
TEAM_FILTER_TYPES,
USER_FILTER_TYPES,
WEBSITE_FILTER_TYPES,
} from './constants';
+import * as yup from 'yup';
+import { TIME_UNIT } from './date';
type ObjectValues = T[keyof T];
+export type TimeUnit = ObjectValues;
+export type Permission = ObjectValues;
+
export type CollectionType = ObjectValues;
export type Role = ObjectValues;
export type EventType = ObjectValues;
export type DynamicDataType = ObjectValues;
export type KafkaTopic = ObjectValues;
+export type ReportType = ObjectValues;
+
export type ReportSearchFilterType = ObjectValues;
export type UserSearchFilterType = ObjectValues;
export type WebsiteSearchFilterType = ObjectValues;
@@ -47,8 +56,8 @@ export interface ReportSearchFilter extends SearchFilter
export interface SearchFilter {
filter?: string;
filterType?: T;
- pageSize?: number;
- page?: number;
+ pageSize: number;
+ page: number;
orderBy?: string;
}
@@ -71,16 +80,25 @@ export interface Auth {
role: string;
isAdmin: boolean;
};
+ grant?: Permission[];
shareToken?: {
websiteId: string;
};
}
+export interface YupRequest {
+ GET?: yup.ObjectSchema;
+ POST?: yup.ObjectSchema;
+ PUT?: yup.ObjectSchema;
+ DELETE?: yup.ObjectSchema;
+}
+
export interface NextApiRequestQueryBody extends NextApiRequest {
auth?: Auth;
query: TQuery & { [key: string]: string | string[] };
body: TBody;
headers: any;
+ yup: YupRequest;
}
export interface NextApiRequestAuth extends NextApiRequest {
@@ -168,8 +186,9 @@ export interface RealtimeUpdate {
export interface DateRange {
startDate: Date;
endDate: Date;
- unit: string;
value: string;
+ unit?: TimeUnit;
+ selectedUnit?: TimeUnit;
}
export interface QueryFilters {
diff --git a/src/lib/yup.ts b/src/lib/yup.ts
new file mode 100644
index 00000000..a9d21028
--- /dev/null
+++ b/src/lib/yup.ts
@@ -0,0 +1,19 @@
+import * as yup from 'yup';
+
+export function getDateRangeValidation() {
+ return {
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+ };
+}
+
+// ex: /funnel|insights|retention/i
+export function getFilterValidation(matchRegex) {
+ return {
+ filter: yup.string(),
+ filterType: yup.string().matches(matchRegex),
+ pageSize: yup.number().integer().positive().max(200),
+ page: yup.number().integer().positive(),
+ orderBy: yup.string(),
+ };
+}
diff --git a/pages/404.js b/src/pages/404.js
similarity index 90%
rename from pages/404.js
rename to src/pages/404.js
index 4a5f4bd9..8fa13a9c 100644
--- a/pages/404.js
+++ b/src/pages/404.js
@@ -1,6 +1,6 @@
import { Row, Column, Flexbox } from 'react-basics';
import AppLayout from 'components/layout/AppLayout';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function Custom404() {
const { formatMessage, labels } = useMessages();
diff --git a/pages/_app.js b/src/pages/_app.js
similarity index 86%
rename from pages/_app.js
rename to src/pages/_app.js
index 8d549773..7022772c 100644
--- a/pages/_app.js
+++ b/src/pages/_app.js
@@ -5,8 +5,7 @@ import Head from 'next/head';
import Script from 'next/script';
import { useRouter } from 'next/router';
import ErrorBoundary from 'components/common/ErrorBoundary';
-import useLocale from 'hooks/useLocale';
-import useConfig from 'hooks/useConfig';
+import useLocale from 'components/hooks/useLocale';
import '@fontsource/inter/400.css';
import '@fontsource/inter/700.css';
import 'react-basics/dist/styles.css';
@@ -27,22 +26,10 @@ const client = new QueryClient({
export default function App({ Component, pageProps }) {
const { locale, messages } = useLocale();
const { basePath, pathname } = useRouter();
- const config = useConfig();
-
- const Wrapper = ({ children }) => {children};
-
- if (config?.uiDisabled) {
- return null;
- }
return (
- null}
- >
+ null}>
diff --git a/pages/api/auth/login.ts b/src/pages/api/auth/login.ts
similarity index 85%
rename from pages/api/auth/login.ts
rename to src/pages/api/auth/login.ts
index b9a2be00..47521084 100644
--- a/pages/api/auth/login.ts
+++ b/src/pages/api/auth/login.ts
@@ -1,19 +1,20 @@
+import redis from '@umami/redis-client';
import debug from 'debug';
+import { setAuthKey } from 'lib/auth';
+import { secret } from 'lib/crypto';
+import { useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody, User } from 'lib/types';
import { NextApiResponse } from 'next';
import {
- ok,
- unauthorized,
- badRequest,
checkPassword,
createSecureToken,
- methodNotAllowed,
forbidden,
+ methodNotAllowed,
+ ok,
+ unauthorized,
} from 'next-basics';
-import redis from '@umami/redis-client';
import { getUserByUsername } from 'queries';
-import { secret } from 'lib/crypto';
-import { NextApiRequestQueryBody, User } from 'lib/types';
-import { setAuthKey } from 'lib/auth';
+import * as yup from 'yup';
const log = debug('umami:auth');
@@ -27,6 +28,13 @@ export interface LoginResponse {
user: User;
}
+const schema = {
+ POST: yup.object().shape({
+ username: yup.string().required(),
+ password: yup.string().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -35,13 +43,12 @@ export default async (
return forbidden(res);
}
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'POST') {
const { username, password } = req.body;
- if (!username || !password) {
- return badRequest(res);
- }
-
const user = await getUserByUsername(username, { includePassword: true });
if (user && checkPassword(password, user.password)) {
diff --git a/pages/api/auth/logout.ts b/src/pages/api/auth/logout.ts
similarity index 100%
rename from pages/api/auth/logout.ts
rename to src/pages/api/auth/logout.ts
diff --git a/pages/api/auth/sso.ts b/src/pages/api/auth/sso.ts
similarity index 100%
rename from pages/api/auth/sso.ts
rename to src/pages/api/auth/sso.ts
diff --git a/pages/api/auth/verify.ts b/src/pages/api/auth/verify.ts
similarity index 100%
rename from pages/api/auth/verify.ts
rename to src/pages/api/auth/verify.ts
diff --git a/pages/api/config.ts b/src/pages/api/config.ts
similarity index 81%
rename from pages/api/config.ts
rename to src/pages/api/config.ts
index bccfd048..adba894a 100644
--- a/pages/api/config.ts
+++ b/src/pages/api/config.ts
@@ -2,21 +2,19 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { ok, methodNotAllowed } from 'next-basics';
export interface ConfigResponse {
- basePath: string;
- trackerScriptName: string;
- updatesDisabled: boolean;
telemetryDisabled: boolean;
- cloudMode: boolean;
+ trackerScriptName: string;
+ uiDisabled: boolean;
+ updatesDisabled: boolean;
}
export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'GET') {
return ok(res, {
- basePath: process.env.BASE_PATH || '',
- trackerScriptName: process.env.TRACKER_SCRIPT_NAME,
- updatesDisabled: !!process.env.DISABLE_UPDATES,
telemetryDisabled: !!process.env.DISABLE_TELEMETRY,
- cloudMode: !!process.env.CLOUD_MODE,
+ trackerScriptName: process.env.TRACKER_SCRIPT_NAME,
+ uiDisabled: !!process.env.DISABLE_UI,
+ updatesDisabled: !!process.env.DISABLE_UPDATES,
});
}
diff --git a/pages/api/event-data/events.ts b/src/pages/api/event-data/events.ts
similarity index 56%
rename from pages/api/event-data/events.ts
rename to src/pages/api/event-data/events.ts
index 9f8f964b..1d1d3787 100644
--- a/pages/api/event-data/events.ts
+++ b/src/pages/api/event-data/events.ts
@@ -1,26 +1,37 @@
import { canViewWebsite } from 'lib/auth';
-import { useCors, useAuth } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
-import { ok, methodNotAllowed, unauthorized } from 'next-basics';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getEventDataEvents } from 'queries';
+import * as yup from 'yup';
-export interface EventDataEventsRequestQuery {
+export interface EventDataFieldsRequestQuery {
websiteId: string;
- dateRange: {
- startDate: string;
- endDate: string;
- };
+ startAt: string;
+ endAt: string;
event?: string;
}
+const schema = {
+ GET: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+ event: yup.string(),
+ }),
+};
+
export default async (
- req: NextApiRequestQueryBody,
+ req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
const { websiteId, startAt, endAt, event } = req.query;
diff --git a/pages/api/event-data/fields.ts b/src/pages/api/event-data/fields.ts
similarity index 63%
rename from pages/api/event-data/fields.ts
rename to src/pages/api/event-data/fields.ts
index b6a73133..1cd24fe6 100644
--- a/pages/api/event-data/fields.ts
+++ b/src/pages/api/event-data/fields.ts
@@ -1,19 +1,27 @@
import { canViewWebsite } from 'lib/auth';
-import { useCors, useAuth } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
-import { ok, methodNotAllowed, unauthorized } from 'next-basics';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getEventDataFields } from 'queries';
+import * as yup from 'yup';
export interface EventDataFieldsRequestQuery {
websiteId: string;
- dateRange: {
- startDate: string;
- endDate: string;
- };
+ startAt: string;
+ endAt: string;
field?: string;
}
+const schema = {
+ GET: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+ field: yup.string(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -21,6 +29,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
const { websiteId, startAt, endAt, field } = req.query;
diff --git a/pages/api/event-data/stats.ts b/src/pages/api/event-data/stats.ts
similarity index 60%
rename from pages/api/event-data/stats.ts
rename to src/pages/api/event-data/stats.ts
index 4ba843be..7f694bc6 100644
--- a/pages/api/event-data/stats.ts
+++ b/src/pages/api/event-data/stats.ts
@@ -1,18 +1,25 @@
import { canViewWebsite } from 'lib/auth';
-import { useCors, useAuth } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
-import { ok, methodNotAllowed, unauthorized } from 'next-basics';
-import { getEventDataStats } from 'queries';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
+import { getEventDataStats } from 'queries/index';
+import * as yup from 'yup';
export interface EventDataStatsRequestQuery {
websiteId: string;
- dateRange: {
- startDate: string;
- endDate: string;
- };
+ startAt: string;
+ endAt: string;
}
+const schema = {
+ GET: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -20,6 +27,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
const { websiteId, startAt, endAt } = req.query;
diff --git a/pages/api/heartbeat.ts b/src/pages/api/heartbeat.ts
similarity index 100%
rename from pages/api/heartbeat.ts
rename to src/pages/api/heartbeat.ts
diff --git a/pages/api/me/index.ts b/src/pages/api/me/index.ts
similarity index 100%
rename from pages/api/me/index.ts
rename to src/pages/api/me/index.ts
diff --git a/pages/api/me/password.ts b/src/pages/api/me/password.ts
similarity index 77%
rename from pages/api/me/password.ts
rename to src/pages/api/me/password.ts
index f9f60fc5..6f49a182 100644
--- a/pages/api/me/password.ts
+++ b/src/pages/api/me/password.ts
@@ -1,15 +1,16 @@
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, User } from 'lib/types';
-import { useAuth } from 'lib/middleware';
import { NextApiResponse } from 'next';
import {
badRequest,
checkPassword,
+ forbidden,
hashPassword,
methodNotAllowed,
- forbidden,
ok,
} from 'next-basics';
import { getUserById, updateUser } from 'queries';
+import * as yup from 'yup';
export interface UserPasswordRequestQuery {
id: string;
@@ -20,6 +21,14 @@ export interface UserPasswordRequestBody {
newPassword: string;
}
+const schema = {
+ POST: yup.object().shape({
+ id: yup.string().uuid().required(),
+ currentPassword: yup.string().required(),
+ newPassword: yup.string().min(8).required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -30,6 +39,9 @@ export default async (
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { currentPassword, newPassword } = req.body;
const { id } = req.auth.user;
diff --git a/pages/api/me/teams.ts b/src/pages/api/me/teams.ts
similarity index 60%
rename from pages/api/me/teams.ts
rename to src/pages/api/me/teams.ts
index d323043b..d394ef07 100644
--- a/pages/api/me/teams.ts
+++ b/src/pages/api/me/teams.ts
@@ -1,10 +1,20 @@
-import { useCors } from 'lib/middleware';
+import { useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { methodNotAllowed } from 'next-basics';
import userTeams from 'pages/api/users/[id]/teams';
+import * as yup from 'yup';
-export interface MyTeamsRequestQuery extends SearchFilter {}
+export interface MyTeamsRequestQuery extends SearchFilter {
+ id: string;
+}
+
+const schema = {
+ GET: yup.object().shape({
+ ...getFilterValidation(/All|Name|Owner/i),
+ }),
+};
export default async (
req: NextApiRequestQueryBody,
@@ -12,7 +22,12 @@ export default async (
) => {
await useCors(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
+ req.query.id = req.auth.user.id;
+
return userTeams(req, res);
}
diff --git a/pages/api/me/websites.ts b/src/pages/api/me/websites.ts
similarity index 64%
rename from pages/api/me/websites.ts
rename to src/pages/api/me/websites.ts
index 238d1b6e..d4a803a0 100644
--- a/pages/api/me/websites.ts
+++ b/src/pages/api/me/websites.ts
@@ -1,11 +1,20 @@
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { methodNotAllowed } from 'next-basics';
-
import userWebsites from 'pages/api/users/[id]/websites';
+import * as yup from 'yup';
-export interface MyWebsitesRequestQuery extends SearchFilter {}
+export interface MyWebsitesRequestQuery extends SearchFilter {
+ id: string;
+}
+
+const schema = {
+ GET: yup.object().shape({
+ ...getFilterValidation(/All|Name|Domain/i),
+ }),
+};
export default async (
req: NextApiRequestQueryBody,
@@ -14,6 +23,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
req.query.id = req.auth.user.id;
diff --git a/pages/api/realtime/[id].ts b/src/pages/api/realtime/[id].ts
similarity index 77%
rename from pages/api/realtime/[id].ts
rename to src/pages/api/realtime/[id].ts
index e78599c6..5b1e1e05 100644
--- a/pages/api/realtime/[id].ts
+++ b/src/pages/api/realtime/[id].ts
@@ -1,22 +1,32 @@
import { subMinutes } from 'date-fns';
import { canViewWebsite } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, RealtimeInit } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getRealtimeData } from 'queries';
-
+import * as yup from 'yup';
export interface RealtimeRequestQuery {
id: string;
startAt: number;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ startAt: yup.number().integer().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
const { id: websiteId, startAt } = req.query;
diff --git a/pages/api/reports/[id].ts b/src/pages/api/reports/[id].ts
similarity index 61%
rename from pages/api/reports/[id].ts
rename to src/pages/api/reports/[id].ts
index 85bc302c..eb4199bc 100644
--- a/pages/api/reports/[id].ts
+++ b/src/pages/api/reports/[id].ts
@@ -1,9 +1,10 @@
-import { canUpdateReport, canViewReport, canDeleteReport } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
-import { NextApiRequestQueryBody } from 'lib/types';
+import { canDeleteReport, canUpdateReport, canViewReport } from 'lib/auth';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody, ReportType, YupRequest } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
-import { getReportById, updateReport, deleteReport } from 'queries';
+import { deleteReport, getReportById, updateReport } from 'queries';
+import * as yup from 'yup';
export interface ReportRequestQuery {
id: string;
@@ -11,12 +12,34 @@ export interface ReportRequestQuery {
export interface ReportRequestBody {
websiteId: string;
- type: string;
+ type: ReportType;
name: string;
description: string;
parameters: string;
}
+const schema: YupRequest = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+ POST: yup.object().shape({
+ id: yup.string().uuid().required(),
+ websiteId: yup.string().uuid().required(),
+ type: yup
+ .string()
+ .matches(/funnel|insights|retention/i)
+ .required(),
+ name: yup.string().max(200).required(),
+ description: yup.string().max(500),
+ parameters: yup
+ .object()
+ .test('len', 'Must not exceed 6000 characters.', val => JSON.stringify(val).length < 6000),
+ }),
+ DELETE: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -24,6 +47,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: reportId } = req.query;
const {
user: { id: userId },
diff --git a/pages/api/reports/funnel.ts b/src/pages/api/reports/funnel.ts
similarity index 65%
rename from pages/api/reports/funnel.ts
rename to src/pages/api/reports/funnel.ts
index 33882e03..a51817bf 100644
--- a/pages/api/reports/funnel.ts
+++ b/src/pages/api/reports/funnel.ts
@@ -1,9 +1,10 @@
import { canViewWebsite } from 'lib/auth';
-import { useCors, useAuth } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
-import { ok, methodNotAllowed, unauthorized } from 'next-basics';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getFunnel } from 'queries';
+import * as yup from 'yup';
export interface FunnelRequestBody {
websiteId: string;
@@ -22,6 +23,21 @@ export interface FunnelResponse {
endAt: number;
}
+const schema = {
+ POST: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ urls: yup.array().min(2).of(yup.string()).required(),
+ window: yup.number().positive().required(),
+ dateRange: yup
+ .object()
+ .shape({
+ startDate: yup.date().required(),
+ endDate: yup.date().required(),
+ })
+ .required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -29,6 +45,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'POST') {
const {
websiteId,
diff --git a/pages/api/reports/index.ts b/src/pages/api/reports/index.ts
similarity index 58%
rename from pages/api/reports/index.ts
rename to src/pages/api/reports/index.ts
index 762f297c..e62a1cc5 100644
--- a/pages/api/reports/index.ts
+++ b/src/pages/api/reports/index.ts
@@ -1,10 +1,11 @@
-import { canViewWebsite } from 'lib/auth';
import { uuid } from 'lib/crypto';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
-import { methodNotAllowed, ok, unauthorized } from 'next-basics';
-import { createReport, getReportsByUserId, getReportsByWebsiteId } from 'queries';
+import { methodNotAllowed, ok } from 'next-basics';
+import { createReport, getReportsByUserId } from 'queries';
+import * as yup from 'yup';
export interface ReportsRequestQuery extends SearchFilter {}
@@ -14,11 +15,28 @@ export interface ReportRequestBody {
type: string;
description: string;
parameters: {
- window: string;
- urls: string[];
+ [key: string]: any;
};
}
+const schema = {
+ GET: yup.object().shape({
+ ...getFilterValidation(/All|Name|Description|Type|Username|Website Name|Website Domain/i),
+ }),
+ POST: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ name: yup.string().max(200).required(),
+ type: yup
+ .string()
+ .matches(/funnel|insights|retention/i)
+ .required(),
+ description: yup.string().max(500),
+ parameters: yup
+ .object()
+ .test('len', 'Must not exceed 6000 characters.', val => JSON.stringify(val).length < 6000),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -26,6 +44,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const {
user: { id: userId },
} = req.auth;
diff --git a/src/pages/api/reports/insights.ts b/src/pages/api/reports/insights.ts
new file mode 100644
index 00000000..4d17c922
--- /dev/null
+++ b/src/pages/api/reports/insights.ts
@@ -0,0 +1,98 @@
+import { canViewWebsite } from 'lib/auth';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody } from 'lib/types';
+import { NextApiResponse } from 'next';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
+import { getInsights } from 'queries';
+import * as yup from 'yup';
+
+export interface InsightsRequestBody {
+ websiteId: string;
+ dateRange: {
+ startDate: string;
+ endDate: string;
+ };
+ fields: { name: string; type: string; label: string }[];
+ filters: { name: string; type: string; filter: string; value: string }[];
+ groups: { name: string; type: string }[];
+}
+
+const schema = {
+ POST: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ dateRange: yup
+ .object()
+ .shape({
+ startDate: yup.date().required(),
+ endDate: yup.date().required(),
+ })
+ .required(),
+ fields: yup
+ .array()
+ .of(
+ yup.object().shape({
+ name: yup.string().required(),
+ type: yup.string().required(),
+ label: yup.string().required(),
+ }),
+ )
+ .min(1)
+ .required(),
+ filters: yup.array().of(
+ yup.object().shape({
+ name: yup.string().required(),
+ type: yup.string().required(),
+ filter: yup.string().required(),
+ value: yup.string().required(),
+ }),
+ ),
+ groups: yup.array().of(
+ yup.object().shape({
+ name: yup.string().required(),
+ type: yup.string().required(),
+ }),
+ ),
+ }),
+};
+
+function convertFilters(filters) {
+ return filters.reduce((obj, { name, ...value }) => {
+ obj[name] = value;
+
+ return obj;
+ }, {});
+}
+
+export default async (
+ req: NextApiRequestQueryBody,
+ res: NextApiResponse,
+) => {
+ await useCors(req, res);
+ await useAuth(req, res);
+
+ req.yup = schema;
+ await useValidate(req, res);
+
+ if (req.method === 'POST') {
+ const {
+ websiteId,
+ dateRange: { startDate, endDate },
+ fields,
+ filters,
+ } = req.body;
+
+ if (!(await canViewWebsite(req.auth, websiteId))) {
+ return unauthorized(res);
+ }
+
+ const data = await getInsights(websiteId, fields, {
+ ...convertFilters(filters),
+ startDate: new Date(startDate),
+ endDate: new Date(endDate),
+ });
+
+ return ok(res, data);
+ }
+
+ return methodNotAllowed(res);
+};
diff --git a/pages/api/reports/retention.ts b/src/pages/api/reports/retention.ts
similarity index 59%
rename from pages/api/reports/retention.ts
rename to src/pages/api/reports/retention.ts
index 40b3266b..4006ab12 100644
--- a/pages/api/reports/retention.ts
+++ b/src/pages/api/reports/retention.ts
@@ -1,33 +1,43 @@
import { canViewWebsite } from 'lib/auth';
-import { useCors, useAuth } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
-import { ok, methodNotAllowed, unauthorized } from 'next-basics';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getRetention } from 'queries';
+import * as yup from 'yup';
export interface RetentionRequestBody {
websiteId: string;
- dateRange: { window; startDate: string; endDate: string };
- timezone: string;
+ dateRange: { startDate: string; endDate: string };
}
-export interface RetentionResponse {
- startAt: number;
- endAt: number;
-}
+const schema = {
+ POST: yup.object().shape({
+ websiteId: yup.string().uuid().required(),
+ dateRange: yup
+ .object()
+ .shape({
+ startDate: yup.date().required(),
+ endDate: yup.date().required(),
+ })
+ .required(),
+ }),
+};
export default async (
req: NextApiRequestQueryBody,
- res: NextApiResponse,
+ res: NextApiResponse,
) => {
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'POST') {
const {
websiteId,
dateRange: { startDate, endDate },
- timezone,
} = req.body;
if (!(await canViewWebsite(req.auth, websiteId))) {
@@ -37,7 +47,6 @@ export default async (
const data = await getRetention(websiteId, {
startDate: new Date(startDate),
endDate: new Date(endDate),
- timezone,
});
return ok(res, data);
diff --git a/pages/api/scripts/telemetry.js b/src/pages/api/scripts/telemetry.js
similarity index 100%
rename from pages/api/scripts/telemetry.js
rename to src/pages/api/scripts/telemetry.js
diff --git a/pages/api/send.ts b/src/pages/api/send.ts
similarity index 78%
rename from pages/api/send.ts
rename to src/pages/api/send.ts
index f90ded77..a379f261 100644
--- a/pages/api/send.ts
+++ b/src/pages/api/send.ts
@@ -1,14 +1,15 @@
-import isbot from 'isbot';
-import ipaddr from 'ipaddr.js';
-import { createToken, ok, send, badRequest, forbidden } from 'next-basics';
-import { saveEvent, saveSessionData } from 'queries';
-import { useCors, useSession } from 'lib/middleware';
-import { getJsonBody, getIpAddress } from 'lib/detect';
-import { secret } from 'lib/crypto';
-import { NextApiRequest, NextApiResponse } from 'next';
import { Resolver } from 'dns/promises';
-import { CollectionType } from 'lib/types';
-import { COLLECTION_TYPE } from 'lib/constants';
+import ipaddr from 'ipaddr.js';
+import isbot from 'isbot';
+import { COLLECTION_TYPE, HOSTNAME_REGEX } from 'lib/constants';
+import { secret } from 'lib/crypto';
+import { getIpAddress, getJsonBody } from 'lib/detect';
+import { useCors, useSession, useValidate } from 'lib/middleware';
+import { CollectionType, YupRequest } from 'lib/types';
+import { NextApiRequest, NextApiResponse } from 'next';
+import { badRequest, createToken, forbidden, ok, send } from 'next-basics';
+import { saveEvent, saveSessionData } from 'queries';
+import * as yup from 'yup';
export interface CollectRequestBody {
payload: {
@@ -43,8 +44,32 @@ export interface NextApiRequestCollect extends NextApiRequest {
city: string;
};
headers: { [key: string]: any };
+ yup: YupRequest;
}
+const schema = {
+ POST: yup.object().shape({
+ payload: yup
+ .object()
+ .shape({
+ data: yup.object(),
+ hostname: yup.string().matches(HOSTNAME_REGEX).max(100),
+ language: yup.string().max(35),
+ referrer: yup.string().max(500),
+ screen: yup.string().max(11),
+ title: yup.string().max(500),
+ url: yup.string().max(500),
+ website: yup.string().uuid().required(),
+ name: yup.string().max(50),
+ })
+ .required(),
+ type: yup
+ .string()
+ .matches(/event|identify/i)
+ .required(),
+ }),
+};
+
export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
await useCors(req, res);
@@ -54,11 +79,8 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
const { type, payload } = getJsonBody(req);
- const error = validateBody({ type, payload });
-
- if (error) {
- return badRequest(res, error);
- }
+ req.yup = schema;
+ await useValidate(req, res);
if (await hasBlockedIp(req)) {
return forbidden(res);
@@ -118,22 +140,6 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
return send(res, token);
};
-function validateBody({ type, payload }: CollectRequestBody) {
- if (!type || !payload) {
- return 'Invalid payload.';
- }
-
- if (type !== COLLECTION_TYPE.event && type !== COLLECTION_TYPE.identify) {
- return 'Wrong payload type.';
- }
-
- const { data } = payload;
-
- if (data && !(typeof data === 'object' && !Array.isArray(data))) {
- return 'Invalid event data.';
- }
-}
-
async function hasBlockedIp(req: NextApiRequestCollect) {
const ignoreIps = process.env.IGNORE_IP;
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
diff --git a/pages/api/share/[id].ts b/src/pages/api/share/[id].ts
similarity index 79%
rename from pages/api/share/[id].ts
rename to src/pages/api/share/[id].ts
index 0592d216..cc782246 100644
--- a/pages/api/share/[id].ts
+++ b/src/pages/api/share/[id].ts
@@ -1,8 +1,10 @@
-import { NextApiRequestQueryBody } from 'lib/types';
import { secret } from 'lib/crypto';
+import { useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { createToken, methodNotAllowed, notFound, ok } from 'next-basics';
import { getWebsiteByShareId } from 'queries';
+import * as yup from 'yup';
export interface ShareRequestQuery {
id: string;
@@ -13,10 +15,19 @@ export interface ShareResponse {
token: string;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: shareId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/teams/[id]/index.ts b/src/pages/api/teams/[id]/index.ts
similarity index 75%
rename from pages/api/teams/[id]/index.ts
rename to src/pages/api/teams/[id]/index.ts
index 7fb664a0..31c47b2f 100644
--- a/pages/api/teams/[id]/index.ts
+++ b/src/pages/api/teams/[id]/index.ts
@@ -1,10 +1,11 @@
import { Team } from '@prisma/client';
-import { NextApiRequestQueryBody } from 'lib/types';
import { canDeleteTeam, canUpdateTeam, canViewTeam } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteTeam, getTeamById, updateTeam } from 'queries';
+import * as yup from 'yup';
export interface TeamRequestQuery {
id: string;
@@ -15,12 +16,29 @@ export interface TeamRequestBody {
accessCode: string;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+ POST: yup.object().shape({
+ id: yup.string().uuid().required(),
+ name: yup.string().max(50).required(),
+ accessCode: yup.string().max(50).required(),
+ }),
+ DELETE: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: teamId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/teams/[id]/users/[userId].ts b/src/pages/api/teams/[id]/users/[userId].ts
similarity index 73%
rename from pages/api/teams/[id]/users/[userId].ts
rename to src/pages/api/teams/[id]/users/[userId].ts
index 1e4ca623..adb635d5 100644
--- a/pages/api/teams/[id]/users/[userId].ts
+++ b/src/pages/api/teams/[id]/users/[userId].ts
@@ -1,18 +1,28 @@
import { canDeleteTeamUser } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteTeamUser } from 'queries';
-
+import * as yup from 'yup';
export interface TeamUserRequestQuery {
id: string;
userId: string;
}
+const schema = {
+ DELETE: yup.object().shape({
+ id: yup.string().uuid().required(),
+ userId: yup.string().uuid().required(),
+ }),
+};
+
export default async (req: NextApiRequestQueryBody, res: NextApiResponse) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'DELETE') {
const { id: teamId, userId } = req.query;
diff --git a/pages/api/teams/[id]/users/index.ts b/src/pages/api/teams/[id]/users/index.ts
similarity index 57%
rename from pages/api/teams/[id]/users/index.ts
rename to src/pages/api/teams/[id]/users/index.ts
index 6f8b077e..52b25da6 100644
--- a/pages/api/teams/[id]/users/index.ts
+++ b/src/pages/api/teams/[id]/users/index.ts
@@ -1,9 +1,9 @@
-import { canUpdateTeam, canViewTeam } from 'lib/auth';
+import { canViewTeam } from 'lib/auth';
import { useAuth } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
import { NextApiResponse } from 'next';
-import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
-import { createTeamUser, getUserByUsername, getUsersByTeamId } from 'queries';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
+import { getUsersByTeamId } from 'queries';
export interface TeamUserRequestQuery extends SearchFilter {
id: string;
@@ -38,24 +38,5 @@ export default async (
return ok(res, users);
}
- if (req.method === 'POST') {
- if (!(await canUpdateTeam(req.auth, teamId))) {
- return unauthorized(res, 'You must be the owner of this team.');
- }
-
- const { email, roleId: roleId } = req.body;
-
- // Check for User
- const user = await getUserByUsername(email);
-
- if (!user) {
- return badRequest(res, 'The User does not exists.');
- }
-
- const updated = await createTeamUser(user.id, teamId, roleId);
-
- return ok(res, updated);
- }
-
return methodNotAllowed(res);
};
diff --git a/pages/api/teams/[id]/websites/[websiteId].ts b/src/pages/api/teams/[id]/websites/[websiteId].ts
similarity index 73%
rename from pages/api/teams/[id]/websites/[websiteId].ts
rename to src/pages/api/teams/[id]/websites/[websiteId].ts
index 795295d3..ada1efdc 100644
--- a/pages/api/teams/[id]/websites/[websiteId].ts
+++ b/src/pages/api/teams/[id]/websites/[websiteId].ts
@@ -1,21 +1,32 @@
import { canDeleteTeamWebsite } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteTeamWebsite } from 'queries/admin/teamWebsite';
+import * as yup from 'yup';
export interface TeamWebsitesRequestQuery {
id: string;
websiteId: string;
}
+const schema = {
+ DELETE: yup.object().shape({
+ id: yup.string().uuid().required(),
+ websiteId: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: teamId, websiteId } = req.query;
if (req.method === 'DELETE') {
diff --git a/pages/api/teams/[id]/websites/index.ts b/src/pages/api/teams/[id]/websites/index.ts
similarity index 71%
rename from pages/api/teams/[id]/websites/index.ts
rename to src/pages/api/teams/[id]/websites/index.ts
index dcd08939..4de32709 100644
--- a/pages/api/teams/[id]/websites/index.ts
+++ b/src/pages/api/teams/[id]/websites/index.ts
@@ -1,9 +1,10 @@
import { canViewTeam } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
-import { getWebsites, getWebsitesByTeamId } from 'queries';
+import { getWebsitesByTeamId } from 'queries';
import { createTeamWebsites } from 'queries/admin/teamWebsite';
export interface TeamWebsiteRequestQuery extends SearchFilter {
@@ -14,12 +15,28 @@ export interface TeamWebsiteRequestBody {
websiteIds?: string[];
}
+import * as yup from 'yup';
+
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ ...getFilterValidation(/All|Name|Domain/i),
+ }),
+ POST: yup.object().shape({
+ id: yup.string().uuid().required(),
+ websiteIds: yup.array().of(yup.string()).min(1).required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: teamId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/teams/index.ts b/src/pages/api/teams/index.ts
similarity index 72%
rename from pages/api/teams/index.ts
rename to src/pages/api/teams/index.ts
index 997ed885..dd742b9e 100644
--- a/pages/api/teams/index.ts
+++ b/src/pages/api/teams/index.ts
@@ -1,23 +1,39 @@
import { Team } from '@prisma/client';
import { canCreateTeam } from 'lib/auth';
import { uuid } from 'lib/crypto';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createTeam, getTeamsByUserId } from 'queries';
+import * as yup from 'yup';
export interface TeamsRequestQuery extends SearchFilter {}
-export interface TeamsRequestBody extends SearchFilter {
+export interface TeamsRequestBody {
name: string;
}
+export interface MyTeamsRequestQuery extends SearchFilter {}
+
+const schema = {
+ GET: yup.object().shape({
+ ...getFilterValidation(/All|Name|Owner/i),
+ }),
+ POST: yup.object().shape({
+ name: yup.string().max(50).required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const {
user: { id: userId },
} = req.auth;
diff --git a/pages/api/teams/join.ts b/src/pages/api/teams/join.ts
similarity index 76%
rename from pages/api/teams/join.ts
rename to src/pages/api/teams/join.ts
index ce7367a0..06feda8a 100644
--- a/pages/api/teams/join.ts
+++ b/src/pages/api/teams/join.ts
@@ -1,21 +1,30 @@
import { Team } from '@prisma/client';
-import { NextApiRequestQueryBody } from 'lib/types';
-import { useAuth } from 'lib/middleware';
-import { NextApiResponse } from 'next';
-import { methodNotAllowed, ok, notFound } from 'next-basics';
-import { createTeamUser, getTeamByAccessCode, getTeamUser } from 'queries';
import { ROLES } from 'lib/constants';
-
+import { useAuth, useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody } from 'lib/types';
+import { NextApiResponse } from 'next';
+import { methodNotAllowed, notFound, ok } from 'next-basics';
+import { createTeamUser, getTeamByAccessCode, getTeamUser } from 'queries';
+import * as yup from 'yup';
export interface TeamsJoinRequestBody {
accessCode: string;
}
+const schema = {
+ POST: yup.object().shape({
+ accessCode: yup.string().max(50).required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'POST') {
const { accessCode } = req.body;
diff --git a/pages/api/users/[id]/index.ts b/src/pages/api/users/[id]/index.ts
similarity index 82%
rename from pages/api/users/[id]/index.ts
rename to src/pages/api/users/[id]/index.ts
index e09b1b5f..3ac560ed 100644
--- a/pages/api/users/[id]/index.ts
+++ b/src/pages/api/users/[id]/index.ts
@@ -1,9 +1,10 @@
-import { NextApiRequestQueryBody, Role, User } from 'lib/types';
import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody, Role, User } from 'lib/types';
import { NextApiResponse } from 'next';
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteUser, getUserById, getUserByUsername, updateUser } from 'queries';
+import * as yup from 'yup';
export interface UserRequestQuery {
id: string;
@@ -15,12 +16,27 @@ export interface UserRequestBody {
role: Role;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+ POST: yup.object().shape({
+ id: yup.string().uuid().required(),
+ username: yup.string().max(255),
+ password: yup.string(),
+ role: yup.string().matches(/admin|user|view-only/i),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const {
user: { id: userId, isAdmin },
} = req.auth;
diff --git a/pages/api/users/[id]/teams.ts b/src/pages/api/users/[id]/teams.ts
similarity index 75%
rename from pages/api/users/[id]/teams.ts
rename to src/pages/api/users/[id]/teams.ts
index 831a992d..eb34410c 100644
--- a/pages/api/users/[id]/teams.ts
+++ b/src/pages/api/users/[id]/teams.ts
@@ -1,9 +1,10 @@
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getTeamsByUserId } from 'queries';
-
+import * as yup from 'yup';
export interface UserTeamsRequestQuery extends SearchFilter {
id: string;
}
@@ -14,6 +15,13 @@ export interface UserTeamsRequestBody {
shareId: string;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ ...getFilterValidation('/All|Name|Owner/i'),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -21,6 +29,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { user } = req.auth;
const { id: userId } = req.query;
diff --git a/pages/api/users/[id]/usage.ts b/src/pages/api/users/[id]/usage.ts
similarity index 84%
rename from pages/api/users/[id]/usage.ts
rename to src/pages/api/users/[id]/usage.ts
index 0118df92..b0fc2055 100644
--- a/pages/api/users/[id]/usage.ts
+++ b/src/pages/api/users/[id]/usage.ts
@@ -1,8 +1,9 @@
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getEventDataUsage, getEventUsage, getUserWebsites } from 'queries';
+import * as yup from 'yup';
export interface UserUsageRequestQuery {
id: string;
@@ -21,6 +22,14 @@ export interface UserUsageRequestResponse {
}[];
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -28,6 +37,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { user } = req.auth;
if (req.method === 'GET') {
diff --git a/pages/api/users/[id]/websites.ts b/src/pages/api/users/[id]/websites.ts
similarity index 64%
rename from pages/api/users/[id]/websites.ts
rename to src/pages/api/users/[id]/websites.ts
index 0e9231f7..65e9a0e8 100644
--- a/pages/api/users/[id]/websites.ts
+++ b/src/pages/api/users/[id]/websites.ts
@@ -1,25 +1,36 @@
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getWebsitesByUserId } from 'queries';
+import * as yup from 'yup';
export interface UserWebsitesRequestQuery extends SearchFilter {
id: string;
-}
-export interface UserWebsitesRequestBody {
- name: string;
- domain: string;
- shareId: string;
+ includeTeams?: boolean;
+ onlyTeams?: boolean;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ includeTeams: yup.boolean(),
+ onlyTeams: yup.boolean(),
+ ...getFilterValidation(/All|Name|Domain/i),
+ }),
+};
+
export default async (
- req: NextApiRequestQueryBody,
+ req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { user } = req.auth;
const { id: userId, page, filter, pageSize, includeTeams, onlyTeams } = req.query;
diff --git a/pages/api/users/index.ts b/src/pages/api/users/index.ts
similarity index 70%
rename from pages/api/users/index.ts
rename to src/pages/api/users/index.ts
index 5e913c02..991986e8 100644
--- a/pages/api/users/index.ts
+++ b/src/pages/api/users/index.ts
@@ -1,8 +1,9 @@
import { canCreateUser, canViewUsers } from 'lib/auth';
import { ROLES } from 'lib/constants';
import { uuid } from 'lib/crypto';
-import { useAuth } from 'lib/middleware';
+import { useAuth, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, Role, SearchFilter, User, UserSearchFilterType } from 'lib/types';
+import { getFilterValidation } from 'lib/yup';
import { NextApiResponse } from 'next';
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createUser, getUserByUsername, getUsers } from 'queries';
@@ -12,15 +13,34 @@ export interface UsersRequestBody {
username: string;
password: string;
id: string;
- role?: Role;
+ role: Role;
}
+import * as yup from 'yup';
+const schema = {
+ GET: yup.object().shape({
+ ...getFilterValidation(/All|Username/i),
+ }),
+ POST: yup.object().shape({
+ username: yup.string().max(255).required(),
+ password: yup.string().required(),
+ id: yup.string().uuid(),
+ role: yup
+ .string()
+ .matches(/admin|user|view-only/i)
+ .required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
if (req.method === 'GET') {
if (!(await canViewUsers(req.auth))) {
return unauthorized(res);
@@ -28,7 +48,7 @@ export default async (
const { page, filter, pageSize } = req.query;
- const users = await getUsers({ page, filter, pageSize: +pageSize || null });
+ const users = await getUsers({ page, filter, pageSize: pageSize ? +pageSize : null });
return ok(res, users);
}
diff --git a/pages/api/websites/[id]/active.ts b/src/pages/api/websites/[id]/active.ts
similarity index 76%
rename from pages/api/websites/[id]/active.ts
rename to src/pages/api/websites/[id]/active.ts
index 99c8d999..abc23dd7 100644
--- a/pages/api/websites/[id]/active.ts
+++ b/src/pages/api/websites/[id]/active.ts
@@ -1,14 +1,21 @@
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getActiveVisitors } from 'queries';
+import * as yup from 'yup';
export interface WebsiteActiveRequestQuery {
id: string;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -16,6 +23,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: websiteId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/websites/[id]/daterange.ts b/src/pages/api/websites/[id]/daterange.ts
similarity index 77%
rename from pages/api/websites/[id]/daterange.ts
rename to src/pages/api/websites/[id]/daterange.ts
index dc043560..bfa5338e 100644
--- a/pages/api/websites/[id]/daterange.ts
+++ b/src/pages/api/websites/[id]/daterange.ts
@@ -1,14 +1,21 @@
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getWebsiteDateRange } from 'queries';
+import * as yup from 'yup';
export interface WebsiteDateRangeRequestQuery {
id: string;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -16,6 +23,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: websiteId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/websites/[id]/events.ts b/src/pages/api/websites/[id]/events.ts
similarity index 70%
rename from pages/api/websites/[id]/events.ts
rename to src/pages/api/websites/[id]/events.ts
index 7d4f999f..427cb40e 100644
--- a/pages/api/websites/[id]/events.ts
+++ b/src/pages/api/websites/[id]/events.ts
@@ -1,6 +1,6 @@
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import moment from 'moment-timezone';
import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
@@ -16,9 +16,21 @@ export interface WebsiteEventsRequestQuery {
unit: string;
timezone: string;
url: string;
- eventName: string;
}
+import * as yup from 'yup';
+
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+ unit: yup.string().required(),
+ timezone: yup.string().required(),
+ url: yup.string(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -26,7 +38,10 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
- const { id: websiteId, timezone, url, eventName } = req.query;
+ req.yup = schema;
+ await useValidate(req, res);
+
+ const { id: websiteId, timezone, url } = req.query;
const { startDate, endDate, unit } = await parseDateRangeQuery(req);
if (req.method === 'GET') {
@@ -44,7 +59,6 @@ export default async (
timezone,
unit,
url,
- eventName,
});
return ok(res, events);
diff --git a/pages/api/websites/[id]/index.ts b/src/pages/api/websites/[id]/index.ts
similarity index 88%
rename from pages/api/websites/[id]/index.ts
rename to src/pages/api/websites/[id]/index.ts
index 3d053d0e..0e5aacce 100644
--- a/pages/api/websites/[id]/index.ts
+++ b/src/pages/api/websites/[id]/index.ts
@@ -2,7 +2,7 @@ import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
import { Website, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite, canUpdateWebsite, canDeleteWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { deleteWebsite, getWebsiteById, updateWebsite } from 'queries';
import { SHARE_ID_REGEX } from 'lib/constants';
@@ -16,6 +16,14 @@ export interface WebsiteRequestBody {
shareId: string;
}
+import * as yup from 'yup';
+
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -23,6 +31,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: websiteId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/websites/[id]/metrics.ts b/src/pages/api/websites/[id]/metrics.ts
similarity index 79%
rename from pages/api/websites/[id]/metrics.ts
rename to src/pages/api/websites/[id]/metrics.ts
index 7c84583c..b8c37339 100644
--- a/pages/api/websites/[id]/metrics.ts
+++ b/src/pages/api/websites/[id]/metrics.ts
@@ -2,30 +2,40 @@ import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { SESSION_COLUMNS, EVENT_COLUMNS, FILTER_COLUMNS } from 'lib/constants';
import { getPageviewMetrics, getSessionMetrics } from 'queries';
import { parseDateRangeQuery } from 'lib/query';
+import * as yup from 'yup';
export interface WebsiteMetricsRequestQuery {
id: string;
type: string;
startAt: number;
endAt: number;
- url: string;
- referrer: string;
- title: string;
- query: string;
- event: string;
- os: string;
- browser: string;
- device: string;
- country: string;
- region: string;
- city: string;
- language: string;
+ url?: string;
+ referrer?: string;
+ title?: string;
+ query?: string;
+ os?: string;
+ browser?: string;
+ device?: string;
+ country?: string;
+ region?: string;
+ city?: string;
+ language?: string;
+ event?: string;
}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ type: yup.string().required(),
+ startAt: yup.number().required(),
+ endAt: yup.number().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -33,6 +43,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const {
id: websiteId,
type,
@@ -40,7 +53,6 @@ export default async (
referrer,
title,
query,
- event,
os,
browser,
device,
@@ -48,6 +60,7 @@ export default async (
region,
city,
language,
+ event,
} = req.query;
if (req.method === 'GET') {
@@ -64,7 +77,6 @@ export default async (
referrer,
title,
query,
- event,
os,
browser,
device,
@@ -72,10 +84,9 @@ export default async (
region,
city,
language,
+ event,
};
- filters[type] = undefined;
-
const column = FILTER_COLUMNS[type] || type;
if (SESSION_COLUMNS.includes(type)) {
diff --git a/pages/api/websites/[id]/pageviews.ts b/src/pages/api/websites/[id]/pageviews.ts
similarity index 87%
rename from pages/api/websites/[id]/pageviews.ts
rename to src/pages/api/websites/[id]/pageviews.ts
index c5532e76..9985ca89 100644
--- a/pages/api/websites/[id]/pageviews.ts
+++ b/src/pages/api/websites/[id]/pageviews.ts
@@ -3,7 +3,7 @@ import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { getPageviewStats, getSessionStats } from 'queries';
import { parseDateRangeQuery } from 'lib/query';
@@ -24,6 +24,13 @@ export interface WebsitePageviewRequestQuery {
city?: string;
}
+import * as yup from 'yup';
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -31,6 +38,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const {
id: websiteId,
timezone,
diff --git a/pages/api/websites/[id]/reports.ts b/src/pages/api/websites/[id]/reports.ts
similarity index 80%
rename from pages/api/websites/[id]/reports.ts
rename to src/pages/api/websites/[id]/reports.ts
index 60c6f714..738f6b37 100644
--- a/pages/api/websites/[id]/reports.ts
+++ b/src/pages/api/websites/[id]/reports.ts
@@ -1,5 +1,5 @@
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
@@ -9,6 +9,13 @@ export interface ReportsRequestQuery extends SearchFilter,
res: NextApiResponse,
@@ -16,6 +23,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: websiteId } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/websites/[id]/reset.ts b/src/pages/api/websites/[id]/reset.ts
similarity index 75%
rename from pages/api/websites/[id]/reset.ts
rename to src/pages/api/websites/[id]/reset.ts
index 23b5305d..cfd5e767 100644
--- a/pages/api/websites/[id]/reset.ts
+++ b/src/pages/api/websites/[id]/reset.ts
@@ -1,6 +1,6 @@
import { NextApiRequestQueryBody } from 'lib/types';
import { canUpdateWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { resetWebsite } from 'queries';
@@ -9,6 +9,13 @@ export interface WebsiteResetRequestQuery {
id: string;
}
+import * as yup from 'yup';
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -16,6 +23,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: websiteId } = req.query;
if (req.method === 'POST') {
diff --git a/pages/api/websites/[id]/stats.ts b/src/pages/api/websites/[id]/stats.ts
similarity index 89%
rename from pages/api/websites/[id]/stats.ts
rename to src/pages/api/websites/[id]/stats.ts
index a77c7eaf..caf54910 100644
--- a/pages/api/websites/[id]/stats.ts
+++ b/src/pages/api/websites/[id]/stats.ts
@@ -2,7 +2,7 @@ import { subMinutes, differenceInMinutes } from 'date-fns';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, WebsiteStats } from 'lib/types';
import { parseDateRangeQuery } from 'lib/query';
import { getWebsiteStats } from 'queries';
@@ -24,6 +24,13 @@ export interface WebsiteStatsRequestQuery {
city: string;
}
+import * as yup from 'yup';
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -31,6 +38,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const {
id: websiteId,
url,
diff --git a/pages/api/websites/[id]/values.ts b/src/pages/api/websites/[id]/values.ts
similarity index 82%
rename from pages/api/websites/[id]/values.ts
rename to src/pages/api/websites/[id]/values.ts
index ad8625bd..d90a1682 100644
--- a/pages/api/websites/[id]/values.ts
+++ b/src/pages/api/websites/[id]/values.ts
@@ -1,6 +1,6 @@
import { NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { EVENT_COLUMNS, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants';
@@ -10,6 +10,13 @@ export interface WebsiteResetRequestQuery {
id: string;
}
+import * as yup from 'yup';
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
@@ -17,6 +24,9 @@ export default async (
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
+
const { id: websiteId, type } = req.query;
if (req.method === 'GET') {
diff --git a/pages/api/websites/index.ts b/src/pages/api/websites/index.ts
similarity index 72%
rename from pages/api/websites/index.ts
rename to src/pages/api/websites/index.ts
index f94fa037..d724f12f 100644
--- a/pages/api/websites/index.ts
+++ b/src/pages/api/websites/index.ts
@@ -1,11 +1,13 @@
import { canCreateWebsite } from 'lib/auth';
import { uuid } from 'lib/crypto';
-import { useAuth, useCors } from 'lib/middleware';
+import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createWebsite } from 'queries';
import userWebsites from 'pages/api/users/[id]/websites';
+import * as yup from 'yup';
+import { getFilterValidation } from 'lib/yup';
export interface WebsitesRequestQuery extends SearchFilter {}
@@ -15,12 +17,25 @@ export interface WebsitesRequestBody {
shareId: string;
}
+const schema = {
+ GET: yup.object().shape({
+ ...getFilterValidation(/All|Name|Domain/i),
+ }),
+ POST: yup.object().shape({
+ name: yup.string().max(100).required(),
+ domain: yup.string().max(500).required(),
+ shareId: yup.string().max(50),
+ }),
+};
+
export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useCors(req, res);
await useAuth(req, res);
+ req.yup = schema;
+ await useValidate(req, res);
const {
user: { id: userId },
@@ -30,7 +45,7 @@ export default async (
req.query.id = userId;
req.query.pageSize = 100;
- return userWebsites(req, res);
+ return userWebsites(req as any, res);
}
if (req.method === 'POST') {
diff --git a/pages/console/[[...id]].js b/src/pages/console/[[...id]].js
similarity index 100%
rename from pages/console/[[...id]].js
rename to src/pages/console/[[...id]].js
diff --git a/pages/dashboard/index.js b/src/pages/dashboard/index.js
similarity index 85%
rename from pages/dashboard/index.js
rename to src/pages/dashboard/index.js
index 061697f4..c1a3c09e 100644
--- a/pages/dashboard/index.js
+++ b/src/pages/dashboard/index.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import Dashboard from 'components/pages/dashboard/Dashboard';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function DashboardPage() {
const { formatMessage, labels } = useMessages();
diff --git a/pages/index.js b/src/pages/index.js
similarity index 100%
rename from pages/index.js
rename to src/pages/index.js
diff --git a/pages/login.js b/src/pages/login.js
similarity index 100%
rename from pages/login.js
rename to src/pages/login.js
diff --git a/pages/logout.js b/src/pages/logout.js
similarity index 93%
rename from pages/logout.js
rename to src/pages/logout.js
index 675f1932..ef89080c 100644
--- a/pages/logout.js
+++ b/src/pages/logout.js
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useRouter } from 'next/router';
-import useApi from 'hooks/useApi';
+import useApi from 'components/hooks/useApi';
import { setUser } from 'store/app';
import { removeClientAuthToken } from 'lib/client';
diff --git a/pages/reports/[id].js b/src/pages/reports/[id].js
similarity index 92%
rename from pages/reports/[id].js
rename to src/pages/reports/[id].js
index 2520e87d..101881a1 100644
--- a/pages/reports/[id].js
+++ b/src/pages/reports/[id].js
@@ -1,7 +1,7 @@
import { useRouter } from 'next/router';
import AppLayout from 'components/layout/AppLayout';
import ReportDetails from 'components/pages/reports/ReportDetails';
-import { useApi, useMessages } from 'hooks';
+import { useApi, useMessages } from 'components/hooks';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/reports/create.js b/src/pages/reports/create.js
similarity index 87%
rename from pages/reports/create.js
rename to src/pages/reports/create.js
index 763e2c63..08f97f28 100644
--- a/pages/reports/create.js
+++ b/src/pages/reports/create.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import ReportTemplates from 'components/pages/reports/ReportTemplates';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/reports/funnel.js b/src/pages/reports/funnel.js
similarity index 86%
rename from pages/reports/funnel.js
rename to src/pages/reports/funnel.js
index 4acdef37..78174f7b 100644
--- a/pages/reports/funnel.js
+++ b/src/pages/reports/funnel.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import FunnelReport from 'components/pages/reports/funnel/FunnelReport';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/reports/index.js b/src/pages/reports/index.js
similarity index 86%
rename from pages/reports/index.js
rename to src/pages/reports/index.js
index ff3b4e86..a1a13a68 100644
--- a/pages/reports/index.js
+++ b/src/pages/reports/index.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import ReportsPage from 'components/pages/reports/ReportsPage';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/reports/insights.js b/src/pages/reports/insights.js
similarity index 88%
rename from pages/reports/insights.js
rename to src/pages/reports/insights.js
index 45236e10..c5220721 100644
--- a/pages/reports/insights.js
+++ b/src/pages/reports/insights.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import InsightsReport from 'components/pages/reports/insights/InsightsReport';
-import { useMessages } from 'hooks';
+import { useMessages } from 'components/hooks';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/reports/retention.js b/src/pages/reports/retention.js
similarity index 86%
rename from pages/reports/retention.js
rename to src/pages/reports/retention.js
index b7f0bd0f..7f5d4cf2 100644
--- a/pages/reports/retention.js
+++ b/src/pages/reports/retention.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import RetentionReport from 'components/pages/reports/retention/RetentionReport';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/settings/profile/index.js b/src/pages/settings/profile/index.js
similarity index 89%
rename from pages/settings/profile/index.js
rename to src/pages/settings/profile/index.js
index 8827f1da..d340c193 100644
--- a/pages/settings/profile/index.js
+++ b/src/pages/settings/profile/index.js
@@ -1,7 +1,7 @@
import AppLayout from 'components/layout/AppLayout';
import SettingsLayout from 'components/layout/SettingsLayout';
import ProfileSettings from 'components/pages/settings/profile/ProfileSettings';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/settings/teams/[id].js b/src/pages/settings/teams/[id].js
similarity index 93%
rename from pages/settings/teams/[id].js
rename to src/pages/settings/teams/[id].js
index a68ef80c..775a6a08 100644
--- a/pages/settings/teams/[id].js
+++ b/src/pages/settings/teams/[id].js
@@ -2,7 +2,7 @@ import AppLayout from 'components/layout/AppLayout';
import SettingsLayout from 'components/layout/SettingsLayout';
import TeamSettings from 'components/pages/settings/teams/TeamSettings';
import { useRouter } from 'next/router';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function ({ disabled }) {
const router = useRouter();
diff --git a/pages/settings/teams/index.js b/src/pages/settings/teams/index.js
similarity index 91%
rename from pages/settings/teams/index.js
rename to src/pages/settings/teams/index.js
index 51739c31..7e56a7d7 100644
--- a/pages/settings/teams/index.js
+++ b/src/pages/settings/teams/index.js
@@ -1,7 +1,7 @@
import AppLayout from 'components/layout/AppLayout';
import SettingsLayout from 'components/layout/SettingsLayout';
import TeamsList from 'components/pages/settings/teams/TeamsList';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function ({ disabled }) {
const { formatMessage, labels } = useMessages();
diff --git a/pages/settings/users/[id].js b/src/pages/settings/users/[id].js
similarity index 93%
rename from pages/settings/users/[id].js
rename to src/pages/settings/users/[id].js
index d1e53419..fdd708fd 100644
--- a/pages/settings/users/[id].js
+++ b/src/pages/settings/users/[id].js
@@ -2,7 +2,7 @@ import AppLayout from 'components/layout/AppLayout';
import SettingsLayout from 'components/layout/SettingsLayout';
import UserSettings from 'components/pages/settings/users/UserSettings';
import { useRouter } from 'next/router';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function ({ disabled }) {
const router = useRouter();
diff --git a/pages/settings/users/index.js b/src/pages/settings/users/index.js
similarity index 91%
rename from pages/settings/users/index.js
rename to src/pages/settings/users/index.js
index ee325adc..90026d87 100644
--- a/pages/settings/users/index.js
+++ b/src/pages/settings/users/index.js
@@ -1,7 +1,7 @@
import AppLayout from 'components/layout/AppLayout';
import SettingsLayout from 'components/layout/SettingsLayout';
import UsersList from 'components/pages/settings/users/UsersList';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function ({ disabled }) {
const { formatMessage, labels } = useMessages();
diff --git a/pages/settings/websites/[id].js b/src/pages/settings/websites/[id].js
similarity index 93%
rename from pages/settings/websites/[id].js
rename to src/pages/settings/websites/[id].js
index f828369e..506da107 100644
--- a/pages/settings/websites/[id].js
+++ b/src/pages/settings/websites/[id].js
@@ -2,7 +2,7 @@ import AppLayout from 'components/layout/AppLayout';
import { useRouter } from 'next/router';
import WebsiteSettings from 'components/pages/settings/websites/WebsiteSettings';
import SettingsLayout from 'components/layout/SettingsLayout';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function ({ disabled }) {
const router = useRouter();
diff --git a/pages/settings/websites/index.js b/src/pages/settings/websites/index.js
similarity index 92%
rename from pages/settings/websites/index.js
rename to src/pages/settings/websites/index.js
index 899ad7c7..f4551f4a 100644
--- a/pages/settings/websites/index.js
+++ b/src/pages/settings/websites/index.js
@@ -1,7 +1,7 @@
import AppLayout from 'components/layout/AppLayout';
import SettingsLayout from 'components/layout/SettingsLayout';
import WebsitesList from 'components/pages/settings/websites/WebsitesList';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function ({ disabled }) {
const { formatMessage, labels } = useMessages();
diff --git a/pages/share/[...id].js b/src/pages/share/[...id].js
similarity index 89%
rename from pages/share/[...id].js
rename to src/pages/share/[...id].js
index 1e424382..a2c69df8 100644
--- a/pages/share/[...id].js
+++ b/src/pages/share/[...id].js
@@ -1,7 +1,7 @@
import { useRouter } from 'next/router';
import ShareLayout from 'components/layout/ShareLayout';
import WebsiteDetailsPage from 'components/pages/websites/WebsiteDetailsPage';
-import useShareToken from 'hooks/useShareToken';
+import useShareToken from 'components/hooks/useShareToken';
export default function () {
const router = useRouter();
diff --git a/pages/sso.js b/src/pages/sso.js
similarity index 100%
rename from pages/sso.js
rename to src/pages/sso.js
diff --git a/pages/websites/[id]/event-data.js b/src/pages/websites/[id]/event-data.js
similarity index 89%
rename from pages/websites/[id]/event-data.js
rename to src/pages/websites/[id]/event-data.js
index 8b44616d..b99d2fc9 100644
--- a/pages/websites/[id]/event-data.js
+++ b/src/pages/websites/[id]/event-data.js
@@ -1,7 +1,7 @@
import { useRouter } from 'next/router';
import AppLayout from 'components/layout/AppLayout';
import WebsiteEventDataPage from 'components/pages/websites/WebsiteEventDataPage';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/websites/[id]/index.js b/src/pages/websites/[id]/index.js
similarity index 89%
rename from pages/websites/[id]/index.js
rename to src/pages/websites/[id]/index.js
index bec7a45f..d3ec5f93 100644
--- a/pages/websites/[id]/index.js
+++ b/src/pages/websites/[id]/index.js
@@ -1,7 +1,7 @@
import { useRouter } from 'next/router';
import AppLayout from 'components/layout/AppLayout';
import WebsiteDetailsPage from 'components/pages/websites/WebsiteDetailsPage';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/pages/websites/[id]/realtime.js b/src/pages/websites/[id]/realtime.js
similarity index 100%
rename from pages/websites/[id]/realtime.js
rename to src/pages/websites/[id]/realtime.js
diff --git a/pages/websites/[id]/reports.js b/src/pages/websites/[id]/reports.js
similarity index 100%
rename from pages/websites/[id]/reports.js
rename to src/pages/websites/[id]/reports.js
diff --git a/pages/websites/index.js b/src/pages/websites/index.js
similarity index 84%
rename from pages/websites/index.js
rename to src/pages/websites/index.js
index 42a327bc..43eed640 100644
--- a/pages/websites/index.js
+++ b/src/pages/websites/index.js
@@ -1,6 +1,6 @@
import AppLayout from 'components/layout/AppLayout';
import WebsitesPage from 'components/pages/websites/WebsitesPage';
-import useMessages from 'hooks/useMessages';
+import useMessages from 'components/hooks/useMessages';
export default function () {
const { formatMessage, labels } = useMessages();
diff --git a/queries/admin/report.ts b/src/queries/admin/report.ts
similarity index 100%
rename from queries/admin/report.ts
rename to src/queries/admin/report.ts
diff --git a/queries/admin/team.ts b/src/queries/admin/team.ts
similarity index 100%
rename from queries/admin/team.ts
rename to src/queries/admin/team.ts
diff --git a/queries/admin/teamUser.ts b/src/queries/admin/teamUser.ts
similarity index 100%
rename from queries/admin/teamUser.ts
rename to src/queries/admin/teamUser.ts
diff --git a/queries/admin/teamWebsite.ts b/src/queries/admin/teamWebsite.ts
similarity index 100%
rename from queries/admin/teamWebsite.ts
rename to src/queries/admin/teamWebsite.ts
diff --git a/queries/admin/user.ts b/src/queries/admin/user.ts
similarity index 94%
rename from queries/admin/user.ts
rename to src/queries/admin/user.ts
index dfb923f3..dfe8ea28 100644
--- a/queries/admin/user.ts
+++ b/src/queries/admin/user.ts
@@ -37,10 +37,10 @@ export async function getUserByUsername(username: string, options: GetUserOption
}
export async function getUsers(
- UserSearchFilter: UserSearchFilter = {},
+ searchFilter: UserSearchFilter,
options?: { include?: Prisma.UserInclude },
): Promise> {
- const { teamId, filter, filterType = USER_FILTER_TYPES.all } = UserSearchFilter;
+ const { teamId, filter, filterType = USER_FILTER_TYPES.all } = searchFilter;
const mode = prisma.getSearchMode();
const where: Prisma.UserWhereInput = {
@@ -67,19 +67,25 @@ export async function getUsers(
},
}),
};
+
const [pageFilters, getParameters] = prisma.getPageFilters({
orderBy: 'username',
- ...UserSearchFilter,
+ ...searchFilter,
});
- const users = await prisma.client.user.findMany({
- where: {
- ...where,
- deletedAt: null,
- },
- ...pageFilters,
- ...(options?.include && { include: options.include }),
- });
+ const users = await prisma.client.user
+ .findMany({
+ where: {
+ ...where,
+ deletedAt: null,
+ },
+ ...pageFilters,
+ ...(options?.include && { include: options.include }),
+ })
+ .then(a => {
+ return a.map(({ password, ...rest }) => rest);
+ });
+
const count = await prisma.client.user.count({
where: {
...where,
diff --git a/queries/admin/website.ts b/src/queries/admin/website.ts
similarity index 100%
rename from queries/admin/website.ts
rename to src/queries/admin/website.ts
diff --git a/queries/analytics/eventData/getEventDataEvents.ts b/src/queries/analytics/eventData/getEventDataEvents.ts
similarity index 100%
rename from queries/analytics/eventData/getEventDataEvents.ts
rename to src/queries/analytics/eventData/getEventDataEvents.ts
diff --git a/queries/analytics/eventData/getEventDataFields.ts b/src/queries/analytics/eventData/getEventDataFields.ts
similarity index 100%
rename from queries/analytics/eventData/getEventDataFields.ts
rename to src/queries/analytics/eventData/getEventDataFields.ts
diff --git a/queries/analytics/eventData/getEventDataStats.ts b/src/queries/analytics/eventData/getEventDataStats.ts
similarity index 100%
rename from queries/analytics/eventData/getEventDataStats.ts
rename to src/queries/analytics/eventData/getEventDataStats.ts
diff --git a/queries/analytics/eventData/getEventDataUsage.ts b/src/queries/analytics/eventData/getEventDataUsage.ts
similarity index 100%
rename from queries/analytics/eventData/getEventDataUsage.ts
rename to src/queries/analytics/eventData/getEventDataUsage.ts
diff --git a/queries/analytics/eventData/saveEventData.ts b/src/queries/analytics/eventData/saveEventData.ts
similarity index 100%
rename from queries/analytics/eventData/saveEventData.ts
rename to src/queries/analytics/eventData/saveEventData.ts
diff --git a/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts
similarity index 100%
rename from queries/analytics/events/getEventMetrics.ts
rename to src/queries/analytics/events/getEventMetrics.ts
diff --git a/queries/analytics/events/getEventUsage.ts b/src/queries/analytics/events/getEventUsage.ts
similarity index 100%
rename from queries/analytics/events/getEventUsage.ts
rename to src/queries/analytics/events/getEventUsage.ts
diff --git a/queries/analytics/events/getEvents.ts b/src/queries/analytics/events/getEvents.ts
similarity index 100%
rename from queries/analytics/events/getEvents.ts
rename to src/queries/analytics/events/getEvents.ts
diff --git a/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts
similarity index 94%
rename from queries/analytics/events/saveEvent.ts
rename to src/queries/analytics/events/saveEvent.ts
index 51087a59..a6457d7e 100644
--- a/queries/analytics/events/saveEvent.ts
+++ b/src/queries/analytics/events/saveEvent.ts
@@ -137,10 +137,15 @@ async function clickhouseQuery(data: {
website_id: websiteId,
session_id: sessionId,
event_id: uuid(),
- country: country ? country : null,
- subdivision1: country && subdivision1 ? `${country}-${subdivision1}` : null,
- subdivision2: subdivision2 ? subdivision2 : null,
- city: city ? city : null,
+ country: country,
+ subdivision1:
+ country && subdivision1
+ ? subdivision1.includes('-')
+ ? subdivision1
+ : `${country}-${subdivision1}`
+ : null,
+ subdivision2: subdivision2,
+ city: city,
url_path: urlPath?.substring(0, URL_LENGTH),
url_query: urlQuery?.substring(0, URL_LENGTH),
referrer_path: referrerPath?.substring(0, URL_LENGTH),
diff --git a/queries/analytics/getActiveVisitors.ts b/src/queries/analytics/getActiveVisitors.ts
similarity index 100%
rename from queries/analytics/getActiveVisitors.ts
rename to src/queries/analytics/getActiveVisitors.ts
diff --git a/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts
similarity index 100%
rename from queries/analytics/getRealtimeData.ts
rename to src/queries/analytics/getRealtimeData.ts
diff --git a/queries/analytics/getValues.ts b/src/queries/analytics/getValues.ts
similarity index 100%
rename from queries/analytics/getValues.ts
rename to src/queries/analytics/getValues.ts
diff --git a/queries/analytics/getWebsiteDateRange.ts b/src/queries/analytics/getWebsiteDateRange.ts
similarity index 100%
rename from queries/analytics/getWebsiteDateRange.ts
rename to src/queries/analytics/getWebsiteDateRange.ts
diff --git a/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts
similarity index 100%
rename from queries/analytics/getWebsiteStats.ts
rename to src/queries/analytics/getWebsiteStats.ts
diff --git a/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts
similarity index 100%
rename from queries/analytics/pageviews/getPageviewMetrics.ts
rename to src/queries/analytics/pageviews/getPageviewMetrics.ts
diff --git a/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts
similarity index 100%
rename from queries/analytics/pageviews/getPageviewStats.ts
rename to src/queries/analytics/pageviews/getPageviewStats.ts
diff --git a/queries/analytics/reports/getFunnel.ts b/src/queries/analytics/reports/getFunnel.ts
similarity index 100%
rename from queries/analytics/reports/getFunnel.ts
rename to src/queries/analytics/reports/getFunnel.ts
diff --git a/queries/analytics/reports/getInsights.ts b/src/queries/analytics/reports/getInsights.ts
similarity index 100%
rename from queries/analytics/reports/getInsights.ts
rename to src/queries/analytics/reports/getInsights.ts
diff --git a/queries/analytics/reports/getRetention.ts b/src/queries/analytics/reports/getRetention.ts
similarity index 100%
rename from queries/analytics/reports/getRetention.ts
rename to src/queries/analytics/reports/getRetention.ts
diff --git a/queries/analytics/sessions/createSession.ts b/src/queries/analytics/sessions/createSession.ts
similarity index 89%
rename from queries/analytics/sessions/createSession.ts
rename to src/queries/analytics/sessions/createSession.ts
index 4fd36d2e..65dbd794 100644
--- a/queries/analytics/sessions/createSession.ts
+++ b/src/queries/analytics/sessions/createSession.ts
@@ -30,7 +30,7 @@ export async function createSession(data: Prisma.SessionCreateInput) {
screen,
language,
country,
- subdivision1: country && subdivision1 ? `${country}-${subdivision1}` : null,
+ subdivision1,
subdivision2,
city,
},
diff --git a/queries/analytics/sessions/getSession.ts b/src/queries/analytics/sessions/getSession.ts
similarity index 100%
rename from queries/analytics/sessions/getSession.ts
rename to src/queries/analytics/sessions/getSession.ts
diff --git a/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts
similarity index 79%
rename from queries/analytics/sessions/getSessionMetrics.ts
rename to src/queries/analytics/sessions/getSessionMetrics.ts
index fb546a73..af358c52 100644
--- a/queries/analytics/sessions/getSessionMetrics.ts
+++ b/src/queries/analytics/sessions/getSessionMetrics.ts
@@ -25,17 +25,22 @@ async function relationalQuery(websiteId: string, column: string, filters: Query
joinSession: SESSION_COLUMNS.includes(column),
},
);
+ const includeCountry = column === 'city' || column === 'subdivision1';
return rawQuery(
`
- select ${column} x, count(*) y
+ select
+ ${column} x,
+ count(*) y
+ ${includeCountry ? ', country' : ''}
from website_event
${joinSession}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type = {{eventType}}
${filterQuery}
- group by 1
+ group by 1
+ ${includeCountry ? ', 3' : ''}
order by 2 desc
limit 100`,
params,
@@ -48,17 +53,21 @@ async function clickhouseQuery(websiteId: string, column: string, filters: Query
...filters,
eventType: EVENT_TYPE.pageView,
});
+ const includeCountry = column === 'city' || column === 'subdivision1';
return rawQuery(
`
select
- ${column} x, count(distinct session_id) y
+ ${column} x,
+ count(distinct session_id) y
+ ${includeCountry ? ', country' : ''}
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime} and {endDate:DateTime}
and event_type = {eventType:UInt32}
${filterQuery}
- group by x
+ group by x
+ ${includeCountry ? ', country' : ''}
order by y desc
limit 100
`,
diff --git a/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts
similarity index 100%
rename from queries/analytics/sessions/getSessionStats.ts
rename to src/queries/analytics/sessions/getSessionStats.ts
diff --git a/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts
similarity index 100%
rename from queries/analytics/sessions/getSessions.ts
rename to src/queries/analytics/sessions/getSessions.ts
diff --git a/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts
similarity index 100%
rename from queries/analytics/sessions/saveSessionData.ts
rename to src/queries/analytics/sessions/saveSessionData.ts
diff --git a/queries/index.js b/src/queries/index.js
similarity index 100%
rename from queries/index.js
rename to src/queries/index.js
diff --git a/store/app.js b/src/store/app.js
similarity index 100%
rename from store/app.js
rename to src/store/app.js
diff --git a/store/dashboard.js b/src/store/dashboard.js
similarity index 100%
rename from store/dashboard.js
rename to src/store/dashboard.js
diff --git a/store/queries.js b/src/store/queries.js
similarity index 100%
rename from store/queries.js
rename to src/store/queries.js
diff --git a/store/version.js b/src/store/version.js
similarity index 97%
rename from store/version.js
rename to src/store/version.js
index c232c7fa..3b5afaac 100644
--- a/store/version.js
+++ b/src/store/version.js
@@ -1,5 +1,5 @@
import { create } from 'zustand';
-import produce from 'immer';
+import { produce } from 'immer';
import semver from 'semver';
import { CURRENT_VERSION, VERSION_CHECK, UPDATES_URL } from 'lib/constants';
import { getItem } from 'next-basics';
diff --git a/store/websites.ts b/src/store/websites.ts
similarity index 94%
rename from store/websites.ts
rename to src/store/websites.ts
index 0d210af6..5d0eeccd 100644
--- a/store/websites.ts
+++ b/src/store/websites.ts
@@ -1,5 +1,5 @@
import { create } from 'zustand';
-import produce from 'immer';
+import { produce } from 'immer';
import { DateRange } from 'lib/types';
const store = create(() => ({}));
diff --git a/styles/index.css b/src/styles/index.css
similarity index 100%
rename from styles/index.css
rename to src/styles/index.css
diff --git a/styles/locale.css b/src/styles/locale.css
similarity index 100%
rename from styles/locale.css
rename to src/styles/locale.css
diff --git a/styles/variables.css b/src/styles/variables.css
similarity index 100%
rename from styles/variables.css
rename to src/styles/variables.css
diff --git a/tracker/index.d.ts b/src/tracker/index.d.ts
similarity index 100%
rename from tracker/index.d.ts
rename to src/tracker/index.d.ts
diff --git a/tracker/index.js b/src/tracker/index.js
similarity index 100%
rename from tracker/index.js
rename to src/tracker/index.js
diff --git a/tsconfig.json b/tsconfig.json
index b022d603..71094dd7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,6 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
- "incremental": true,
"lib": ["dom", "dom.iterable", "esnext"],
"skipLibCheck": true,
"esModuleInterop": true,
@@ -18,11 +17,21 @@
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"strict": true,
- "baseUrl": ".",
"strictNullChecks": false,
"noEmit": true,
- "jsx": "preserve"
+ "jsx": "preserve",
+ "incremental": false,
+ "baseUrl": "./src",
+ "paths": {
+ "assets/*": ["./assets/*"],
+ "components/*": ["./components/*"],
+ "lib/*": ["./lib/*"],
+ "pages/*": ["./pages/*"],
+ "queries/*": ["./queries/*"],
+ "store/*": ["./store/*"],
+ "styles/*": ["./styles/*"]
+ }
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"],
"exclude": ["node_modules"]
}
diff --git a/yarn.lock b/yarn.lock
index e18f833c..5d99a135 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15,7 +15,7 @@
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.5":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz"
integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==
@@ -1550,7 +1550,7 @@
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
-"@jridgewell/sourcemap-codec@^1.4.13":
+"@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@@ -1754,10 +1754,10 @@
slash "^3.0.0"
tiny-glob "^0.2.9"
-"@next/env@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/env/-/env-13.3.1.tgz#589707043065f6b71d411ed9b8f1ffd057c0fd4a"
- integrity sha512-EDtCoedIZC7JlUQ3uaQpSc4aVmyhbLHmQVALg7pFfQgOTjgSnn7mKtA0DiCMkYvvsx6aFb5octGMtWrOtGXW9A==
+"@next/env@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.19.tgz#46905b4e6f62da825b040343cbc233144e9578d3"
+ integrity sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ==
"@next/eslint-plugin-next@12.3.4":
version "12.3.4"
@@ -1766,50 +1766,50 @@
dependencies:
glob "7.1.7"
-"@next/swc-darwin-arm64@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.1.tgz#2c9719dd10a9cdf63bf50a7576b05dcf78999fe8"
- integrity sha512-UXPtriEc/pBP8luSLSCZBcbzPeVv+SSjs9cH/KygTbhmACye8/OOXRZO13Z2Wq1G0gLmEAIHQAOuF+vafPd2lw==
+"@next/swc-darwin-arm64@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz#77ad462b5ced4efdc26cb5a0053968d2c7dac1b6"
+ integrity sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==
-"@next/swc-darwin-x64@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.1.tgz#0be90342c89e53a390ccd9bece15f7f5cd480049"
- integrity sha512-lT36yYxosCfLtplFzJWgo0hrPu6/do8+msgM7oQkPeohDNdhjtjFUgOOwdSnPublLR6Mo2Ym4P/wl5OANuD2bw==
+"@next/swc-darwin-x64@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz#aebe38713a4ce536ee5f2a291673e14b715e633a"
+ integrity sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==
-"@next/swc-linux-arm64-gnu@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.1.tgz#a7353265839f8b8569a346a444dc3ab3770d297e"
- integrity sha512-wRb76nLWJhonH8s3kxC/1tFguEkeOPayIwe9mkaz1G/yeS3OrjeyKMJsb4+Kdg0zbTo53bNCOl59NNtDM7yyyw==
+"@next/swc-linux-arm64-gnu@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz#ec54db65b587939c7b94f9a84800f003a380f5a6"
+ integrity sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==
-"@next/swc-linux-arm64-musl@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.1.tgz#24552e6102c350e372f83f505a1d93c880551a50"
- integrity sha512-qz3BzjJRZ16Iq/jrp+pjiYOc0jTjHlfmxQmZk9x/+5uhRP6/eWQSTAPVJ33BMo6oK5O5N4644OgTAbzXzorecg==
+"@next/swc-linux-arm64-musl@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz#1f5e2c1ea6941e7d530d9f185d5d64be04279d86"
+ integrity sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==
-"@next/swc-linux-x64-gnu@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.1.tgz#5f335a683b6eafa52307b12af97782993b6c45ff"
- integrity sha512-6mgkLmwlyWlomQmpl21I3hxgqE5INoW4owTlcLpNsd1V4wP+J46BlI/5zV5KWWbzjfncIqzXoeGs5Eg+1GHODA==
+"@next/swc-linux-x64-gnu@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz#96b0882492a2f7ffcce747846d3680730f69f4d1"
+ integrity sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==
-"@next/swc-linux-x64-musl@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.1.tgz#58e5aad6f97203a0788783f66324456c8f9cdb50"
- integrity sha512-uqm5sielhQmKJM+qayIhgZv1KlS5pqTdQ99b+Z7hMWryXS96qE0DftTmMZowBcUL6x7s2vSXyH5wPtO1ON7LBg==
+"@next/swc-linux-x64-musl@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz#f276b618afa321d2f7b17c81fc83f429fb0fd9d8"
+ integrity sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==
-"@next/swc-win32-arm64-msvc@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.1.tgz#f8ed1badab57ed4503969758754e6fb0cf326753"
- integrity sha512-WomIiTj/v3LevltlibNQKmvrOymNRYL+a0dp5R73IwPWN5FvXWwSELN/kiNALig/+T3luc4qHNTyvMCp9L6U5Q==
+"@next/swc-win32-arm64-msvc@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz#1599ae0d401da5ffca0947823dac577697cce577"
+ integrity sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==
-"@next/swc-win32-ia32-msvc@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.1.tgz#7f599c8975b09ee5527cc49b9e5a4d13be50635a"
- integrity sha512-M+PoH+0+q658wRUbs285RIaSTYnGBSTdweH/0CdzDgA6Q4rBM0sQs4DHmO3BPP0ltCO/vViIoyG7ks66XmCA5g==
+"@next/swc-win32-ia32-msvc@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz#55cdd7da90818f03e4da16d976f0cb22045d16fd"
+ integrity sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==
-"@next/swc-win32-x64-msvc@13.3.1":
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.1.tgz#192d43ab44ebb98bd4f5865d0e1d7ce62703182f"
- integrity sha512-Sl1F4Vp5Z1rNXWZYqJwMuWRRol4bqOB6+/d7KqkgQ4AcafKPN1PZmpkCoxv4UFHtFNIB7EotnuIhtXu3zScicQ==
+"@next/swc-win32-x64-msvc@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz#648f79c4e09279212ac90d871646ae12d80cdfce"
+ integrity sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -1832,22 +1832,22 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
-"@prisma/client@5.0.0":
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.0.0.tgz#9f0cd4164f4ffddb28bb1811c27eb7fa1e01a087"
- integrity sha512-XlO5ELNAQ7rV4cXIDJUNBEgdLwX3pjtt9Q/RHqDpGf43szpNJx2hJnggfFs7TKNx0cOFsl6KJCSfqr5duEU/bQ==
+"@prisma/client@5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.2.0.tgz#cbfdd440614b38736563a7999f39922fcde0ed50"
+ integrity sha512-AiTjJwR4J5Rh6Z/9ZKrBBLel3/5DzUNntMohOy7yObVnVoTNVFi2kvpLZlFuKO50d7yDspOtW6XBpiAd0BVXbQ==
dependencies:
- "@prisma/engines-version" "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584"
+ "@prisma/engines-version" "5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f"
-"@prisma/engines-version@4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584":
- version "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584"
- resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584.tgz#b36eda5620872d3fac810c302a7e46cf41daa033"
- integrity sha512-HHiUF6NixsldsP3JROq07TYBLEjXFKr6PdH8H4gK/XAoTmIplOJBCgrIUMrsRAnEuGyRoRLXKXWUb943+PFoKQ==
+"@prisma/engines-version@5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f":
+ version "5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f"
+ resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f.tgz#11366e7ff031c908debf4983248d40046016de37"
+ integrity sha512-jsnKT5JIDIE01lAeCj2ghY9IwxkedhKNvxQeoyLs6dr4ZXynetD0vTy7u6wMJt8vVPv8I5DPy/I4CFaoXAgbtg==
-"@prisma/engines@5.0.0":
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.0.0.tgz#5249650eabe77c458c90f2be97d8210353c2e22e"
- integrity sha512-kyT/8fd0OpWmhAU5YnY7eP31brW1q1YrTGoblWrhQJDiN/1K+Z8S1kylcmtjqx5wsUGcP1HBWutayA/jtyt+sg==
+"@prisma/engines@5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.2.0.tgz#e5dff48eb324c8137393933292d44ea5c3bc2ce7"
+ integrity sha512-dT7FOLUCdZmq+AunLqB1Iz+ZH/IIS1Fz2THmKZQ6aFONrQD/BQ5ecJ7g2wGS2OgyUFf4OaLam6/bxmgdOBDqig==
"@react-spring/animated@~9.7.3":
version "9.7.3"
@@ -1969,19 +1969,19 @@
dependencies:
slash "^4.0.0"
-"@rollup/plugin-buble@^0.21.3":
- version "0.21.3"
- resolved "https://registry.npmjs.org/@rollup/plugin-buble/-/plugin-buble-0.21.3.tgz"
- integrity sha512-Iv8cCuFPnMdqV4pcyU+OrfjOfagPArRQ1PyQjx5KgHk3dARedI+8PNTLSMpJts0lQJr8yF2pAU4GxpxCBJ9HYw==
+"@rollup/plugin-buble@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-buble/-/plugin-buble-1.0.2.tgz#30af390341b0888490f781fcf17e469198d118a2"
+ integrity sha512-Hz9+AigRWwS93vmorrVrhyG9SdSCZAkBDx614w09iFQYFUAP2HmdUrQyZsb1WO2n+iDvPFznrTE16la+eGNcEQ==
dependencies:
- "@rollup/pluginutils" "^3.0.8"
+ "@rollup/pluginutils" "^5.0.1"
"@types/buble" "^0.19.2"
buble "^0.20.0"
-"@rollup/plugin-commonjs@^24.1.0":
- version "24.1.0"
- resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz"
- integrity sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==
+"@rollup/plugin-commonjs@^25.0.4":
+ version "25.0.4"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.4.tgz#a7547a0c4ec3fa79818eb313e1de0023e548f4e6"
+ integrity sha512-L92Vz9WUZXDnlQQl3EwbypJR4+DM2EbsO+/KOcEkP4Mc6Ct453EeDB2uH9lgRwj4w5yflgNpq9pHOiY8aoUXBQ==
dependencies:
"@rollup/pluginutils" "^5.0.1"
commondir "^1.0.1"
@@ -1992,15 +1992,15 @@
"@rollup/plugin-json@^6.0.0":
version "6.0.0"
- resolved "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.0.0.tgz#199fea6670fd4dfb1f4932250569b14719db234a"
integrity sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==
dependencies:
"@rollup/pluginutils" "^5.0.1"
-"@rollup/plugin-node-resolve@^15.0.2":
- version "15.1.0"
- resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz"
- integrity sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==
+"@rollup/plugin-node-resolve@^15.2.0":
+ version "15.2.0"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.0.tgz#982053b237f81471aace570472e88a456d211621"
+ integrity sha512-mKur03xNGT8O9ODO6FtT43ITGqHWZbKPdVJHZb+iV9QYcdlhUUB0wgknvA4KCUmC5oHJF6O2W1EgmyOQyVUI4Q==
dependencies:
"@rollup/pluginutils" "^5.0.1"
"@types/resolve" "1.20.2"
@@ -2009,22 +2009,13 @@
is-module "^1.0.0"
resolve "^1.22.1"
-"@rollup/plugin-replace@^4.0.0":
- version "4.0.0"
- resolved "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-4.0.0.tgz"
- integrity sha512-+rumQFiaNac9y64OHtkHGmdjm7us9bo1PlbgQfdihQtuNxzjpaB064HbRnewUOggLQxVCCyINfStkgmBeQpv1g==
+"@rollup/plugin-replace@^5.0.2":
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz#45f53501b16311feded2485e98419acb8448c61d"
+ integrity sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==
dependencies:
- "@rollup/pluginutils" "^3.1.0"
- magic-string "^0.25.7"
-
-"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
- version "3.1.0"
- resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz"
- integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
- dependencies:
- "@types/estree" "0.0.39"
- estree-walker "^1.0.1"
- picomatch "^2.2.2"
+ "@rollup/pluginutils" "^5.0.1"
+ magic-string "^0.27.0"
"@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.0.2":
version "5.0.2"
@@ -2040,75 +2031,99 @@
resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz"
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
+"@svgr/babel-plugin-add-jsx-attribute@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22"
+ integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==
+
"@svgr/babel-plugin-add-jsx-attribute@^6.5.1":
version "6.5.1"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz"
integrity sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==
-"@svgr/babel-plugin-add-jsx-attribute@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz"
- integrity sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==
-
-"@svgr/babel-plugin-remove-jsx-attribute@*", "@svgr/babel-plugin-remove-jsx-attribute@^7.0.0":
+"@svgr/babel-plugin-remove-jsx-attribute@*":
version "7.0.0"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz"
integrity sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==
-"@svgr/babel-plugin-remove-jsx-empty-expression@*", "@svgr/babel-plugin-remove-jsx-empty-expression@^7.0.0":
+"@svgr/babel-plugin-remove-jsx-attribute@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186"
+ integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==
+
+"@svgr/babel-plugin-remove-jsx-empty-expression@*":
version "7.0.0"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz"
integrity sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==
+"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44"
+ integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==
+
+"@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27"
+ integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==
+
"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1":
version "6.5.1"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz"
integrity sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==
-"@svgr/babel-plugin-replace-jsx-attribute-value@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-7.0.0.tgz"
- integrity sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==
+"@svgr/babel-plugin-svg-dynamic-title@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0"
+ integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==
"@svgr/babel-plugin-svg-dynamic-title@^6.5.1":
version "6.5.1"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz"
integrity sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==
-"@svgr/babel-plugin-svg-dynamic-title@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-7.0.0.tgz"
- integrity sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==
+"@svgr/babel-plugin-svg-em-dimensions@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501"
+ integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==
"@svgr/babel-plugin-svg-em-dimensions@^6.5.1":
version "6.5.1"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz"
integrity sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==
-"@svgr/babel-plugin-svg-em-dimensions@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-7.0.0.tgz"
- integrity sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==
+"@svgr/babel-plugin-transform-react-native-svg@8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754"
+ integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==
"@svgr/babel-plugin-transform-react-native-svg@^6.5.1":
version "6.5.1"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz"
integrity sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==
-"@svgr/babel-plugin-transform-react-native-svg@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-7.0.0.tgz"
- integrity sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==
+"@svgr/babel-plugin-transform-svg-component@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e"
+ integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==
"@svgr/babel-plugin-transform-svg-component@^6.5.1":
version "6.5.1"
resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz"
integrity sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==
-"@svgr/babel-plugin-transform-svg-component@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-7.0.0.tgz"
- integrity sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==
+"@svgr/babel-preset@8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece"
+ integrity sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==
+ dependencies:
+ "@svgr/babel-plugin-add-jsx-attribute" "8.0.0"
+ "@svgr/babel-plugin-remove-jsx-attribute" "8.0.0"
+ "@svgr/babel-plugin-remove-jsx-empty-expression" "8.0.0"
+ "@svgr/babel-plugin-replace-jsx-attribute-value" "8.0.0"
+ "@svgr/babel-plugin-svg-dynamic-title" "8.0.0"
+ "@svgr/babel-plugin-svg-em-dimensions" "8.0.0"
+ "@svgr/babel-plugin-transform-react-native-svg" "8.1.0"
+ "@svgr/babel-plugin-transform-svg-component" "8.0.0"
"@svgr/babel-preset@^6.5.1":
version "6.5.1"
@@ -2124,19 +2139,16 @@
"@svgr/babel-plugin-transform-react-native-svg" "^6.5.1"
"@svgr/babel-plugin-transform-svg-component" "^6.5.1"
-"@svgr/babel-preset@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-7.0.0.tgz"
- integrity sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==
+"@svgr/core@8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88"
+ integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==
dependencies:
- "@svgr/babel-plugin-add-jsx-attribute" "^7.0.0"
- "@svgr/babel-plugin-remove-jsx-attribute" "^7.0.0"
- "@svgr/babel-plugin-remove-jsx-empty-expression" "^7.0.0"
- "@svgr/babel-plugin-replace-jsx-attribute-value" "^7.0.0"
- "@svgr/babel-plugin-svg-dynamic-title" "^7.0.0"
- "@svgr/babel-plugin-svg-em-dimensions" "^7.0.0"
- "@svgr/babel-plugin-transform-react-native-svg" "^7.0.0"
- "@svgr/babel-plugin-transform-svg-component" "^7.0.0"
+ "@babel/core" "^7.21.3"
+ "@svgr/babel-preset" "8.1.0"
+ camelcase "^6.2.0"
+ cosmiconfig "^8.1.3"
+ snake-case "^3.0.4"
"@svgr/core@^6.5.1":
version "6.5.1"
@@ -2149,15 +2161,13 @@
camelcase "^6.2.0"
cosmiconfig "^7.0.1"
-"@svgr/core@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/core/-/core-7.0.0.tgz"
- integrity sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==
+"@svgr/hast-util-to-babel-ast@8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4"
+ integrity sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==
dependencies:
- "@babel/core" "^7.21.3"
- "@svgr/babel-preset" "^7.0.0"
- camelcase "^6.2.0"
- cosmiconfig "^8.1.3"
+ "@babel/types" "^7.21.3"
+ entities "^4.4.0"
"@svgr/hast-util-to-babel-ast@^6.5.1":
version "6.5.1"
@@ -2167,13 +2177,15 @@
"@babel/types" "^7.20.0"
entities "^4.4.0"
-"@svgr/hast-util-to-babel-ast@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-7.0.0.tgz"
- integrity sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==
+"@svgr/plugin-jsx@8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928"
+ integrity sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==
dependencies:
- "@babel/types" "^7.21.3"
- entities "^4.4.0"
+ "@babel/core" "^7.21.3"
+ "@svgr/babel-preset" "8.1.0"
+ "@svgr/hast-util-to-babel-ast" "8.0.0"
+ svg-parser "^2.0.4"
"@svgr/plugin-jsx@^6.5.1":
version "6.5.1"
@@ -2185,15 +2197,14 @@
"@svgr/hast-util-to-babel-ast" "^6.5.1"
svg-parser "^2.0.4"
-"@svgr/plugin-jsx@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-7.0.0.tgz"
- integrity sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==
+"@svgr/plugin-svgo@8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz#b115b7b967b564f89ac58feae89b88c3decd0f00"
+ integrity sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==
dependencies:
- "@babel/core" "^7.21.3"
- "@svgr/babel-preset" "^7.0.0"
- "@svgr/hast-util-to-babel-ast" "^7.0.0"
- svg-parser "^2.0.4"
+ cosmiconfig "^8.1.3"
+ deepmerge "^4.3.1"
+ svgo "^3.0.2"
"@svgr/plugin-svgo@^6.5.1":
version "6.5.1"
@@ -2204,19 +2215,10 @@
deepmerge "^4.2.2"
svgo "^2.8.0"
-"@svgr/plugin-svgo@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-7.0.0.tgz"
- integrity sha512-263znzlu3qTKj71/ot5G9l2vpL4CW+pr2IexBFIwwB+fRAXE9Xnw2rUFgE6P4+37N9siOuC4lKkgBfUCOLFRKQ==
- dependencies:
- cosmiconfig "^8.1.3"
- deepmerge "^4.3.1"
- svgo "^3.0.2"
-
-"@svgr/rollup@^7.0.0":
- version "7.0.0"
- resolved "https://registry.npmjs.org/@svgr/rollup/-/rollup-7.0.0.tgz"
- integrity sha512-zlx0lxtxTnrXFF+ISuff+hht2XcWXa6uXEliwQbz+o0/qRIrcqyB9ShalO9ekVWB5icgxCWQ5lDaULJTt/pTlA==
+"@svgr/rollup@^8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/rollup/-/rollup-8.1.0.tgz#2c8e09655336cda4b7843799a5d2a5860300b030"
+ integrity sha512-0XR1poYvPQoPpmfDYLEqUGu5ePAQ4pdgN3VFsZBNAeze7qubVpsIY1o1R6PZpKep/DKu33GSm2NhwpCLkMs2Cw==
dependencies:
"@babel/core" "^7.21.3"
"@babel/plugin-transform-react-constant-elements" "^7.21.3"
@@ -2224,9 +2226,9 @@
"@babel/preset-react" "^7.18.6"
"@babel/preset-typescript" "^7.21.0"
"@rollup/pluginutils" "^5.0.2"
- "@svgr/core" "^7.0.0"
- "@svgr/plugin-jsx" "^7.0.0"
- "@svgr/plugin-svgo" "^7.0.0"
+ "@svgr/core" "8.1.0"
+ "@svgr/plugin-jsx" "8.1.0"
+ "@svgr/plugin-svgo" "8.1.0"
"@svgr/webpack@^6.2.1":
version "6.5.1"
@@ -2242,24 +2244,24 @@
"@svgr/plugin-jsx" "^6.5.1"
"@svgr/plugin-svgo" "^6.5.1"
-"@swc/helpers@0.5.0":
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.0.tgz#bf1d807b60f7290d0ec763feea7ccdeda06e85f1"
- integrity sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==
+"@swc/helpers@0.5.1":
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a"
+ integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==
dependencies:
tslib "^2.4.0"
-"@tanstack/query-core@4.32.0":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.32.0.tgz#e0f4a830283612430450c13badd353766423f523"
- integrity sha512-ei4IYwL2kmlKSlCw9WgvV7PpXi0MiswVwfQRxawhJA690zWO3dU49igaQ/UMTl+Jy9jj9dK5IKAYvbX7kUvviQ==
+"@tanstack/query-core@4.33.0":
+ version "4.33.0"
+ resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.33.0.tgz#7756da9a75a424e521622b1d84eb55b7a2b33715"
+ integrity sha512-qYu73ptvnzRh6se2nyBIDHGBQvPY1XXl3yR769B7B6mIDD7s+EZhdlWHQ67JI6UOTFRaI7wupnTnwJ3gE0Mr/g==
-"@tanstack/react-query@^4.16.1":
- version "4.32.0"
- resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.32.0.tgz#701b45b149cfd4b54a68705f9100973db3ba5d5d"
- integrity sha512-B8WUMcByYAH9500ENejDCATOmEZhqjtS9wsfiQ3BNa+s+yAynY8SESI8WWHhSqUmjd0pmCSFRP6BOUGSda3QXA==
+"@tanstack/react-query@^4.33.0":
+ version "4.33.0"
+ resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.33.0.tgz#e927b0343a6ecaa948fee59e9ca98fe561062638"
+ integrity sha512-97nGbmDK0/m0B86BdiXzx3EW9RcDYKpnyL2+WwyuLHEgpfThYAnXFaMMmnTDuAO4bQJXEhflumIEUfKmP7ESGA==
dependencies:
- "@tanstack/query-core" "4.32.0"
+ "@tanstack/query-core" "4.33.0"
use-sync-external-store "^1.2.0"
"@trysound/sax@0.2.0":
@@ -2332,16 +2334,18 @@
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz"
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
-"@types/estree@0.0.39":
- version "0.0.39"
- resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz"
- integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
-
"@types/estree@^0.0.50":
version "0.0.50"
resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz"
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
+"@types/fs-extra@^8.0.1":
+ version "8.1.2"
+ resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.2.tgz#7125cc2e4bdd9bd2fc83005ffdb1d0ba00cca61f"
+ integrity sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==
+ dependencies:
+ "@types/node" "*"
+
"@types/fs-extra@^9.0.1":
version "9.0.13"
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz"
@@ -2410,6 +2414,11 @@
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.38.tgz"
integrity sha512-5jY9RhV7c0Z4Jy09G+NIDTsCZ5G0L5n+Z+p+Y7t5VJHM30bgwzSjVtlcBxqAj+6L/swIlvtOSzr8rBk/aNyV2g==
+"@types/node@^18.11.9":
+ version "18.17.6"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.6.tgz#0296e9a30b22d2a8fcaa48d3c45afe51474ca55b"
+ integrity sha512-fGmT/P7z7ecA6bv/ia5DlaWCH4YeZvAQMNpUhrJjtAhOhZfoxS1VLUgU2pdk63efSjQaOJWdXMuAJsws+8I6dg==
+
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@@ -2425,6 +2434,13 @@
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+"@types/react-dom@^18.0.8":
+ version "18.2.7"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63"
+ integrity sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==
+ dependencies:
+ "@types/react" "*"
+
"@types/react-redux@^7.1.20":
version "7.1.24"
resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz"
@@ -2444,6 +2460,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^18.0.25":
+ version "18.2.20"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.20.tgz#1605557a83df5c8a2cc4eeb743b3dfc0eb6aaeb2"
+ integrity sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/resolve@1.20.2":
version "1.20.2"
resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz"
@@ -3432,7 +3457,7 @@ colord@^2.9.1, colord@^2.9.2, colord@^2.9.3:
resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz"
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
-colorette@^1.4.0:
+colorette@^1.1.0, colorette@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz"
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
@@ -4097,6 +4122,14 @@ domutils@^3.0.1:
domelementtype "^2.3.0"
domhandler "^5.0.1"
+dot-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
+ integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
+ dependencies:
+ no-case "^3.0.4"
+ tslib "^2.0.3"
+
dotenv@^10.0.0:
version "10.0.0"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz"
@@ -4560,11 +4593,6 @@ estree-walker@^0.6.1:
resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz"
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
-estree-walker@^1.0.1:
- version "1.0.1"
- resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz"
- integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
-
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
@@ -4822,6 +4850,15 @@ fs-extra@^11.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
+fs-extra@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+ integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
fs-extra@^9.0.0:
version "9.1.0"
resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz"
@@ -4945,6 +4982,11 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
glob@7.1.7:
version "7.1.7"
resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz"
@@ -5020,6 +5062,20 @@ globalyzer@0.1.0:
resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465"
integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==
+globby@10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22"
+ integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==
+ dependencies:
+ "@types/glob" "^7.1.1"
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.0.3"
+ glob "^7.1.3"
+ ignore "^5.1.1"
+ merge2 "^1.2.3"
+ slash "^3.0.0"
+
globby@^10.0.1:
version "10.0.2"
resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz"
@@ -5532,6 +5588,11 @@ is-plain-obj@^2.0.0:
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+is-plain-object@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
+ integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
+
is-plain-object@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
@@ -5752,6 +5813,13 @@ jsonc-parser@^3.2.0:
resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@@ -6041,6 +6109,13 @@ loud-rejection@^2.2.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.2"
+lower-case@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
+ integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
+ dependencies:
+ tslib "^2.0.3"
+
lru-cache@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61"
@@ -6074,12 +6149,12 @@ magic-string@^0.27.0:
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13"
-magic-string@^0.30.0:
- version "0.30.0"
- resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz"
- integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==
+magic-string@^0.30.2:
+ version "0.30.2"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.2.tgz#dcf04aad3d0d1314bc743d076c50feb29b3c7aca"
+ integrity sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==
dependencies:
- "@jridgewell/sourcemap-codec" "^1.4.13"
+ "@jridgewell/sourcemap-codec" "^1.4.15"
make-dir@^3.0.0:
version "3.1.0"
@@ -6380,33 +6455,43 @@ next-basics@^0.36.0:
jsonwebtoken "^9.0.0"
pure-rand "^6.0.2"
-next@13.3.1:
- version "13.3.1"
- resolved "https://registry.yarnpkg.com/next/-/next-13.3.1.tgz#17625f7423db2e059d71b41bd9031756cf2b33bc"
- integrity sha512-eByWRxPzKHs2oQz1yE41LX35umhz86ZSZ+mYyXBqn2IBi2hyUqxBA88avywdr4uyH+hCJczegGsDGWbzQA5Rqw==
+next@13.4.19:
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/next/-/next-13.4.19.tgz#2326e02aeedee2c693d4f37b90e4f0ed6882b35f"
+ integrity sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw==
dependencies:
- "@next/env" "13.3.1"
- "@swc/helpers" "0.5.0"
+ "@next/env" "13.4.19"
+ "@swc/helpers" "0.5.1"
busboy "1.6.0"
caniuse-lite "^1.0.30001406"
postcss "8.4.14"
styled-jsx "5.1.1"
+ watchpack "2.4.0"
+ zod "3.21.4"
optionalDependencies:
- "@next/swc-darwin-arm64" "13.3.1"
- "@next/swc-darwin-x64" "13.3.1"
- "@next/swc-linux-arm64-gnu" "13.3.1"
- "@next/swc-linux-arm64-musl" "13.3.1"
- "@next/swc-linux-x64-gnu" "13.3.1"
- "@next/swc-linux-x64-musl" "13.3.1"
- "@next/swc-win32-arm64-msvc" "13.3.1"
- "@next/swc-win32-ia32-msvc" "13.3.1"
- "@next/swc-win32-x64-msvc" "13.3.1"
+ "@next/swc-darwin-arm64" "13.4.19"
+ "@next/swc-darwin-x64" "13.4.19"
+ "@next/swc-linux-arm64-gnu" "13.4.19"
+ "@next/swc-linux-arm64-musl" "13.4.19"
+ "@next/swc-linux-x64-gnu" "13.4.19"
+ "@next/swc-linux-x64-musl" "13.4.19"
+ "@next/swc-win32-arm64-msvc" "13.4.19"
+ "@next/swc-win32-ia32-msvc" "13.4.19"
+ "@next/swc-win32-x64-msvc" "13.4.19"
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+no-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
+ integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
+ dependencies:
+ lower-case "^2.0.2"
+ tslib "^2.0.3"
+
node-abi@^3.3.0:
version "3.45.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.45.0.tgz#f568f163a3bfca5aacfce1fbeee1fa2cc98441f5"
@@ -6788,7 +6873,7 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -7438,12 +7523,12 @@ pretty-bytes@^5.6.0:
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
-prisma@5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.0.0.tgz#f6571c46dc2478172cb7bc1bb62d74026a2c2630"
- integrity sha512-KYWk83Fhi1FH59jSpavAYTt2eoMVW9YKgu8ci0kuUnt6Dup5Qy47pcB4/TLmiPAbhGrxxSz7gsSnJcCmkyPANA==
+prisma@5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.2.0.tgz#a302dc2635cdec1d22d552ece837fb29a03563b9"
+ integrity sha512-FfFlpjVCkZwrqxDnP4smlNYSH1so+CbfjgdpioFzGGqlQAEm6VHAYSzV7jJgC3ebtY9dNOhDMS2+4/1DDSM7bQ==
dependencies:
- "@prisma/engines" "5.0.0"
+ "@prisma/engines" "5.2.0"
promise.series@^0.2.0:
version "0.2.0"
@@ -8023,6 +8108,17 @@ rimraf@^3.0.0, rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
+rollup-plugin-copy@^3.4.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286"
+ integrity sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==
+ dependencies:
+ "@types/fs-extra" "^8.0.1"
+ colorette "^1.1.0"
+ fs-extra "^8.1.0"
+ globby "10.0.1"
+ is-plain-object "^3.0.0"
+
rollup-plugin-delete@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/rollup-plugin-delete/-/rollup-plugin-delete-2.0.0.tgz"
@@ -8030,14 +8126,14 @@ rollup-plugin-delete@^2.0.0:
dependencies:
del "^5.1.0"
-rollup-plugin-dts@^5.3.0:
- version "5.3.0"
- resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-5.3.0.tgz"
- integrity sha512-8FXp0ZkyZj1iU5klkIJYLjIq/YZSwBoERu33QBDxm/1yw5UU4txrEtcmMkrq+ZiKu3Q4qvPCNqc3ovX6rjqzbQ==
+rollup-plugin-dts@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-5.3.1.tgz#c2841269a3a5cb986b7791b0328e6a178eba108f"
+ integrity sha512-gusMi+Z4gY/JaEQeXnB0RUdU82h1kF0WYzCWgVmV4p3hWXqelaKuCvcJawfeg+EKn2T1Ie+YWF2OiN1/L8bTVg==
dependencies:
- magic-string "^0.30.0"
+ magic-string "^0.30.2"
optionalDependencies:
- "@babel/code-frame" "^7.18.6"
+ "@babel/code-frame" "^7.22.5"
rollup-plugin-esbuild@^5.0.0:
version "5.0.0"
@@ -8050,10 +8146,10 @@ rollup-plugin-esbuild@^5.0.0:
joycon "^3.1.1"
jsonc-parser "^3.2.0"
-rollup-plugin-node-externals@^5.1.2:
- version "5.1.3"
- resolved "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-5.1.3.tgz"
- integrity sha512-Q3VMjsn39r0/mjKrX++rFlC7kwL7YZdScdyU7BEo+PrEremal3mnol/1X+wQUU++7NeqC1ZNAeRYnHGtsTu9GQ==
+rollup-plugin-node-externals@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-node-externals/-/rollup-plugin-node-externals-6.1.1.tgz#dff1a85073fe3c0b2c423b280259fe80392026a8"
+ integrity sha512-127OFMkpH5rBVlRHRBDUMk1m1sGuzbGy7so5aj/IkpUb2r3+wOWjR/erUzd2ChEQWPsxsyQG6xpYYvPBAdcBRA==
rollup-plugin-postcss@^4.0.2:
version "4.0.2"
@@ -8091,10 +8187,10 @@ rollup-pluginutils@^2.8.2:
dependencies:
estree-walker "^0.6.1"
-rollup@^2.70.1:
- version "2.79.1"
- resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz"
- integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
+rollup@^3.28.0:
+ version "3.28.0"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.28.0.tgz#a3c70004b01934760c0cb8df717c7a1d932389a2"
+ integrity sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==
optionalDependencies:
fsevents "~2.3.2"
@@ -8339,6 +8435,14 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
+snake-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
+ integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
+ dependencies:
+ dot-case "^3.0.4"
+ tslib "^2.0.3"
+
sort-keys@^4.0.0:
version "4.2.0"
resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz"
@@ -8940,6 +9044,11 @@ tslib@^2.0.1, tslib@^2.1.0:
resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+tslib@^2.0.3:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+ integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
tslib@^2.4.0:
version "2.5.0"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz"
@@ -9052,11 +9161,16 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
-typescript@^4.0, typescript@^4.5, typescript@^4.9.5:
+typescript@^4.0, typescript@^4.5:
version "4.9.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
+typescript@^5.1.6:
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
+ integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
+
ufo@^1.0.0, ufo@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76"
@@ -9128,6 +9242,11 @@ unicode-property-aliases-ecmascript@^2.0.0:
resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz"
integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
+universalify@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
@@ -9228,6 +9347,14 @@ vue@^3.2.23:
"@vue/server-renderer" "3.2.36"
"@vue/shared" "3.2.36"
+watchpack@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
web-streams-polyfill@^3.0.3:
version "3.2.1"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
@@ -9394,6 +9521,11 @@ yup@^0.32.11:
property-expr "^2.0.4"
toposort "^2.0.2"
+zod@3.21.4:
+ version "3.21.4"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
+ integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==
+
zustand@^4.3.8:
version "4.3.9"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.9.tgz#a7d4332bbd75dfd25c6848180b3df1407217f2ad"