diff --git a/next-env.d.ts b/next-env.d.ts
index 4f11a03d..fd36f949 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -1,5 +1,6 @@
///
///
+///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/next.config.js b/next.config.js
index cc3cde7c..cf7dce7f 100644
--- a/next.config.js
+++ b/next.config.js
@@ -6,7 +6,7 @@ const pkg = require('./package.json');
const contentSecurityPolicy = `
default-src 'self';
img-src *;
- script-src 'self' 'unsafe-eval';
+ script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' api.umami.is;
frame-ancestors 'self' ${process.env.ALLOWED_FRAME_URLS};
@@ -74,16 +74,23 @@ if (process.env.CLOUD_MODE && process.env.CLOUD_URL && process.env.DISABLE_LOGIN
});
}
+const basePath = process.env.BASE_PATH;
+
+/** @type {import('next').NextConfig} */
const config = {
+ reactStrictMode: false,
env: {
- cloudMode: process.env.CLOUD_MODE,
+ basePath: basePath || '',
+ cloudMode: !!process.env.CLOUD_MODE,
cloudUrl: process.env.CLOUD_URL,
configUrl: '/config',
currentVersion: pkg.version,
defaultLocale: process.env.DEFAULT_LOCALE,
+ disableLogin: process.env.DISABLE_LOGIN,
+ disableUI: process.env.DISABLE_UI,
isProduction: process.env.NODE_ENV === 'production',
},
- basePath: process.env.BASE_PATH,
+ basePath,
output: 'standalone',
eslint: {
ignoreDuringBuilds: true,
@@ -92,11 +99,23 @@ const config = {
ignoreBuildErrors: true,
},
webpack(config) {
- config.module.rules.push({
- test: /\.svg$/,
- issuer: /\.{js|jsx|ts|tsx}$/,
- use: ['@svgr/webpack'],
- });
+ const fileLoaderRule = config.module.rules.find(rule => rule.test?.test?.('.svg'));
+
+ config.module.rules.push(
+ {
+ ...fileLoaderRule,
+ test: /\.svg$/i,
+ resourceQuery: /url/,
+ },
+ {
+ test: /\.svg$/i,
+ issuer: fileLoaderRule.issuer,
+ resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] },
+ use: ['@svgr/webpack'],
+ },
+ );
+
+ fileLoaderRule.exclude = /\.svg$/i;
config.resolve.alias['public'] = path.resolve('./public');
diff --git a/package.json b/package.json
index 0a69e5e3..f2a5d9b9 100644
--- a/package.json
+++ b/package.json
@@ -61,16 +61,17 @@
".next/cache"
],
"dependencies": {
+ "@clickhouse/client": "^0.2.2",
"@fontsource/inter": "^4.5.15",
"@prisma/client": "5.3.1",
+ "@react-spring/web": "^9.7.3",
"@tanstack/react-query": "^4.33.0",
- "@umami/prisma-client": "^0.2.0",
+ "@umami/prisma-client": "^0.3.0",
"@umami/redis-client": "^0.15.0",
"chalk": "^4.1.1",
"chart.js": "^4.2.1",
"chartjs-adapter-date-fns": "^3.0.0",
"classnames": "^2.3.1",
- "clickhouse": "^2.5.0",
"colord": "^2.9.2",
"cors": "^2.8.5",
"cross-spawn": "^7.0.3",
@@ -91,18 +92,17 @@
"kafkajs": "^2.1.0",
"maxmind": "^4.3.6",
"moment-timezone": "^0.5.35",
- "next": "13.5.2",
+ "next": "13.5.3",
"next-basics": "^0.36.0",
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
"react": "^18.2.0",
- "react-basics": "^0.100.0",
+ "react-basics": "^0.102.0",
"react-beautiful-dnd": "^13.1.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.4",
- "react-intl": "^5.24.7",
+ "react-intl": "^6.4.7",
"react-simple-maps": "^2.3.0",
- "react-spring": "^9.4.4",
"react-use-measure": "^2.0.4",
"react-window": "^1.8.6",
"request-ip": "^3.3.0",
@@ -123,12 +123,12 @@
"@rollup/plugin-node-resolve": "^15.2.0",
"@rollup/plugin-replace": "^5.0.2",
"@svgr/rollup": "^8.1.0",
- "@svgr/webpack": "^6.2.1",
+ "@svgr/webpack": "^8.1.0",
"@types/node": "^18.11.9",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
- "@typescript-eslint/eslint-plugin": "^5.50.0",
- "@typescript-eslint/parser": "^5.50.0",
+ "@typescript-eslint/eslint-plugin": "^6.7.3",
+ "@typescript-eslint/parser": "^6.7.3",
"cross-env": "^7.0.3",
"esbuild": "^0.17.17",
"eslint": "^8.33.0",
@@ -138,8 +138,8 @@
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"extract-react-intl-messages": "^4.1.1",
- "husky": "^7.0.0",
- "lint-staged": "^11.0.0",
+ "husky": "^8.0.3",
+ "lint-staged": "^14.0.1",
"postcss": "^8.4.31",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^15.1.0",
diff --git a/public/images/os/windows-mobile.png b/public/images/os/windows-mobile.png
new file mode 100644
index 00000000..4a899a30
Binary files /dev/null and b/public/images/os/windows-mobile.png differ
diff --git a/rollup.components.config.mjs b/rollup.components.config.mjs
index c4481d0e..9be07390 100644
--- a/rollup.components.config.mjs
+++ b/rollup.components.config.mjs
@@ -19,6 +19,7 @@ const customResolver = resolve({
const aliasConfig = {
entries: [
+ { find: /^app/, replacement: path.resolve('./src/app') },
{ find: /^components/, replacement: path.resolve('./src/components') },
{ find: /^hooks/, replacement: path.resolve('./src/hooks') },
{ find: /^lib/, replacement: path.resolve('./src/lib') },
diff --git a/src/app/(main)/NavBar.js b/src/app/(main)/NavBar.js
new file mode 100644
index 00000000..211adf5f
--- /dev/null
+++ b/src/app/(main)/NavBar.js
@@ -0,0 +1,58 @@
+'use client';
+import { Icon, Text } from 'react-basics';
+import Link from 'next/link';
+import classNames from 'classnames';
+import Icons from 'components/icons';
+import ThemeButton from 'components/input/ThemeButton';
+import LanguageButton from 'components/input/LanguageButton';
+import ProfileButton from 'components/input/ProfileButton';
+import useMessages from 'components/hooks/useMessages';
+import HamburgerButton from 'components/common/HamburgerButton';
+import { usePathname } from 'next/navigation';
+import styles from './NavBar.module.css';
+
+export function NavBar() {
+ const pathname = usePathname();
+ const { formatMessage, labels } = useMessages();
+
+ const links = [
+ { label: formatMessage(labels.dashboard), url: '/dashboard' },
+ { label: formatMessage(labels.websites), url: '/websites' },
+ { label: formatMessage(labels.reports), url: '/reports' },
+ { label: formatMessage(labels.settings), url: '/settings' },
+ ].filter(n => n);
+
+ return (
+
+
+
+
+
+ umami
+
+
+ {links.map(({ url, label }) => {
+ return (
+
+ {label}
+
+ );
+ })}
+
+
+
+
+
+
+ );
+}
+
+export default NavBar;
diff --git a/src/components/layout/NavBar.module.css b/src/app/(main)/NavBar.module.css
similarity index 75%
rename from src/components/layout/NavBar.module.css
rename to src/app/(main)/NavBar.module.css
index dd5085a0..fd022eca 100644
--- a/src/components/layout/NavBar.module.css
+++ b/src/app/(main)/NavBar.module.css
@@ -1,7 +1,7 @@
.navbar {
+ display: grid;
+ grid-template-columns: max-content 1fr 1fr;
position: relative;
- display: flex;
- flex-direction: row;
align-items: center;
height: 60px;
background: var(--base75);
@@ -9,17 +9,6 @@
padding: 0 20px;
}
-.left,
-.right {
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.right {
- justify-content: flex-end;
-}
-
.logo {
display: flex;
flex-direction: row;
@@ -35,29 +24,24 @@
flex-direction: row;
gap: 30px;
padding: 0 40px;
- flex: 1;
font-weight: 700;
+ max-height: 60px;
}
-.links a {
- display: flex;
- align-items: center;
- gap: 10px;
- line-height: 60px;
+.links a,
+.links a:active,
+.links a:visited {
color: var(--font-color200);
+ line-height: 60px;
border-bottom: 2px solid transparent;
}
-.links span {
- white-space: nowrap;
-}
-
.links a:hover {
color: var(--font-color100);
border-bottom: 2px solid var(--primary400);
}
-.links .selected {
+.links a.selected {
color: var(--font-color100);
border-bottom: 2px solid var(--primary400);
}
@@ -68,7 +52,6 @@
flex-direction: row;
align-items: center;
justify-content: flex-end;
- min-width: 0;
}
.mobile {
@@ -76,6 +59,10 @@
}
@media only screen and (max-width: 768px) {
+ .navbar {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
.links,
.actions {
display: none;
diff --git a/src/app/(main)/Shell.tsx b/src/app/(main)/Shell.tsx
new file mode 100644
index 00000000..980abb62
--- /dev/null
+++ b/src/app/(main)/Shell.tsx
@@ -0,0 +1,27 @@
+'use client';
+import Script from 'next/script';
+import { usePathname } from 'next/navigation';
+import UpdateNotice from 'components/common/UpdateNotice';
+import { useRequireLogin, useConfig } from 'components/hooks';
+
+export function Shell({ children }) {
+ const { user } = useRequireLogin();
+ const config = useConfig();
+ const pathname = usePathname();
+
+ if (!user || !config) {
+ return null;
+ }
+
+ return (
+ <>
+ {children}
+
+ {process.env.NODE_ENV === 'production' && !pathname.includes('/share/') && (
+
+ )}
+ >
+ );
+}
+
+export default Shell;
diff --git a/src/components/pages/console/TestConsole.js b/src/app/(main)/console/TestConsole.js
similarity index 88%
rename from src/components/pages/console/TestConsole.js
rename to src/app/(main)/console/TestConsole.js
index 71eb27b4..1ae3eaf1 100644
--- a/src/components/pages/console/TestConsole.js
+++ b/src/app/(main)/console/TestConsole.js
@@ -1,14 +1,15 @@
+'use client';
import WebsiteSelect from 'components/input/WebsiteSelect';
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 WebsiteChart from '../../(main)/websites/[id]/WebsiteChart';
import useApi from 'components/hooks/useApi';
import Head from 'next/head';
import Link from 'next/link';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import Script from 'next/script';
-import { Button, Column, Row } from 'react-basics';
+import { Button } from 'react-basics';
import styles from './TestConsole.module.css';
export function TestConsole() {
@@ -90,8 +91,8 @@ export function TestConsole() {
src={`${basePath}/script.js`}
data-cache="true"
/>
-
-
+
+
Page links
page one
@@ -114,8 +115,8 @@ export function TestConsole() {
external link (tab)
-
-
+
+
Click events
Send event
@@ -130,8 +131,8 @@ export function TestConsole() {
>
Send event with data
-
-
+
+
Javascript events
Run script
@@ -140,14 +141,12 @@ export function TestConsole() {
Run identify
-
-
-
-
-
-
-
-
+
+
+
+
+
+
>
)}
diff --git a/src/components/pages/console/TestConsole.module.css b/src/app/(main)/console/TestConsole.module.css
similarity index 100%
rename from src/components/pages/console/TestConsole.module.css
rename to src/app/(main)/console/TestConsole.module.css
diff --git a/src/app/(main)/console/[[...id]]/page.tsx b/src/app/(main)/console/[[...id]]/page.tsx
new file mode 100644
index 00000000..d020ddf9
--- /dev/null
+++ b/src/app/(main)/console/[[...id]]/page.tsx
@@ -0,0 +1,20 @@
+import TestConsole from '../TestConsole';
+import { Metadata } from 'next';
+
+async function getEnabled() {
+ return !!process.env.ENABLE_TEST_CONSOLE;
+}
+
+export default async function ConsolePage() {
+ const enabled = await getEnabled();
+
+ if (!enabled) {
+ return null;
+ }
+
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Test Console | umami',
+};
diff --git a/src/components/pages/dashboard/Dashboard.js b/src/app/(main)/dashboard/Dashboard.js
similarity index 79%
rename from src/components/pages/dashboard/Dashboard.js
rename to src/app/(main)/dashboard/Dashboard.js
index 2294b8be..5fb65f23 100644
--- a/src/components/pages/dashboard/Dashboard.js
+++ b/src/app/(main)/dashboard/Dashboard.js
@@ -1,11 +1,11 @@
-import { Button, Icon, Icons, Text } from 'react-basics';
+'use client';
+import { Button, Icon, Icons, Loading, Text } from 'react-basics';
import Link from 'next/link';
-import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import Pager from 'components/common/Pager';
-import WebsiteChartList from 'components/pages/websites/WebsiteChartList';
-import DashboardSettingsButton from 'components/pages/dashboard/DashboardSettingsButton';
-import DashboardEdit from 'components/pages/dashboard/DashboardEdit';
+import WebsiteChartList from '../../(main)/websites/[id]/WebsiteChartList';
+import DashboardSettingsButton from 'app/(main)/dashboard/DashboardSettingsButton';
+import DashboardEdit from 'app/(main)/dashboard/DashboardEdit';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import useApi from 'components/hooks/useApi';
import useDashboard from 'store/dashboard';
@@ -20,18 +20,18 @@ export function Dashboard() {
const { get, useQuery } = useApi();
const { page, handlePageChange } = useApiFilter();
const pageSize = 10;
- const {
- data: result,
- isLoading,
- error,
- } = useQuery(['websites', page, pageSize], () =>
+ const { data: result, isLoading } = useQuery(['websites', page, pageSize], () =>
get('/websites', { includeTeams: 1, page, pageSize }),
);
const { data, count } = result || {};
const hasData = data && data?.length !== 0;
+ if (isLoading) {
+ return ;
+ }
+
return (
-
+ <>
{!editing && hasData && }
@@ -63,7 +63,7 @@ export function Dashboard() {
)}
>
)}
-
+ >
);
}
diff --git a/src/components/pages/dashboard/DashboardEdit.js b/src/app/(main)/dashboard/DashboardEdit.js
similarity index 93%
rename from src/components/pages/dashboard/DashboardEdit.js
rename to src/app/(main)/dashboard/DashboardEdit.js
index f628599f..3af33867 100644
--- a/src/components/pages/dashboard/DashboardEdit.js
+++ b/src/app/(main)/dashboard/DashboardEdit.js
@@ -1,3 +1,4 @@
+'use client';
import { useState, useMemo } from 'react';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import classNames from 'classnames';
@@ -7,7 +8,6 @@ import useDashboard, { saveDashboard } from 'store/dashboard';
import useMessages from 'components/hooks/useMessages';
import useApi from 'components/hooks/useApi';
import styles from './DashboardEdit.module.css';
-import Page from 'components/layout/Page';
const dragId = 'dashboard-website-ordering';
@@ -17,11 +17,7 @@ export function DashboardEdit() {
const { formatMessage, labels } = useMessages();
const [order, setOrder] = useState(websiteOrder || []);
const { get, useQuery } = useApi();
- const {
- data: result,
- isLoading,
- error,
- } = useQuery(['websites'], () => get('/websites', { includeTeams: 1 }));
+ const { data: result } = useQuery(['websites'], () => get('/websites', { includeTeams: 1 }));
const { data: websites } = result || {};
const ordered = useMemo(() => {
@@ -59,7 +55,7 @@ export function DashboardEdit() {
}
return (
-
+ <>
{formatMessage(labels.save)}
@@ -105,7 +101,7 @@ export function DashboardEdit() {
-
+ >
);
}
diff --git a/src/components/pages/dashboard/DashboardEdit.module.css b/src/app/(main)/dashboard/DashboardEdit.module.css
similarity index 100%
rename from src/components/pages/dashboard/DashboardEdit.module.css
rename to src/app/(main)/dashboard/DashboardEdit.module.css
diff --git a/src/components/pages/dashboard/DashboardSettingsButton.js b/src/app/(main)/dashboard/DashboardSettingsButton.js
similarity index 100%
rename from src/components/pages/dashboard/DashboardSettingsButton.js
rename to src/app/(main)/dashboard/DashboardSettingsButton.js
diff --git a/src/components/pages/dashboard/DashboardSettingsButton.module.css b/src/app/(main)/dashboard/DashboardSettingsButton.module.css
similarity index 100%
rename from src/components/pages/dashboard/DashboardSettingsButton.module.css
rename to src/app/(main)/dashboard/DashboardSettingsButton.module.css
diff --git a/src/app/(main)/dashboard/page.tsx b/src/app/(main)/dashboard/page.tsx
new file mode 100644
index 00000000..91cc9c6e
--- /dev/null
+++ b/src/app/(main)/dashboard/page.tsx
@@ -0,0 +1,10 @@
+import Dashboard from 'app/(main)/dashboard/Dashboard';
+import { Metadata } from 'next';
+
+export default function DashboardPage() {
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Dashboard | umami',
+};
diff --git a/src/components/layout/AppLayout.module.css b/src/app/(main)/layout.module.css
similarity index 91%
rename from src/components/layout/AppLayout.module.css
rename to src/app/(main)/layout.module.css
index bcce963f..0afd11f9 100644
--- a/src/components/layout/AppLayout.module.css
+++ b/src/app/(main)/layout.module.css
@@ -10,7 +10,6 @@
width: 100vw;
grid-column: 1;
grid-row: 1 / 2;
- z-index: var(--z-index-popup);
}
.body {
diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx
new file mode 100644
index 00000000..1c9cc277
--- /dev/null
+++ b/src/app/(main)/layout.tsx
@@ -0,0 +1,19 @@
+import Shell from './Shell';
+import NavBar from './NavBar';
+import Page from 'components/layout/Page';
+import styles from './layout.module.css';
+
+export default function AppLayout({ children }) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/reports/ReportDeleteButton.js b/src/app/(main)/reports/ReportDeleteButton.js
new file mode 100644
index 00000000..35809a98
--- /dev/null
+++ b/src/app/(main)/reports/ReportDeleteButton.js
@@ -0,0 +1,42 @@
+import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
+import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm';
+import { useApi, useMessages } from 'components/hooks';
+import { setValue } from 'store/cache';
+
+export function ReportDeleteButton({ reportId, reportName, onDelete }) {
+ const { formatMessage, labels } = useMessages();
+ const { del, useMutation } = useApi();
+ const { mutate } = useMutation(reportId => del(`/reports/${reportId}`));
+
+ const handleConfirm = close => {
+ mutate(reportId, {
+ onSuccess: () => {
+ setValue('reports', Date.now());
+ onDelete?.();
+ close();
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.delete)}
+
+
+ {close => (
+
+ )}
+
+
+ );
+}
+
+export default ReportDeleteButton;
diff --git a/src/app/(main)/reports/ReportsDataTable.js b/src/app/(main)/reports/ReportsDataTable.js
new file mode 100644
index 00000000..b1c058f1
--- /dev/null
+++ b/src/app/(main)/reports/ReportsDataTable.js
@@ -0,0 +1,18 @@
+'use client';
+import { useApi } from 'components/hooks';
+import ReportsTable from './ReportsTable';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import DataTable from 'components/common/DataTable';
+import useCache from 'store/cache';
+
+export default function ReportsDataTable() {
+ const { get } = useApi();
+ const modified = useCache(state => state?.reports);
+ const queryResult = useFilterQuery(['reports', { modified }], params => get(`/reports`, params));
+
+ return (
+
+ {({ data }) => }
+
+ );
+}
diff --git a/src/app/(main)/reports/ReportsHeader.js b/src/app/(main)/reports/ReportsHeader.js
new file mode 100644
index 00000000..43cce217
--- /dev/null
+++ b/src/app/(main)/reports/ReportsHeader.js
@@ -0,0 +1,25 @@
+'use client';
+import PageHeader from 'components/layout/PageHeader';
+import { Button, Icon, Icons, Text } from 'react-basics';
+import { useMessages } from 'components/hooks';
+import { useRouter } from 'next/navigation';
+
+export function ReportsHeader() {
+ const { formatMessage, labels } = useMessages();
+ const router = useRouter();
+
+ const handleClick = () => router.push('/reports/create');
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.createReport)}
+
+
+ );
+}
+
+export default ReportsHeader;
diff --git a/src/app/(main)/reports/ReportsTable.js b/src/app/(main)/reports/ReportsTable.js
new file mode 100644
index 00000000..a85b09e3
--- /dev/null
+++ b/src/app/(main)/reports/ReportsTable.js
@@ -0,0 +1,50 @@
+import LinkButton from 'components/common/LinkButton';
+import { useMessages } from 'components/hooks';
+import useUser from 'components/hooks/useUser';
+import { GridColumn, GridTable, Icon, Icons, Text } from 'react-basics';
+import { REPORT_TYPES } from 'lib/constants';
+import ReportDeleteButton from './ReportDeleteButton';
+
+export function ReportsTable({ data = [], showDomain }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ return (
+
+
+
+
+ {row => {
+ return formatMessage(
+ labels[Object.keys(REPORT_TYPES).find(key => REPORT_TYPES[key] === row.type)],
+ );
+ }}
+
+ {showDomain && (
+
+ {row => row.website.domain}
+
+ )}
+
+ {row => {
+ const { id, name, userId, website } = row;
+ return (
+ <>
+ {(user.id === userId || user.id === website?.userId) && (
+
+ )}
+
+
+
+
+ {formatMessage(labels.view)}
+
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default ReportsTable;
diff --git a/src/components/pages/reports/BaseParameters.js b/src/app/(main)/reports/[id]/BaseParameters.js
similarity index 100%
rename from src/components/pages/reports/BaseParameters.js
rename to src/app/(main)/reports/[id]/BaseParameters.js
index 44a9da5b..a54ef4f3 100644
--- a/src/components/pages/reports/BaseParameters.js
+++ b/src/app/(main)/reports/[id]/BaseParameters.js
@@ -1,10 +1,10 @@
+import { useContext } from 'react';
import { FormRow } from 'react-basics';
+import { parseDateRange } from 'lib/date';
import DateFilter from 'components/input/DateFilter';
import WebsiteSelect from 'components/input/WebsiteSelect';
-import { parseDateRange } from 'lib/date';
-import { useContext } from 'react';
-import { ReportContext } from './Report';
import { useMessages } from 'components/hooks';
+import { ReportContext } from './Report';
export function BaseParameters({
showWebsiteSelect = true,
diff --git a/src/components/pages/reports/FieldAddForm.js b/src/app/(main)/reports/[id]/FieldAddForm.js
similarity index 100%
rename from src/components/pages/reports/FieldAddForm.js
rename to src/app/(main)/reports/[id]/FieldAddForm.js
diff --git a/src/components/pages/reports/FieldAddForm.module.css b/src/app/(main)/reports/[id]/FieldAddForm.module.css
similarity index 100%
rename from src/components/pages/reports/FieldAddForm.module.css
rename to src/app/(main)/reports/[id]/FieldAddForm.module.css
diff --git a/src/components/pages/reports/FieldAggregateForm.js b/src/app/(main)/reports/[id]/FieldAggregateForm.js
similarity index 100%
rename from src/components/pages/reports/FieldAggregateForm.js
rename to src/app/(main)/reports/[id]/FieldAggregateForm.js
diff --git a/src/components/pages/reports/FieldFilterForm.js b/src/app/(main)/reports/[id]/FieldFilterForm.js
similarity index 100%
rename from src/components/pages/reports/FieldFilterForm.js
rename to src/app/(main)/reports/[id]/FieldFilterForm.js
diff --git a/src/components/pages/reports/FieldFilterForm.module.css b/src/app/(main)/reports/[id]/FieldFilterForm.module.css
similarity index 100%
rename from src/components/pages/reports/FieldFilterForm.module.css
rename to src/app/(main)/reports/[id]/FieldFilterForm.module.css
diff --git a/src/components/pages/reports/FieldSelectForm.js b/src/app/(main)/reports/[id]/FieldSelectForm.js
similarity index 100%
rename from src/components/pages/reports/FieldSelectForm.js
rename to src/app/(main)/reports/[id]/FieldSelectForm.js
diff --git a/src/components/pages/reports/FieldSelectForm.module.css b/src/app/(main)/reports/[id]/FieldSelectForm.module.css
similarity index 100%
rename from src/components/pages/reports/FieldSelectForm.module.css
rename to src/app/(main)/reports/[id]/FieldSelectForm.module.css
diff --git a/src/components/pages/reports/FilterSelectForm.js b/src/app/(main)/reports/[id]/FilterSelectForm.js
similarity index 84%
rename from src/components/pages/reports/FilterSelectForm.js
rename to src/app/(main)/reports/[id]/FilterSelectForm.js
index 5265c741..6ecd7751 100644
--- a/src/components/pages/reports/FilterSelectForm.js
+++ b/src/app/(main)/reports/[id]/FilterSelectForm.js
@@ -1,16 +1,20 @@
import { useState } from 'react';
import FieldSelectForm from './FieldSelectForm';
import FieldFilterForm from './FieldFilterForm';
-import { useApi } from 'components/hooks';
+import { useApi, useDateRange } from 'components/hooks';
import { Loading } from 'react-basics';
function useValues(websiteId, type) {
const { get, useQuery } = useApi();
+ const [dateRange] = useDateRange(websiteId);
+ const { startDate, endDate } = dateRange;
const { data, error, isLoading } = useQuery(
['websites:values', websiteId, type],
() =>
get(`/websites/${websiteId}/values`, {
type,
+ startAt: +startDate,
+ endAt: +endDate,
}),
{ enabled: !!(websiteId && type) },
);
diff --git a/src/components/pages/reports/ParameterList.js b/src/app/(main)/reports/[id]/ParameterList.js
similarity index 100%
rename from src/components/pages/reports/ParameterList.js
rename to src/app/(main)/reports/[id]/ParameterList.js
diff --git a/src/components/pages/reports/ParameterList.module.css b/src/app/(main)/reports/[id]/ParameterList.module.css
similarity index 100%
rename from src/components/pages/reports/ParameterList.module.css
rename to src/app/(main)/reports/[id]/ParameterList.module.css
diff --git a/src/components/pages/reports/PopupForm.js b/src/app/(main)/reports/[id]/PopupForm.js
similarity index 100%
rename from src/components/pages/reports/PopupForm.js
rename to src/app/(main)/reports/[id]/PopupForm.js
diff --git a/src/components/pages/reports/PopupForm.module.css b/src/app/(main)/reports/[id]/PopupForm.module.css
similarity index 100%
rename from src/components/pages/reports/PopupForm.module.css
rename to src/app/(main)/reports/[id]/PopupForm.module.css
diff --git a/src/components/pages/reports/Report.js b/src/app/(main)/reports/[id]/Report.js
similarity index 69%
rename from src/components/pages/reports/Report.js
rename to src/app/(main)/reports/[id]/Report.js
index de17aca6..0bea18ed 100644
--- a/src/components/pages/reports/Report.js
+++ b/src/app/(main)/reports/[id]/Report.js
@@ -1,20 +1,18 @@
+'use client';
import { createContext } from 'react';
-import Page from 'components/layout/Page';
-import styles from './reports.module.css';
import { useReport } from 'components/hooks';
+import styles from './Report.module.css';
export const ReportContext = createContext(null);
export function Report({ reportId, defaultParameters, children, ...props }) {
const report = useReport(reportId, defaultParameters);
- //console.log({ report });
-
return (
-
+
{children}
-
+
);
}
diff --git a/src/components/pages/reports/reports.module.css b/src/app/(main)/reports/[id]/Report.module.css
similarity index 100%
rename from src/components/pages/reports/reports.module.css
rename to src/app/(main)/reports/[id]/Report.module.css
diff --git a/src/components/pages/reports/ReportBody.js b/src/app/(main)/reports/[id]/ReportBody.js
similarity index 75%
rename from src/components/pages/reports/ReportBody.js
rename to src/app/(main)/reports/[id]/ReportBody.js
index 2310c8af..b34b138d 100644
--- a/src/components/pages/reports/ReportBody.js
+++ b/src/app/(main)/reports/[id]/ReportBody.js
@@ -1,4 +1,4 @@
-import styles from './reports.module.css';
+import styles from './Report.module.css';
export function ReportBody({ children }) {
return {children}
;
diff --git a/src/app/(main)/reports/[id]/ReportDetails.js b/src/app/(main)/reports/[id]/ReportDetails.js
new file mode 100644
index 00000000..8605ffb3
--- /dev/null
+++ b/src/app/(main)/reports/[id]/ReportDetails.js
@@ -0,0 +1,26 @@
+'use client';
+import FunnelReport from '../funnel/FunnelReport';
+import EventDataReport from '../event-data/EventDataReport';
+import InsightsReport from '../insights/InsightsReport';
+import RetentionReport from '../retention/RetentionReport';
+import { useApi } from 'components/hooks';
+
+const reports = {
+ funnel: FunnelReport,
+ 'event-data': EventDataReport,
+ insights: InsightsReport,
+ retention: RetentionReport,
+};
+
+export default function ReportDetails({ reportId }) {
+ const { get, useQuery } = useApi();
+ const { data: report } = useQuery(['reports', reportId], () => get(`/reports/${reportId}`));
+
+ if (!report) {
+ return null;
+ }
+
+ const ReportComponent = reports[report.type];
+
+ return ;
+}
diff --git a/src/components/pages/reports/ReportHeader.js b/src/app/(main)/reports/[id]/ReportHeader.js
similarity index 96%
rename from src/components/pages/reports/ReportHeader.js
rename to src/app/(main)/reports/[id]/ReportHeader.js
index 930f745b..a182142f 100644
--- a/src/components/pages/reports/ReportHeader.js
+++ b/src/app/(main)/reports/[id]/ReportHeader.js
@@ -1,11 +1,11 @@
import { useContext } from 'react';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import { Icon, LoadingButton, InlineEditField, useToasts } from 'react-basics';
import PageHeader from 'components/layout/PageHeader';
import { useMessages, useApi } from 'components/hooks';
import { ReportContext } from './Report';
import styles from './ReportHeader.module.css';
-import reportStyles from './reports.module.css';
+import reportStyles from './Report.module.css';
export function ReportHeader({ icon }) {
const { report, updateReport } = useContext(ReportContext);
diff --git a/src/components/pages/reports/ReportHeader.module.css b/src/app/(main)/reports/[id]/ReportHeader.module.css
similarity index 100%
rename from src/components/pages/reports/ReportHeader.module.css
rename to src/app/(main)/reports/[id]/ReportHeader.module.css
diff --git a/src/components/pages/reports/ReportMenu.js b/src/app/(main)/reports/[id]/ReportMenu.js
similarity index 75%
rename from src/components/pages/reports/ReportMenu.js
rename to src/app/(main)/reports/[id]/ReportMenu.js
index abfea6fe..91c6fc16 100644
--- a/src/components/pages/reports/ReportMenu.js
+++ b/src/app/(main)/reports/[id]/ReportMenu.js
@@ -1,4 +1,4 @@
-import styles from './reports.module.css';
+import styles from './Report.module.css';
export function ReportMenu({ children }) {
return {children}
;
diff --git a/src/app/(main)/reports/[id]/page.tsx b/src/app/(main)/reports/[id]/page.tsx
new file mode 100644
index 00000000..9ba87f41
--- /dev/null
+++ b/src/app/(main)/reports/[id]/page.tsx
@@ -0,0 +1,14 @@
+import ReportDetails from './ReportDetails';
+import { Metadata } from 'next';
+
+export default function ReportDetailsPage({ params: { id } }) {
+ if (!id) {
+ return null;
+ }
+
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Reports | umami',
+};
diff --git a/src/components/pages/reports/ReportTemplates.js b/src/app/(main)/reports/create/ReportTemplates.js
similarity index 96%
rename from src/components/pages/reports/ReportTemplates.js
rename to src/app/(main)/reports/create/ReportTemplates.js
index 59cc8b31..003cb3fc 100644
--- a/src/components/pages/reports/ReportTemplates.js
+++ b/src/app/(main)/reports/create/ReportTemplates.js
@@ -1,6 +1,6 @@
+'use client';
import Link from 'next/link';
import { Button, Icons, Text, Icon } from 'react-basics';
-import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import Funnel from 'assets/funnel.svg';
import Lightbulb from 'assets/lightbulb.svg';
@@ -57,7 +57,7 @@ export function ReportTemplates({ showHeader = true }) {
];
return (
-
+ <>
{showHeader && }
{reports.map(({ title, description, url, icon }) => {
@@ -66,7 +66,7 @@ export function ReportTemplates({ showHeader = true }) {
);
})}
-
+ >
);
}
diff --git a/src/components/pages/reports/ReportTemplates.module.css b/src/app/(main)/reports/create/ReportTemplates.module.css
similarity index 100%
rename from src/components/pages/reports/ReportTemplates.module.css
rename to src/app/(main)/reports/create/ReportTemplates.module.css
diff --git a/src/app/(main)/reports/create/page.tsx b/src/app/(main)/reports/create/page.tsx
new file mode 100644
index 00000000..a1a761bc
--- /dev/null
+++ b/src/app/(main)/reports/create/page.tsx
@@ -0,0 +1,10 @@
+import ReportTemplates from './ReportTemplates';
+import { Metadata } from 'next';
+
+export default function ReportsCreatePage() {
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Create Report | umami',
+};
diff --git a/src/components/pages/reports/event-data/EventDataParameters.js b/src/app/(main)/reports/event-data/EventDataParameters.js
similarity index 95%
rename from src/components/pages/reports/event-data/EventDataParameters.js
rename to src/app/(main)/reports/event-data/EventDataParameters.js
index e0fadb8b..fe9d1e00 100644
--- a/src/components/pages/reports/event-data/EventDataParameters.js
+++ b/src/app/(main)/reports/event-data/EventDataParameters.js
@@ -1,13 +1,13 @@
import { useContext, useRef } from 'react';
-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';
-import { DATA_TYPES, REPORT_PARAMETERS } from 'lib/constants';
import Icons from 'components/icons';
-import FieldAddForm from '../FieldAddForm';
-import BaseParameters from '../BaseParameters';
-import ParameterList from '../ParameterList';
+import { useApi, useMessages } from 'components/hooks';
+import { DATA_TYPES, REPORT_PARAMETERS } from 'lib/constants';
+import { ReportContext } from '../[id]/Report';
+import FieldAddForm from '../[id]/FieldAddForm';
+import ParameterList from '../[id]/ParameterList';
+import BaseParameters from '../[id]/BaseParameters';
import styles from './EventDataParameters.module.css';
function useFields(websiteId, startDate, endDate) {
diff --git a/src/components/pages/reports/event-data/EventDataParameters.module.css b/src/app/(main)/reports/event-data/EventDataParameters.module.css
similarity index 100%
rename from src/components/pages/reports/event-data/EventDataParameters.module.css
rename to src/app/(main)/reports/event-data/EventDataParameters.module.css
diff --git a/src/components/pages/reports/event-data/EventDataReport.js b/src/app/(main)/reports/event-data/EventDataReport.js
similarity index 76%
rename from src/components/pages/reports/event-data/EventDataReport.js
rename to src/app/(main)/reports/event-data/EventDataReport.js
index eb49a29d..e91cb4a2 100644
--- a/src/components/pages/reports/event-data/EventDataReport.js
+++ b/src/app/(main)/reports/event-data/EventDataReport.js
@@ -1,7 +1,7 @@
-import Report from '../Report';
-import ReportHeader from '../ReportHeader';
-import ReportMenu from '../ReportMenu';
-import ReportBody from '../ReportBody';
+import Report from '../[id]/Report';
+import ReportHeader from '../[id]/ReportHeader';
+import ReportMenu from '../[id]/ReportMenu';
+import ReportBody from '../[id]/ReportBody';
import EventDataParameters from './EventDataParameters';
import EventDataTable from './EventDataTable';
import Nodes from 'assets/nodes.svg';
diff --git a/src/components/pages/reports/event-data/EventDataTable.js b/src/app/(main)/reports/event-data/EventDataTable.js
similarity index 92%
rename from src/components/pages/reports/event-data/EventDataTable.js
rename to src/app/(main)/reports/event-data/EventDataTable.js
index b6450261..b709aee7 100644
--- a/src/components/pages/reports/event-data/EventDataTable.js
+++ b/src/app/(main)/reports/event-data/EventDataTable.js
@@ -1,7 +1,7 @@
import { useContext } from 'react';
import { GridTable, GridColumn } from 'react-basics';
import { useMessages } from 'components/hooks';
-import { ReportContext } from '../Report';
+import { ReportContext } from '../[id]/Report';
export function EventDataTable() {
const { report } = useContext(ReportContext);
diff --git a/src/components/pages/reports/funnel/FunnelChart.js b/src/app/(main)/reports/funnel/FunnelChart.js
similarity index 97%
rename from src/components/pages/reports/funnel/FunnelChart.js
rename to src/app/(main)/reports/funnel/FunnelChart.js
index 829a3008..7461afbc 100644
--- a/src/components/pages/reports/funnel/FunnelChart.js
+++ b/src/app/(main)/reports/funnel/FunnelChart.js
@@ -5,7 +5,7 @@ import useTheme from 'components/hooks/useTheme';
import BarChart from 'components/metrics/BarChart';
import { formatLongNumber } from 'lib/format';
import styles from './FunnelChart.module.css';
-import { ReportContext } from '../Report';
+import { ReportContext } from '../[id]/Report';
export function FunnelChart({ className, loading }) {
const { report } = useContext(ReportContext);
diff --git a/src/components/pages/reports/funnel/FunnelChart.module.css b/src/app/(main)/reports/funnel/FunnelChart.module.css
similarity index 100%
rename from src/components/pages/reports/funnel/FunnelChart.module.css
rename to src/app/(main)/reports/funnel/FunnelChart.module.css
diff --git a/src/components/pages/reports/funnel/FunnelParameters.js b/src/app/(main)/reports/funnel/FunnelParameters.js
similarity index 92%
rename from src/components/pages/reports/funnel/FunnelParameters.js
rename to src/app/(main)/reports/funnel/FunnelParameters.js
index a3fbe663..0e96185f 100644
--- a/src/components/pages/reports/funnel/FunnelParameters.js
+++ b/src/app/(main)/reports/funnel/FunnelParameters.js
@@ -13,10 +13,10 @@ import {
} from 'react-basics';
import Icons from 'components/icons';
import UrlAddForm from './UrlAddForm';
-import { ReportContext } from 'components/pages/reports/Report';
-import BaseParameters from '../BaseParameters';
-import ParameterList from '../ParameterList';
-import PopupForm from '../PopupForm';
+import { ReportContext } from '../[id]/Report';
+import BaseParameters from '../[id]/BaseParameters';
+import ParameterList from '../[id]/ParameterList';
+import PopupForm from '../[id]/PopupForm';
export function FunnelParameters() {
const { report, runReport, updateReport, isRunning } = useContext(ReportContext);
diff --git a/src/components/pages/reports/funnel/FunnelReport.js b/src/app/(main)/reports/funnel/FunnelReport.js
similarity index 77%
rename from src/components/pages/reports/funnel/FunnelReport.js
rename to src/app/(main)/reports/funnel/FunnelReport.js
index d2971fa3..69f46091 100644
--- a/src/components/pages/reports/funnel/FunnelReport.js
+++ b/src/app/(main)/reports/funnel/FunnelReport.js
@@ -1,10 +1,11 @@
+'use client';
import FunnelChart from './FunnelChart';
import FunnelTable from './FunnelTable';
import FunnelParameters from './FunnelParameters';
-import Report from '../Report';
-import ReportHeader from '../ReportHeader';
-import ReportMenu from '../ReportMenu';
-import ReportBody from '../ReportBody';
+import Report from '../[id]/Report';
+import ReportHeader from '../[id]/ReportHeader';
+import ReportMenu from '../[id]/ReportMenu';
+import ReportBody from '../[id]/ReportBody';
import Funnel from 'assets/funnel.svg';
import { REPORT_TYPES } from 'lib/constants';
diff --git a/src/components/pages/reports/funnel/FunnelReport.module.css b/src/app/(main)/reports/funnel/FunnelReport.module.css
similarity index 100%
rename from src/components/pages/reports/funnel/FunnelReport.module.css
rename to src/app/(main)/reports/funnel/FunnelReport.module.css
diff --git a/src/components/pages/reports/funnel/FunnelTable.js b/src/app/(main)/reports/funnel/FunnelTable.js
similarity index 90%
rename from src/components/pages/reports/funnel/FunnelTable.js
rename to src/app/(main)/reports/funnel/FunnelTable.js
index 5ca2593c..4cf797f2 100644
--- a/src/components/pages/reports/funnel/FunnelTable.js
+++ b/src/app/(main)/reports/funnel/FunnelTable.js
@@ -1,7 +1,7 @@
import { useContext } from 'react';
import ListTable from 'components/metrics/ListTable';
import { useMessages } from 'components/hooks';
-import { ReportContext } from '../Report';
+import { ReportContext } from '../[id]/Report';
export function FunnelTable() {
const { report } = useContext(ReportContext);
diff --git a/src/components/pages/reports/funnel/UrlAddForm.js b/src/app/(main)/reports/funnel/UrlAddForm.js
similarity index 100%
rename from src/components/pages/reports/funnel/UrlAddForm.js
rename to src/app/(main)/reports/funnel/UrlAddForm.js
diff --git a/src/components/pages/reports/funnel/UrlAddForm.module.css b/src/app/(main)/reports/funnel/UrlAddForm.module.css
similarity index 100%
rename from src/components/pages/reports/funnel/UrlAddForm.module.css
rename to src/app/(main)/reports/funnel/UrlAddForm.module.css
diff --git a/src/app/(main)/reports/funnel/page.tsx b/src/app/(main)/reports/funnel/page.tsx
new file mode 100644
index 00000000..1ce70c75
--- /dev/null
+++ b/src/app/(main)/reports/funnel/page.tsx
@@ -0,0 +1,10 @@
+import FunnelReport from './FunnelReport';
+import { Metadata } from 'next';
+
+export default function FunnelReportPage() {
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Funnel Report | umami',
+};
diff --git a/src/components/pages/reports/insights/InsightsParameters.js b/src/app/(main)/reports/insights/InsightsParameters.js
similarity index 94%
rename from src/components/pages/reports/insights/InsightsParameters.js
rename to src/app/(main)/reports/insights/InsightsParameters.js
index 3ddc0367..d2ba144e 100644
--- a/src/components/pages/reports/insights/InsightsParameters.js
+++ b/src/app/(main)/reports/insights/InsightsParameters.js
@@ -10,14 +10,14 @@ import {
Popup,
TooltipPopup,
} from 'react-basics';
-import { ReportContext } from 'components/pages/reports/Report';
import Icons from 'components/icons';
-import BaseParameters from '../BaseParameters';
-import ParameterList from '../ParameterList';
+import BaseParameters from '../[id]/BaseParameters';
+import { ReportContext } from '../[id]/Report';
+import ParameterList from '../[id]/ParameterList';
+import FilterSelectForm from '../[id]/FilterSelectForm';
+import FieldSelectForm from '../[id]/FieldSelectForm';
+import PopupForm from '../[id]/PopupForm';
import styles from './InsightsParameters.module.css';
-import PopupForm from '../PopupForm';
-import FilterSelectForm from '../FilterSelectForm';
-import FieldSelectForm from '../FieldSelectForm';
export function InsightsParameters() {
const { report, runReport, updateReport, isRunning } = useContext(ReportContext);
diff --git a/src/components/pages/reports/insights/InsightsParameters.module.css b/src/app/(main)/reports/insights/InsightsParameters.module.css
similarity index 100%
rename from src/components/pages/reports/insights/InsightsParameters.module.css
rename to src/app/(main)/reports/insights/InsightsParameters.module.css
diff --git a/src/components/pages/reports/insights/InsightsReport.js b/src/app/(main)/reports/insights/InsightsReport.js
similarity index 76%
rename from src/components/pages/reports/insights/InsightsReport.js
rename to src/app/(main)/reports/insights/InsightsReport.js
index 3d855d9e..f99e187b 100644
--- a/src/components/pages/reports/insights/InsightsReport.js
+++ b/src/app/(main)/reports/insights/InsightsReport.js
@@ -1,7 +1,8 @@
-import Report from '../Report';
-import ReportHeader from '../ReportHeader';
-import ReportMenu from '../ReportMenu';
-import ReportBody from '../ReportBody';
+'use client';
+import Report from '../[id]/Report';
+import ReportHeader from '../[id]/ReportHeader';
+import ReportMenu from '../[id]/ReportMenu';
+import ReportBody from '../[id]/ReportBody';
import InsightsParameters from './InsightsParameters';
import InsightsTable from './InsightsTable';
import Lightbulb from 'assets/lightbulb.svg';
diff --git a/src/components/pages/reports/insights/InsightsTable.js b/src/app/(main)/reports/insights/InsightsTable.js
similarity index 96%
rename from src/components/pages/reports/insights/InsightsTable.js
rename to src/app/(main)/reports/insights/InsightsTable.js
index 88bd0275..05d38042 100644
--- a/src/components/pages/reports/insights/InsightsTable.js
+++ b/src/app/(main)/reports/insights/InsightsTable.js
@@ -1,7 +1,7 @@
import { useContext, useEffect, useState } from 'react';
import { GridTable, GridColumn } from 'react-basics';
import { useFormat, useMessages } from 'components/hooks';
-import { ReportContext } from '../Report';
+import { ReportContext } from '../[id]/Report';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
export function InsightsTable() {
diff --git a/src/app/(main)/reports/insights/page.tsx b/src/app/(main)/reports/insights/page.tsx
new file mode 100644
index 00000000..1f4db5c6
--- /dev/null
+++ b/src/app/(main)/reports/insights/page.tsx
@@ -0,0 +1,10 @@
+import InsightsReport from './InsightsReport';
+import { Metadata } from 'next';
+
+export default function InsightsReportPage() {
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Insights Report | umami',
+};
diff --git a/src/app/(main)/reports/page.tsx b/src/app/(main)/reports/page.tsx
new file mode 100644
index 00000000..aba59db2
--- /dev/null
+++ b/src/app/(main)/reports/page.tsx
@@ -0,0 +1,14 @@
+import ReportsHeader from './ReportsHeader';
+import ReportsDataTable from './ReportsDataTable';
+
+export default function ReportsPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
+export const metadata = {
+ title: 'Reports | umami',
+};
diff --git a/src/components/pages/reports/retention/RetentionParameters.js b/src/app/(main)/reports/retention/RetentionParameters.js
similarity index 92%
rename from src/components/pages/reports/retention/RetentionParameters.js
rename to src/app/(main)/reports/retention/RetentionParameters.js
index e87108d1..866376e0 100644
--- a/src/components/pages/reports/retention/RetentionParameters.js
+++ b/src/app/(main)/reports/retention/RetentionParameters.js
@@ -1,9 +1,9 @@
import { useContext, useRef } from 'react';
import { useMessages } from 'components/hooks';
import { Form, FormButtons, FormRow, SubmitButton } from 'react-basics';
-import { ReportContext } from 'components/pages/reports/Report';
+import { ReportContext } from '../[id]/Report';
import { MonthSelect } from 'components/input/MonthSelect';
-import BaseParameters from '../BaseParameters';
+import BaseParameters from '../[id]/BaseParameters';
import { parseDateRange } from 'lib/date';
export function RetentionParameters() {
diff --git a/src/components/pages/reports/retention/RetentionReport.js b/src/app/(main)/reports/retention/RetentionReport.js
similarity index 81%
rename from src/components/pages/reports/retention/RetentionReport.js
rename to src/app/(main)/reports/retention/RetentionReport.js
index a9aaeb3e..ae42e76b 100644
--- a/src/components/pages/reports/retention/RetentionReport.js
+++ b/src/app/(main)/reports/retention/RetentionReport.js
@@ -1,9 +1,10 @@
+'use client';
import RetentionTable from './RetentionTable';
import RetentionParameters from './RetentionParameters';
-import Report from '../Report';
-import ReportHeader from '../ReportHeader';
-import ReportMenu from '../ReportMenu';
-import ReportBody from '../ReportBody';
+import Report from '../[id]/Report';
+import ReportHeader from '../[id]/ReportHeader';
+import ReportMenu from '../[id]/ReportMenu';
+import ReportBody from '../[id]/ReportBody';
import Magnet from 'assets/magnet.svg';
import { REPORT_TYPES } from 'lib/constants';
import { parseDateRange } from 'lib/date';
diff --git a/src/components/pages/reports/retention/RetentionReport.module.css b/src/app/(main)/reports/retention/RetentionReport.module.css
similarity index 100%
rename from src/components/pages/reports/retention/RetentionReport.module.css
rename to src/app/(main)/reports/retention/RetentionReport.module.css
diff --git a/src/components/pages/reports/retention/RetentionTable.js b/src/app/(main)/reports/retention/RetentionTable.js
similarity index 91%
rename from src/components/pages/reports/retention/RetentionTable.js
rename to src/app/(main)/reports/retention/RetentionTable.js
index ad1eaa6f..a71fae6f 100644
--- a/src/components/pages/reports/retention/RetentionTable.js
+++ b/src/app/(main)/reports/retention/RetentionTable.js
@@ -1,13 +1,14 @@
import { useContext } from 'react';
import classNames from 'classnames';
-import { ReportContext } from '../Report';
+import { ReportContext } from '../[id]/Report';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
-import { useMessages } from 'components/hooks';
-import { useLocale } from 'components/hooks';
+import { useMessages, useLocale } from 'components/hooks';
import { formatDate } from 'lib/date';
import styles from './RetentionTable.module.css';
-export function RetentionTable() {
+const DAYS = [1, 2, 3, 4, 5, 6, 7, 14, 21, 28];
+
+export function RetentionTable({ days = DAYS }) {
const { formatMessage, labels } = useMessages();
const { locale } = useLocale();
const { report } = useContext(ReportContext);
@@ -17,8 +18,6 @@ export function RetentionTable() {
return ;
}
- const days = [1, 2, 3, 4, 5, 6, 7, 14, 21, 28];
-
const rows = data.reduce((arr, row) => {
const { date, visitors, day } = row;
if (day === 0) {
diff --git a/src/components/pages/reports/retention/RetentionTable.module.css b/src/app/(main)/reports/retention/RetentionTable.module.css
similarity index 100%
rename from src/components/pages/reports/retention/RetentionTable.module.css
rename to src/app/(main)/reports/retention/RetentionTable.module.css
diff --git a/src/app/(main)/reports/retention/page.js b/src/app/(main)/reports/retention/page.js
new file mode 100644
index 00000000..7c60cee8
--- /dev/null
+++ b/src/app/(main)/reports/retention/page.js
@@ -0,0 +1,9 @@
+import RetentionReport from './RetentionReport';
+
+export default function RetentionReportPage() {
+ return ;
+}
+
+export const metadata = {
+ title: 'Create Report | umami',
+};
diff --git a/src/app/(main)/settings/layout.module.css b/src/app/(main)/settings/layout.module.css
new file mode 100644
index 00000000..19162ef5
--- /dev/null
+++ b/src/app/(main)/settings/layout.module.css
@@ -0,0 +1,31 @@
+.layout {
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ gap: 20px;
+}
+
+.menu {
+ width: 240px;
+ padding-top: 34px;
+ padding-right: 20px;
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ min-height: 50vh;
+}
+
+@media only screen and (max-width: 992px) {
+ .layout {
+ grid-template-columns: 1fr;
+ }
+
+ .menu {
+ display: none;
+ }
+
+ .content {
+ margin-top: 20px;
+ }
+}
diff --git a/src/components/layout/SettingsLayout.js b/src/app/(main)/settings/layout.tsx
similarity index 57%
rename from src/components/layout/SettingsLayout.js
rename to src/app/(main)/settings/layout.tsx
index 0f4aa5d9..42e9b62f 100644
--- a/src/components/layout/SettingsLayout.js
+++ b/src/app/(main)/settings/layout.tsx
@@ -1,15 +1,15 @@
-import { Row, Column } from 'react-basics';
-import { useRouter } from 'next/router';
-import SideNav from './SideNav';
+'use client';
+import { usePathname } from 'next/navigation';
import useUser from 'components/hooks/useUser';
import useMessages from 'components/hooks/useMessages';
-import styles from './SettingsLayout.module.css';
+import SideNav from 'components/layout/SideNav';
+import styles from './layout.module.css';
-export function SettingsLayout({ children }) {
+export default function SettingsLayout({ children }) {
const { user } = useUser();
- const { pathname } = useRouter();
+ const pathname = usePathname();
const { formatMessage, labels } = useMessages();
- const cloudMode = Boolean(process.env.cloudMode);
+ const cloudMode = !!process.env.cloudMode;
const items = [
{ key: 'websites', label: formatMessage(labels.websites), url: '/settings/websites' },
@@ -20,18 +20,18 @@ export function SettingsLayout({ children }) {
const getKey = () => items.find(({ url }) => pathname === url)?.key;
+ if (cloudMode) {
+ return null;
+ }
+
return (
-
+
{!cloudMode && (
-
+
-
+
)}
-
- {children}
-
-
+ {children}
+
);
}
-
-export default SettingsLayout;
diff --git a/src/components/pages/settings/profile/DateRangeSetting.js b/src/app/(main)/settings/profile/DateRangeSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/DateRangeSetting.js
rename to src/app/(main)/settings/profile/DateRangeSetting.js
diff --git a/src/components/pages/settings/profile/LanguageSetting.js b/src/app/(main)/settings/profile/LanguageSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/LanguageSetting.js
rename to src/app/(main)/settings/profile/LanguageSetting.js
diff --git a/src/components/pages/settings/profile/PasswordChangeButton.js b/src/app/(main)/settings/profile/PasswordChangeButton.js
similarity index 91%
rename from src/components/pages/settings/profile/PasswordChangeButton.js
rename to src/app/(main)/settings/profile/PasswordChangeButton.js
index 81324eaa..29ec844a 100644
--- a/src/components/pages/settings/profile/PasswordChangeButton.js
+++ b/src/app/(main)/settings/profile/PasswordChangeButton.js
@@ -1,5 +1,5 @@
import { Button, Icon, Text, useToasts, ModalTrigger, Modal } from 'react-basics';
-import PasswordEditForm from 'components/pages/settings/profile/PasswordEditForm';
+import PasswordEditForm from 'app/(main)/settings/profile/PasswordEditForm';
import Icons from 'components/icons';
import useMessages from 'components/hooks/useMessages';
diff --git a/src/components/pages/settings/profile/PasswordEditForm.js b/src/app/(main)/settings/profile/PasswordEditForm.js
similarity index 100%
rename from src/components/pages/settings/profile/PasswordEditForm.js
rename to src/app/(main)/settings/profile/PasswordEditForm.js
diff --git a/src/app/(main)/settings/profile/ProfileHeader.js b/src/app/(main)/settings/profile/ProfileHeader.js
new file mode 100644
index 00000000..35aeb0e6
--- /dev/null
+++ b/src/app/(main)/settings/profile/ProfileHeader.js
@@ -0,0 +1,11 @@
+'use client';
+import PageHeader from 'components/layout/PageHeader';
+import { useMessages } from 'components/hooks';
+
+export function ProfileHeader() {
+ const { formatMessage, labels } = useMessages();
+
+ return ;
+}
+
+export default ProfileHeader;
diff --git a/src/components/pages/settings/profile/ProfileDetails.js b/src/app/(main)/settings/profile/ProfileSettings.js
similarity index 80%
rename from src/components/pages/settings/profile/ProfileDetails.js
rename to src/app/(main)/settings/profile/ProfileSettings.js
index d4a3a7d5..89c6354c 100644
--- a/src/components/pages/settings/profile/ProfileDetails.js
+++ b/src/app/(main)/settings/profile/ProfileSettings.js
@@ -1,14 +1,15 @@
+'use client';
import { Form, FormRow } from 'react-basics';
-import TimezoneSetting from 'components/pages/settings/profile/TimezoneSetting';
-import DateRangeSetting from 'components/pages/settings/profile/DateRangeSetting';
-import LanguageSetting from 'components/pages/settings/profile/LanguageSetting';
-import ThemeSetting from 'components/pages/settings/profile/ThemeSetting';
+import TimezoneSetting from 'app/(main)/settings/profile/TimezoneSetting';
+import DateRangeSetting from 'app/(main)/settings/profile/DateRangeSetting';
+import LanguageSetting from 'app/(main)/settings/profile/LanguageSetting';
+import ThemeSetting from 'app/(main)/settings/profile/ThemeSetting';
import PasswordChangeButton from './PasswordChangeButton';
import useUser from 'components/hooks/useUser';
import useMessages from 'components/hooks/useMessages';
import { ROLES } from 'lib/constants';
-export function ProfileDetails() {
+export function ProfileSettings() {
const { user } = useUser();
const { formatMessage, labels } = useMessages();
const cloudMode = Boolean(process.env.cloudMode);
@@ -58,4 +59,4 @@ export function ProfileDetails() {
);
}
-export default ProfileDetails;
+export default ProfileSettings;
diff --git a/src/components/pages/settings/profile/ThemeSetting.js b/src/app/(main)/settings/profile/ThemeSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/ThemeSetting.js
rename to src/app/(main)/settings/profile/ThemeSetting.js
diff --git a/src/components/pages/settings/profile/ThemeSetting.module.css b/src/app/(main)/settings/profile/ThemeSetting.module.css
similarity index 100%
rename from src/components/pages/settings/profile/ThemeSetting.module.css
rename to src/app/(main)/settings/profile/ThemeSetting.module.css
diff --git a/src/components/pages/settings/profile/TimezoneSetting.js b/src/app/(main)/settings/profile/TimezoneSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/TimezoneSetting.js
rename to src/app/(main)/settings/profile/TimezoneSetting.js
diff --git a/src/app/(main)/settings/profile/page.tsx b/src/app/(main)/settings/profile/page.tsx
new file mode 100644
index 00000000..d7a3ad92
--- /dev/null
+++ b/src/app/(main)/settings/profile/page.tsx
@@ -0,0 +1,16 @@
+import ProfileHeader from './ProfileHeader';
+import ProfileSettings from './ProfileSettings';
+import { Metadata } from 'next';
+
+export default function () {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export const metadata: Metadata = {
+ title: 'Profile Settings | umami',
+};
diff --git a/src/components/pages/settings/teams/TeamAddForm.js b/src/app/(main)/settings/teams/TeamAddForm.js
similarity index 91%
rename from src/components/pages/settings/teams/TeamAddForm.js
rename to src/app/(main)/settings/teams/TeamAddForm.js
index 7910e098..b8bb8c3a 100644
--- a/src/components/pages/settings/teams/TeamAddForm.js
+++ b/src/app/(main)/settings/teams/TeamAddForm.js
@@ -8,6 +8,7 @@ import {
Button,
SubmitButton,
} from 'react-basics';
+import { setValue } from 'store/cache';
import useApi from 'components/hooks/useApi';
import useMessages from 'components/hooks/useMessages';
@@ -20,8 +21,9 @@ export function TeamAddForm({ onSave, onClose }) {
const handleSubmit = async data => {
mutate(data, {
onSuccess: async () => {
- onSave();
- onClose();
+ setValue('teams', Date.now());
+ onSave?.();
+ onClose?.();
},
});
};
diff --git a/src/app/(main)/settings/teams/TeamDeleteButton.js b/src/app/(main)/settings/teams/TeamDeleteButton.js
new file mode 100644
index 00000000..5e4a41ea
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamDeleteButton.js
@@ -0,0 +1,25 @@
+import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import TeamDeleteForm from './TeamDeleteForm';
+
+export function TeamDeleteButton({ teamId, teamName, onDelete }) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.delete)}
+
+
+ {close => (
+
+ )}
+
+
+ );
+}
+
+export default TeamDeleteButton;
diff --git a/src/components/pages/settings/teams/TeamDeleteForm.js b/src/app/(main)/settings/teams/TeamDeleteForm.js
similarity index 89%
rename from src/components/pages/settings/teams/TeamDeleteForm.js
rename to src/app/(main)/settings/teams/TeamDeleteForm.js
index 210c8ada..9b80668a 100644
--- a/src/components/pages/settings/teams/TeamDeleteForm.js
+++ b/src/app/(main)/settings/teams/TeamDeleteForm.js
@@ -1,6 +1,7 @@
import { Button, Form, FormButtons, SubmitButton } from 'react-basics';
import useApi from 'components/hooks/useApi';
import useMessages from 'components/hooks/useMessages';
+import { setValue } from 'store/cache';
export function TeamDeleteForm({ teamId, teamName, onSave, onClose }) {
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
@@ -10,8 +11,9 @@ export function TeamDeleteForm({ teamId, teamName, onSave, onClose }) {
const handleSubmit = async data => {
mutate(data, {
onSuccess: async () => {
- onSave();
- onClose();
+ setValue('teams', Date.now());
+ onSave?.();
+ onClose?.();
},
});
};
diff --git a/src/components/pages/settings/teams/TeamJoinForm.js b/src/app/(main)/settings/teams/TeamJoinForm.js
similarity index 90%
rename from src/components/pages/settings/teams/TeamJoinForm.js
rename to src/app/(main)/settings/teams/TeamJoinForm.js
index 23abcf00..528e1d75 100644
--- a/src/components/pages/settings/teams/TeamJoinForm.js
+++ b/src/app/(main)/settings/teams/TeamJoinForm.js
@@ -10,6 +10,7 @@ import {
} from 'react-basics';
import useApi from 'components/hooks/useApi';
import useMessages from 'components/hooks/useMessages';
+import { setValue } from 'store/cache';
export function TeamJoinForm({ onSave, onClose }) {
const { formatMessage, labels, getMessage } = useMessages();
@@ -20,8 +21,9 @@ export function TeamJoinForm({ onSave, onClose }) {
const handleSubmit = async data => {
mutate(data, {
onSuccess: async () => {
- onSave();
- onClose();
+ setValue('teams:members', Date.now());
+ onSave?.();
+ onClose?.();
},
});
};
diff --git a/src/app/(main)/settings/teams/TeamLeaveButton.js b/src/app/(main)/settings/teams/TeamLeaveButton.js
new file mode 100644
index 00000000..7b98f082
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamLeaveButton.js
@@ -0,0 +1,35 @@
+import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import useLocale from 'components/hooks/useLocale';
+import useUser from 'components/hooks/useUser';
+import TeamDeleteForm from './TeamLeaveForm';
+
+export function TeamLeaveButton({ teamId, teamName, onLeave }) {
+ const { formatMessage, labels } = useMessages();
+ const { dir } = useLocale();
+ const { user } = useUser();
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.leave)}
+
+
+ {close => (
+
+ )}
+
+
+ );
+}
+
+export default TeamLeaveButton;
diff --git a/src/components/pages/settings/teams/TeamLeaveForm.js b/src/app/(main)/settings/teams/TeamLeaveForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamLeaveForm.js
rename to src/app/(main)/settings/teams/TeamLeaveForm.js
diff --git a/src/app/(main)/settings/teams/TeamsAddButton.js b/src/app/(main)/settings/teams/TeamsAddButton.js
new file mode 100644
index 00000000..b7850812
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamsAddButton.js
@@ -0,0 +1,24 @@
+import { Button, Icon, Modal, ModalTrigger, Text } from 'react-basics';
+import Icons from 'components/icons';
+import useMessages from 'components/hooks/useMessages';
+import TeamAddForm from './TeamAddForm';
+
+export function TeamsAddButton({ onAdd }) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.createTeam)}
+
+
+ {close => }
+
+
+ );
+}
+
+export default TeamsAddButton;
diff --git a/src/app/(main)/settings/teams/TeamsDataTable.js b/src/app/(main)/settings/teams/TeamsDataTable.js
new file mode 100644
index 00000000..164838f9
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamsDataTable.js
@@ -0,0 +1,26 @@
+'use client';
+import DataTable from 'components/common/DataTable';
+import TeamsTable from 'app/(main)/settings/teams/TeamsTable';
+import useApi from 'components/hooks/useApi';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import useCache from 'store/cache';
+
+export function TeamsDataTable() {
+ const { get } = useApi();
+ const modified = useCache(state => state?.teams);
+ const queryResult = useFilterQuery(['teams', { modified }], params => {
+ return get(`/teams`, {
+ ...params,
+ });
+ });
+
+ return (
+
+ {({ data }) => {
+ return ;
+ }}
+
+ );
+}
+
+export default TeamsDataTable;
diff --git a/src/app/(main)/settings/teams/TeamsHeader.js b/src/app/(main)/settings/teams/TeamsHeader.js
new file mode 100644
index 00000000..444f8703
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamsHeader.js
@@ -0,0 +1,24 @@
+'use client';
+import { Flexbox } from 'react-basics';
+import PageHeader from 'components/layout/PageHeader';
+import { ROLES } from 'lib/constants';
+import useUser from 'components/hooks/useUser';
+import useMessages from 'components/hooks/useMessages';
+import TeamsJoinButton from './TeamsJoinButton';
+import TeamsAddButton from './TeamsAddButton';
+
+export function TeamsHeader() {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ return (
+
+
+
+ {user.role !== ROLES.viewOnly && }
+
+
+ );
+}
+
+export default TeamsHeader;
diff --git a/src/app/(main)/settings/teams/TeamsJoinButton.js b/src/app/(main)/settings/teams/TeamsJoinButton.js
new file mode 100644
index 00000000..f8d2fa23
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamsJoinButton.js
@@ -0,0 +1,29 @@
+import { Button, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
+import Icons from 'components/icons';
+import useMessages from 'components/hooks/useMessages';
+import TeamJoinForm from './TeamJoinForm';
+
+export function TeamsJoinButton() {
+ const { formatMessage, labels, messages } = useMessages();
+ const { showToast } = useToasts();
+
+ const handleJoin = () => {
+ showToast({ message: formatMessage(messages.saved), variant: 'success' });
+ };
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.joinTeam)}
+
+
+ {close => }
+
+
+ );
+}
+
+export default TeamsJoinButton;
diff --git a/src/app/(main)/settings/teams/TeamsTable.js b/src/app/(main)/settings/teams/TeamsTable.js
new file mode 100644
index 00000000..1f7f1da4
--- /dev/null
+++ b/src/app/(main)/settings/teams/TeamsTable.js
@@ -0,0 +1,47 @@
+'use client';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+import { ROLES } from 'lib/constants';
+import Link from 'next/link';
+import { Button, GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from 'react-basics';
+import TeamDeleteButton from './TeamDeleteButton';
+import TeamLeaveButton from './TeamLeaveButton';
+
+export function TeamsTable({ data = [] }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+ const breakpoint = useBreakpoint();
+
+ return (
+
+
+
+ {row => row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username}
+
+
+ {row => {
+ const { id, name, teamUser } = row;
+ const owner = teamUser.find(({ role }) => role === ROLES.teamOwner);
+ const isOwner = user.id === owner?.userId;
+
+ return (
+ <>
+ {isOwner && }
+ {!isOwner && }
+
+
+
+
+
+ {formatMessage(isOwner ? labels.edit : labels.view)}
+
+
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default TeamsTable;
diff --git a/src/components/pages/settings/teams/WebsiteTags.js b/src/app/(main)/settings/teams/WebsiteTags.js
similarity index 100%
rename from src/components/pages/settings/teams/WebsiteTags.js
rename to src/app/(main)/settings/teams/WebsiteTags.js
diff --git a/src/components/pages/settings/teams/WebsiteTags.module.css b/src/app/(main)/settings/teams/WebsiteTags.module.css
similarity index 100%
rename from src/components/pages/settings/teams/WebsiteTags.module.css
rename to src/app/(main)/settings/teams/WebsiteTags.module.css
diff --git a/src/components/pages/settings/teams/TeamEditForm.js b/src/app/(main)/settings/teams/[id]/TeamEditForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamEditForm.js
rename to src/app/(main)/settings/teams/[id]/TeamEditForm.js
diff --git a/src/components/pages/settings/teams/TeamMemberRemoveButton.js b/src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.js
similarity index 88%
rename from src/components/pages/settings/teams/TeamMemberRemoveButton.js
rename to src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.js
index 3ec0f8b3..603adae3 100644
--- a/src/components/pages/settings/teams/TeamMemberRemoveButton.js
+++ b/src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.js
@@ -1,6 +1,7 @@
import useApi from 'components/hooks/useApi';
import useMessages from 'components/hooks/useMessages';
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
+import { setValue } from 'store/cache';
export function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) {
const { formatMessage, labels } = useMessages();
@@ -12,7 +13,8 @@ export function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) {
{},
{
onSuccess: () => {
- onSave();
+ setValue('team:members', Date.now());
+ onSave?.();
},
},
);
diff --git a/src/app/(main)/settings/teams/[id]/TeamMembers.js b/src/app/(main)/settings/teams/[id]/TeamMembers.js
new file mode 100644
index 00000000..55db9886
--- /dev/null
+++ b/src/app/(main)/settings/teams/[id]/TeamMembers.js
@@ -0,0 +1,29 @@
+import useApi from 'components/hooks/useApi';
+import TeamMembersTable from './TeamMembersTable';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import DataTable from 'components/common/DataTable';
+import useCache from 'store/cache';
+
+export function TeamMembers({ teamId, readOnly }) {
+ const { get } = useApi();
+ const modified = useCache(state => state?.['team:members']);
+ const queryResult = useFilterQuery(
+ ['team:members', { teamId, modified }],
+ params => {
+ return get(`/teams/${teamId}/users`, {
+ ...params,
+ });
+ },
+ { enabled: !!teamId },
+ );
+
+ return (
+ <>
+
+ {({ data }) => }
+
+ >
+ );
+}
+
+export default TeamMembers;
diff --git a/src/app/(main)/settings/teams/[id]/TeamMembersTable.js b/src/app/(main)/settings/teams/[id]/TeamMembersTable.js
new file mode 100644
index 00000000..122ce9fa
--- /dev/null
+++ b/src/app/(main)/settings/teams/[id]/TeamMembersTable.js
@@ -0,0 +1,35 @@
+import { GridColumn, GridTable } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+import { ROLES } from 'lib/constants';
+import TeamMemberRemoveButton from './TeamMemberRemoveButton';
+
+export function TeamMembersTable({ data = [], teamId, readOnly }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ const roles = {
+ [ROLES.teamOwner]: formatMessage(labels.teamOwner),
+ [ROLES.teamMember]: formatMessage(labels.teamMember),
+ };
+
+ return (
+
+
+
+ {row => roles[row?.teamUser?.[0]?.role]}
+
+
+ {row => {
+ return (
+ !readOnly &&
+ row?.teamUser?.[0]?.role !== ROLES.teamOwner &&
+ user?.id !== row?.id &&
+ );
+ }}
+
+
+ );
+}
+
+export default TeamMembersTable;
diff --git a/src/components/pages/settings/teams/TeamSettings.js b/src/app/(main)/settings/teams/[id]/TeamSettings.js
similarity index 90%
rename from src/components/pages/settings/teams/TeamSettings.js
rename to src/app/(main)/settings/teams/[id]/TeamSettings.js
index 8c4fe8f4..8ec0ad85 100644
--- a/src/components/pages/settings/teams/TeamSettings.js
+++ b/src/app/(main)/settings/teams/[id]/TeamSettings.js
@@ -1,6 +1,6 @@
+'use client';
import { useEffect, useState } from 'react';
-import { Item, Tabs, useToasts } from 'react-basics';
-import Page from 'components/layout/Page';
+import { Item, Loading, Tabs, useToasts, Flexbox } from 'react-basics';
import PageHeader from 'components/layout/PageHeader';
import { ROLES } from 'lib/constants';
import useUser from 'components/hooks/useUser';
@@ -41,8 +41,12 @@ export function TeamSettings({ teamId }) {
}
}, [data]);
+ if (isLoading || !values) {
+ return ;
+ }
+
return (
-
+
- {formatMessage(labels.details)}
@@ -54,7 +58,7 @@ export function TeamSettings({ teamId }) {
)}
{tab === 'members' && }
{tab === 'websites' && }
-
+
);
}
diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js
new file mode 100644
index 00000000..95f8490a
--- /dev/null
+++ b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js
@@ -0,0 +1,64 @@
+import useApi from 'components/hooks/useApi';
+import { useState } from 'react';
+import { Button, Form, FormButtons, GridColumn, Loading, SubmitButton, Toggle } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import WebsitesDataTable from '../../websites/WebsitesDataTable';
+import Empty from 'components/common/Empty';
+import { setValue } from 'store/cache';
+
+export function TeamWebsiteAddForm({ teamId, onSave, onClose }) {
+ const { formatMessage, labels } = useMessages();
+ const { get, post, useQuery, useMutation } = useApi();
+ const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data));
+ const { data: websites, isLoading } = useQuery(['websites'], () => get('/websites'));
+ const [selected, setSelected] = useState([]);
+ const hasData = websites && websites.data.length > 0;
+
+ const handleSubmit = () => {
+ mutate(
+ { websiteIds: selected },
+ {
+ onSuccess: async () => {
+ setValue('team:websites', Date.now());
+ onSave?.();
+ onClose?.();
+ },
+ },
+ );
+ };
+
+ const handleSelect = id => {
+ setSelected(state => (state.includes(id) ? state.filter(n => n !== id) : state.concat(id)));
+ };
+
+ return (
+ <>
+ {isLoading && !hasData && }
+ {!isLoading && !hasData && }
+ {hasData && (
+
+ )}
+ >
+ );
+}
+
+export default TeamWebsiteAddForm;
diff --git a/src/components/pages/settings/teams/TeamWebsiteRemoveButton.js b/src/app/(main)/settings/teams/[id]/TeamWebsiteRemoveButton.js
similarity index 73%
rename from src/components/pages/settings/teams/TeamWebsiteRemoveButton.js
rename to src/app/(main)/settings/teams/[id]/TeamWebsiteRemoveButton.js
index c0ddf95c..59e393e1 100644
--- a/src/components/pages/settings/teams/TeamWebsiteRemoveButton.js
+++ b/src/app/(main)/settings/teams/[id]/TeamWebsiteRemoveButton.js
@@ -7,19 +7,16 @@ export function TeamWebsiteRemoveButton({ teamId, websiteId, onSave }) {
const { del, useMutation } = useApi();
const { mutate, isLoading } = useMutation(() => del(`/teams/${teamId}/websites/${websiteId}`));
- const handleRemoveTeamMember = () => {
- mutate(
- {},
- {
- onSuccess: () => {
- onSave();
- },
+ const handleRemoveTeamMember = async () => {
+ await mutate(null, {
+ onSuccess: () => {
+ onSave();
},
- );
+ });
};
return (
- handleRemoveTeamMember()} isLoading={isLoading}>
+ handleRemoveTeamMember()} isLoading={isLoading}>
diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsites.js b/src/app/(main)/settings/teams/[id]/TeamWebsites.js
new file mode 100644
index 00000000..9e76ffab
--- /dev/null
+++ b/src/app/(main)/settings/teams/[id]/TeamWebsites.js
@@ -0,0 +1,52 @@
+import { ActionForm, Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
+import TeamWebsitesTable from './TeamWebsitesTable';
+import TeamWebsiteAddForm from './TeamWebsiteAddForm';
+import useApi from 'components/hooks/useApi';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import DataTable from 'components/common/DataTable';
+import useCache from 'store/cache';
+
+export function TeamWebsites({ teamId }) {
+ const { formatMessage, labels, messages } = useMessages();
+ const { user } = useUser();
+ const { get } = useApi();
+ const modified = useCache(state => state?.['team:websites']);
+ const queryResult = useFilterQuery(
+ ['team:websites', { teamId, modified }],
+ params => {
+ return get(`/teams/${teamId}/websites`, {
+ ...params,
+ });
+ },
+ { enabled: !!user },
+ );
+
+ const handleChange = () => {
+ queryResult.refetch();
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ {formatMessage(labels.addWebsite)}
+
+
+ {close => }
+
+
+
+
+ {({ data }) => }
+
+ >
+ );
+}
+
+export default TeamWebsites;
diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.js b/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.js
new file mode 100644
index 00000000..0f802212
--- /dev/null
+++ b/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.js
@@ -0,0 +1,42 @@
+import Link from 'next/link';
+import { Button, GridColumn, GridTable, Icon, Icons, Text } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+import TeamWebsiteRemoveButton from './TeamWebsiteRemoveButton';
+
+export function TeamWebsitesTable({ data = [], onRemove }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ return (
+
+
+
+
+ {row => {
+ const { id: teamId, teamUser } = row.teamWebsite[0].team;
+ const { id: websiteId, userId } = row;
+ const owner = teamUser[0];
+ const canRemove = user.id === userId || user.id === owner.userId;
+ return (
+ <>
+ {canRemove && (
+
+ )}
+
+
+
+
+
+ {formatMessage(labels.view)}
+
+
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default TeamWebsitesTable;
diff --git a/src/app/(main)/settings/teams/[id]/page.js b/src/app/(main)/settings/teams/[id]/page.js
new file mode 100644
index 00000000..652e65c1
--- /dev/null
+++ b/src/app/(main)/settings/teams/[id]/page.js
@@ -0,0 +1,9 @@
+import TeamSettings from './TeamSettings';
+
+export default function ({ params }) {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/app/(main)/settings/teams/page.tsx b/src/app/(main)/settings/teams/page.tsx
new file mode 100644
index 00000000..0cdb6f7d
--- /dev/null
+++ b/src/app/(main)/settings/teams/page.tsx
@@ -0,0 +1,20 @@
+import TeamsDataTable from './TeamsDataTable';
+import TeamsHeader from './TeamsHeader';
+import { Metadata } from 'next';
+
+export default function () {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export const metadata: Metadata = {
+ title: 'Teams Settings | umami',
+};
diff --git a/src/components/pages/settings/users/UserAddButton.js b/src/app/(main)/settings/users/UserAddButton.js
similarity index 100%
rename from src/components/pages/settings/users/UserAddButton.js
rename to src/app/(main)/settings/users/UserAddButton.js
diff --git a/src/components/pages/settings/users/UserAddForm.js b/src/app/(main)/settings/users/UserAddForm.js
similarity index 100%
rename from src/components/pages/settings/users/UserAddForm.js
rename to src/app/(main)/settings/users/UserAddForm.js
diff --git a/src/app/(main)/settings/users/UserDeleteButton.js b/src/app/(main)/settings/users/UserDeleteButton.js
new file mode 100644
index 00000000..22d93171
--- /dev/null
+++ b/src/app/(main)/settings/users/UserDeleteButton.js
@@ -0,0 +1,27 @@
+import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+import UserDeleteForm from './UserDeleteForm';
+
+export function UserDeleteButton({ userId, username, onDelete }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.delete)}
+
+
+ {close => (
+
+ )}
+
+
+ );
+}
+
+export default UserDeleteButton;
diff --git a/src/components/pages/settings/users/UserDeleteForm.js b/src/app/(main)/settings/users/UserDeleteForm.js
similarity index 100%
rename from src/components/pages/settings/users/UserDeleteForm.js
rename to src/app/(main)/settings/users/UserDeleteForm.js
diff --git a/src/components/pages/settings/users/UserEditForm.js b/src/app/(main)/settings/users/UserEditForm.js
similarity index 100%
rename from src/components/pages/settings/users/UserEditForm.js
rename to src/app/(main)/settings/users/UserEditForm.js
diff --git a/src/components/pages/settings/users/UserWebsites.js b/src/app/(main)/settings/users/UserWebsites.js
similarity index 90%
rename from src/components/pages/settings/users/UserWebsites.js
rename to src/app/(main)/settings/users/UserWebsites.js
index 14127275..18b5f1a7 100644
--- a/src/components/pages/settings/users/UserWebsites.js
+++ b/src/app/(main)/settings/users/UserWebsites.js
@@ -1,6 +1,6 @@
import Page from 'components/layout/Page';
import useApi from 'components/hooks/useApi';
-import WebsitesTable from 'components/pages/settings/websites/WebsitesTable';
+import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable';
import useApiFilter from 'components/hooks/useApiFilter';
export function UserWebsites({ userId }) {
@@ -22,7 +22,7 @@ export function UserWebsites({ userId }) {
{hasData && (
state?.users);
+ const queryResult = useFilterQuery(['users', { modified }], params => {
+ return get(`/users`, {
+ ...params,
+ });
+ });
+
+ return (
+ <>
+
+ {({ data }) => }
+ >
+ );
+}
+
+export default UsersDataTable;
diff --git a/src/app/(main)/settings/users/UsersHeader.js b/src/app/(main)/settings/users/UsersHeader.js
new file mode 100644
index 00000000..caf1f913
--- /dev/null
+++ b/src/app/(main)/settings/users/UsersHeader.js
@@ -0,0 +1,16 @@
+'use client';
+import PageHeader from 'components/layout/PageHeader';
+import useMessages from 'components/hooks/useMessages';
+import UserAddButton from './UserAddButton';
+
+export function UsersHeader({ onAdd }) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+
+
+ );
+}
+
+export default UsersHeader;
diff --git a/src/app/(main)/settings/users/UsersTable.js b/src/app/(main)/settings/users/UsersTable.js
new file mode 100644
index 00000000..a0b5aba1
--- /dev/null
+++ b/src/app/(main)/settings/users/UsersTable.js
@@ -0,0 +1,54 @@
+import { Button, Text, Icon, Icons, GridTable, GridColumn, useBreakpoint } from 'react-basics';
+import { formatDistance } from 'date-fns';
+import Link from 'next/link';
+import { ROLES } from 'lib/constants';
+import useMessages from 'components/hooks/useMessages';
+import useLocale from 'components/hooks/useLocale';
+import UserDeleteButton from './UserDeleteButton';
+
+export function UsersTable({ data = [] }) {
+ const { formatMessage, labels } = useMessages();
+ const { dateLocale } = useLocale();
+ const breakpoint = useBreakpoint();
+
+ return (
+
+
+
+ {row =>
+ formatMessage(
+ labels[Object.keys(ROLES).find(key => ROLES[key] === row.role)] || labels.unknown,
+ )
+ }
+
+
+ {row =>
+ formatDistance(new Date(row.createdAt), new Date(), {
+ addSuffix: true,
+ locale: dateLocale,
+ })
+ }
+
+
+ {row => {
+ const { id, username } = row;
+ return (
+ <>
+
+
+
+
+
+ {formatMessage(labels.edit)}
+
+
+
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default UsersTable;
diff --git a/src/components/pages/settings/users/UserSettings.js b/src/app/(main)/settings/users/[id]/UserSettings.js
similarity index 84%
rename from src/components/pages/settings/users/UserSettings.js
rename to src/app/(main)/settings/users/[id]/UserSettings.js
index 5fadf1a1..ea340ab7 100644
--- a/src/components/pages/settings/users/UserSettings.js
+++ b/src/app/(main)/settings/users/[id]/UserSettings.js
@@ -1,10 +1,10 @@
+'use client';
import { useEffect, useState } from 'react';
-import { Item, Tabs, useToasts } from 'react-basics';
-import UserEditForm from 'components/pages/settings/users/UserEditForm';
-import Page from 'components/layout/Page';
+import { Item, Loading, Tabs, useToasts } from 'react-basics';
+import UserEditForm from '../UserEditForm';
import PageHeader from 'components/layout/PageHeader';
import useApi from 'components/hooks/useApi';
-import UserWebsites from './UserWebsites';
+import UserWebsites from '../UserWebsites';
import useMessages from 'components/hooks/useMessages';
export function UserSettings({ userId }) {
@@ -41,8 +41,12 @@ export function UserSettings({ userId }) {
}
}, [data]);
+ if (isLoading || !values) {
+ return ;
+ }
+
return (
-
+ <>
- {formatMessage(labels.details)}
@@ -50,7 +54,7 @@ export function UserSettings({ userId }) {
{tab === 'details' && }
{tab === 'websites' && }
-
+ >
);
}
diff --git a/src/app/(main)/settings/users/[id]/page.js b/src/app/(main)/settings/users/[id]/page.js
new file mode 100644
index 00000000..7a6378aa
--- /dev/null
+++ b/src/app/(main)/settings/users/[id]/page.js
@@ -0,0 +1,9 @@
+import UserSettings from './UserSettings';
+
+export default function ({ params }) {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/app/(main)/settings/users/page.tsx b/src/app/(main)/settings/users/page.tsx
new file mode 100644
index 00000000..00ebe98c
--- /dev/null
+++ b/src/app/(main)/settings/users/page.tsx
@@ -0,0 +1,9 @@
+import UsersDataTable from './UsersDataTable';
+import { Metadata } from 'next';
+
+export default function () {
+ return ;
+}
+export const metadata: Metadata = {
+ title: 'Users | umami',
+};
diff --git a/src/app/(main)/settings/websites/WebsiteAddButton.js b/src/app/(main)/settings/websites/WebsiteAddButton.js
new file mode 100644
index 00000000..b1a69429
--- /dev/null
+++ b/src/app/(main)/settings/websites/WebsiteAddButton.js
@@ -0,0 +1,31 @@
+import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
+import WebsiteAddForm from './WebsiteAddForm';
+import useMessages from 'components/hooks/useMessages';
+import { setValue } from 'store/cache';
+
+export function WebsiteAddButton({ onSave }) {
+ const { formatMessage, labels, messages } = useMessages();
+ const { showToast } = useToasts();
+
+ const handleSave = async () => {
+ showToast({ message: formatMessage(messages.saved), variant: 'success' });
+ setValue('websites', Date.now());
+ onSave?.();
+ };
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.addWebsite)}
+
+
+ {close => }
+
+
+ );
+}
+
+export default WebsiteAddButton;
diff --git a/src/components/pages/settings/websites/WebsiteAddForm.js b/src/app/(main)/settings/websites/WebsiteAddForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteAddForm.js
rename to src/app/(main)/settings/websites/WebsiteAddForm.js
diff --git a/src/components/pages/settings/websites/WebsiteSettings.js b/src/app/(main)/settings/websites/WebsiteSettings.js
similarity index 80%
rename from src/components/pages/settings/websites/WebsiteSettings.js
rename to src/app/(main)/settings/websites/WebsiteSettings.js
index 63e89814..71d5fe23 100644
--- a/src/components/pages/settings/websites/WebsiteSettings.js
+++ b/src/app/(main)/settings/websites/WebsiteSettings.js
@@ -1,13 +1,13 @@
+'use client';
import { useEffect, useState } from 'react';
import { Item, Tabs, useToasts, Button, Text, Icon, Icons } from 'react-basics';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import Link from 'next/link';
-import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
-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 WebsiteEditForm from './[id]/WebsiteEditForm';
+import WebsiteData from './[id]/WebsiteData';
+import TrackingCode from './[id]/TrackingCode';
+import ShareUrl from './[id]/ShareUrl';
import useApi from 'components/hooks/useApi';
import useMessages from 'components/hooks/useMessages';
@@ -16,11 +16,10 @@ export function WebsiteSettings({ websiteId, openExternal = false, analyticsUrl
const { formatMessage, labels, messages } = useMessages();
const { get, useQuery } = useApi();
const { showToast } = useToasts();
- const { data, isLoading } = useQuery(
- ['website', websiteId],
- () => get(`/websites/${websiteId}`),
- { enabled: !!websiteId, cacheTime: 0 },
- );
+ const { data } = useQuery(['website', websiteId], () => get(`/websites/${websiteId}`), {
+ enabled: !!websiteId,
+ cacheTime: 0,
+ });
const [values, setValues] = useState(null);
const [tab, setTab] = useState('details');
@@ -48,7 +47,7 @@ export function WebsiteSettings({ websiteId, openExternal = false, analyticsUrl
}, [data]);
return (
-
+ <>
@@ -78,7 +77,7 @@ export function WebsiteSettings({ websiteId, openExternal = false, analyticsUrl
/>
)}
{tab === 'data' && }
-
+ >
);
}
diff --git a/src/components/pages/websites/WebsiteList.module.css b/src/app/(main)/settings/websites/Websites.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteList.module.css
rename to src/app/(main)/settings/websites/Websites.module.css
diff --git a/src/app/(main)/settings/websites/WebsitesDataTable.tsx b/src/app/(main)/settings/websites/WebsitesDataTable.tsx
new file mode 100644
index 00000000..441ae56d
--- /dev/null
+++ b/src/app/(main)/settings/websites/WebsitesDataTable.tsx
@@ -0,0 +1,66 @@
+'use client';
+import { ReactNode } from 'react';
+import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable';
+import useUser from 'components/hooks/useUser';
+import useApi from 'components/hooks/useApi';
+import DataTable from 'components/common/DataTable';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import useCache from 'store/cache';
+
+export interface WebsitesDataTableProps {
+ allowEdit?: boolean;
+ allowView?: boolean;
+ showActions?: boolean;
+ showTeam?: boolean;
+ includeTeams?: boolean;
+ onlyTeams?: boolean;
+ children?: ReactNode;
+}
+
+function useWebsites({ includeTeams, onlyTeams }) {
+ const { user } = useUser();
+ const { get } = useApi();
+ const modified = useCache((state: any) => state?.websites);
+
+ return useFilterQuery(
+ ['websites', { includeTeams, onlyTeams, modified }],
+ (params: any) => {
+ return get(`/users/${user?.id}/websites`, {
+ includeTeams,
+ onlyTeams,
+ ...params,
+ });
+ },
+ { enabled: !!user },
+ );
+}
+
+export function WebsitesDataTable({
+ allowEdit = true,
+ allowView = true,
+ showActions = true,
+ showTeam,
+ includeTeams,
+ onlyTeams,
+ children,
+}: WebsitesDataTableProps) {
+ const queryResult = useWebsites({ includeTeams, onlyTeams });
+
+ return (
+
+ {({ data }) => (
+
+ {children}
+
+ )}
+
+ );
+}
+
+export default WebsitesDataTable;
diff --git a/src/app/(main)/settings/websites/WebsitesHeader.js b/src/app/(main)/settings/websites/WebsitesHeader.js
new file mode 100644
index 00000000..61bfb886
--- /dev/null
+++ b/src/app/(main)/settings/websites/WebsitesHeader.js
@@ -0,0 +1,16 @@
+'use client';
+import useMessages from 'components/hooks/useMessages';
+import PageHeader from 'components/layout/PageHeader';
+import WebsiteAddButton from './WebsiteAddButton';
+
+export function WebsitesHeader({ showActions = true }) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+ {!process.env.cloudMode && showActions && }
+
+ );
+}
+
+export default WebsitesHeader;
diff --git a/src/app/(main)/settings/websites/WebsitesTable.js b/src/app/(main)/settings/websites/WebsitesTable.js
new file mode 100644
index 00000000..eef3f7d4
--- /dev/null
+++ b/src/app/(main)/settings/websites/WebsitesTable.js
@@ -0,0 +1,72 @@
+import Link from 'next/link';
+import { Button, Text, Icon, Icons, GridTable, GridColumn, useBreakpoint } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+
+export function WebsitesTable({
+ data = [],
+ showTeam,
+ showActions,
+ allowEdit,
+ allowView,
+ children,
+}) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+ const breakpoint = useBreakpoint();
+
+ return (
+
+
+
+ {showTeam && (
+
+ {row => row.teamWebsite[0]?.team.name}
+
+ )}
+ {showTeam && (
+
+ {row => row.user.username}
+
+ )}
+ {showActions && (
+
+ {row => {
+ const {
+ id,
+ user: { id: ownerId },
+ } = row;
+
+ return (
+ <>
+ {allowEdit && (!showTeam || ownerId === user.id) && (
+
+
+
+
+
+ {formatMessage(labels.edit)}
+
+
+ )}
+ {allowView && (
+
+
+
+
+
+ {formatMessage(labels.view)}
+
+
+ )}
+ >
+ );
+ }}
+
+ )}
+ {children}
+
+ );
+}
+
+export default WebsitesTable;
diff --git a/src/components/pages/settings/websites/WebsitesTable.module.css b/src/app/(main)/settings/websites/WebsitesTable.module.css
similarity index 100%
rename from src/components/pages/settings/websites/WebsitesTable.module.css
rename to src/app/(main)/settings/websites/WebsitesTable.module.css
diff --git a/src/components/pages/settings/websites/ShareUrl.js b/src/app/(main)/settings/websites/[id]/ShareUrl.js
similarity index 91%
rename from src/components/pages/settings/websites/ShareUrl.js
rename to src/app/(main)/settings/websites/[id]/ShareUrl.js
index f4569ca3..72ba217c 100644
--- a/src/components/pages/settings/websites/ShareUrl.js
+++ b/src/app/(main)/settings/websites/[id]/ShareUrl.js
@@ -10,7 +10,6 @@ import {
} from 'react-basics';
import { useEffect, useMemo, useRef, useState } from 'react';
import { getRandomChars } from 'next-basics';
-import { useRouter } from 'next/router';
import useApi from 'components/hooks/useApi';
import useMessages from 'components/hooks/useMessages';
@@ -21,14 +20,16 @@ export function ShareUrl({ websiteId, data, analyticsUrl, onSave }) {
const { name, shareId } = data;
const [id, setId] = useState(shareId);
const { post, useMutation } = useApi();
- const { basePath } = useRouter();
const { mutate, error } = useMutation(({ shareId }) =>
post(`/websites/${websiteId}`, { shareId }),
);
const ref = useRef(null);
const url = useMemo(
- () => `${analyticsUrl || location.origin}${basePath}/share/${id}/${encodeURIComponent(name)}`,
- [id, name, basePath],
+ () =>
+ `${analyticsUrl || location.origin}${process.env.basePath}/share/${id}/${encodeURIComponent(
+ name,
+ )}`,
+ [id, name],
);
const handleSubmit = async data => {
diff --git a/src/components/pages/settings/websites/TrackingCode.js b/src/app/(main)/settings/websites/[id]/TrackingCode.js
similarity index 82%
rename from src/components/pages/settings/websites/TrackingCode.js
rename to src/app/(main)/settings/websites/[id]/TrackingCode.js
index 298cd17a..368368d7 100644
--- a/src/components/pages/settings/websites/TrackingCode.js
+++ b/src/app/(main)/settings/websites/[id]/TrackingCode.js
@@ -1,11 +1,9 @@
import { TextArea } from 'react-basics';
import useMessages from 'components/hooks/useMessages';
import useConfig from 'components/hooks/useConfig';
-import { useRouter } from 'next/router';
export function TrackingCode({ websiteId, analyticsUrl }) {
const { formatMessage, messages } = useMessages();
- const { basePath } = useRouter();
const config = useConfig();
const trackerScriptName =
@@ -13,7 +11,7 @@ export function TrackingCode({ websiteId, analyticsUrl }) {
const url = trackerScriptName?.startsWith('http')
? trackerScriptName
- : `${analyticsUrl || location.origin}${basePath}/${trackerScriptName}`;
+ : `${analyticsUrl || location.origin}${process.env.basePath}/${trackerScriptName}`;
const code = ``;
diff --git a/src/components/pages/settings/websites/WebsiteData.js b/src/app/(main)/settings/websites/[id]/WebsiteData.js
similarity index 89%
rename from src/components/pages/settings/websites/WebsiteData.js
rename to src/app/(main)/settings/websites/[id]/WebsiteData.js
index 08d6702e..07dc9257 100644
--- a/src/components/pages/settings/websites/WebsiteData.js
+++ b/src/app/(main)/settings/websites/[id]/WebsiteData.js
@@ -1,6 +1,6 @@
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 WebsiteDeleteForm from './WebsiteDeleteForm';
+import WebsiteResetForm from './WebsiteResetForm';
import useMessages from 'components/hooks/useMessages';
export function WebsiteData({ websiteId, onSave }) {
diff --git a/src/components/pages/settings/websites/WebsiteDeleteForm.js b/src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteDeleteForm.js
rename to src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.js
diff --git a/src/components/pages/settings/websites/WebsiteEditForm.js b/src/app/(main)/settings/websites/[id]/WebsiteEditForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteEditForm.js
rename to src/app/(main)/settings/websites/[id]/WebsiteEditForm.js
diff --git a/src/components/pages/settings/websites/WebsiteResetForm.js b/src/app/(main)/settings/websites/[id]/WebsiteResetForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteResetForm.js
rename to src/app/(main)/settings/websites/[id]/WebsiteResetForm.js
diff --git a/src/app/(main)/settings/websites/[id]/page.js b/src/app/(main)/settings/websites/[id]/page.js
new file mode 100644
index 00000000..37324659
--- /dev/null
+++ b/src/app/(main)/settings/websites/[id]/page.js
@@ -0,0 +1,9 @@
+import WebsiteSettings from '../WebsiteSettings';
+
+export default async function WebsiteSettingsPage({ params: { id } }) {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/app/(main)/settings/websites/page.tsx b/src/app/(main)/settings/websites/page.tsx
new file mode 100644
index 00000000..2c83dce0
--- /dev/null
+++ b/src/app/(main)/settings/websites/page.tsx
@@ -0,0 +1,16 @@
+import WebsitesDataTable from './WebsitesDataTable';
+import WebsitesHeader from './WebsitesHeader';
+import { Metadata } from 'next';
+
+export default function () {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export const metadata: Metadata = {
+ title: 'Websites Settings | umami',
+};
diff --git a/src/app/(main)/websites/WebsiteTableView.js b/src/app/(main)/websites/WebsiteTableView.js
new file mode 100644
index 00000000..7c71b84b
--- /dev/null
+++ b/src/app/(main)/websites/WebsiteTableView.js
@@ -0,0 +1,41 @@
+import { useState } from 'react';
+import { Grid, GridRow } from 'components/layout/Grid';
+import PagesTable from 'components/metrics/PagesTable';
+import ReferrersTable from 'components/metrics/ReferrersTable';
+import BrowsersTable from 'components/metrics/BrowsersTable';
+import OSTable from 'components/metrics/OSTable';
+import DevicesTable from 'components/metrics/DevicesTable';
+import WorldMap from 'components/common/WorldMap';
+import CountriesTable from 'components/metrics/CountriesTable';
+import EventsTable from 'components/metrics/EventsTable';
+import EventsChart from 'components/metrics/EventsChart';
+
+export default function WebsiteTableView({ websiteId }) {
+ const [countryData, setCountryData] = useState();
+ const tableProps = {
+ websiteId,
+ limit: 10,
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/websites/WebsitesBrowse.js b/src/app/(main)/websites/WebsitesBrowse.js
new file mode 100644
index 00000000..f1bab7bf
--- /dev/null
+++ b/src/app/(main)/websites/WebsitesBrowse.js
@@ -0,0 +1,31 @@
+'use client';
+import WebsitesDataTable from '../settings/websites/WebsitesDataTable';
+import { useMessages } from 'components/hooks';
+import { useState } from 'react';
+import { Item, Tabs } from 'react-basics';
+
+const TABS = {
+ myWebsites: 'my-websites',
+ teamWebsites: 'team-websites',
+};
+
+export function WebsitesBrowse() {
+ const { formatMessage, labels } = useMessages();
+ const [tab, setTab] = useState(TABS.myWebsites);
+ const allowEdit = !process.env.cloudMode;
+
+ return (
+ <>
+
+ - {formatMessage(labels.myWebsites)}
+ - {formatMessage(labels.teamWebsites)}
+
+ {tab === TABS.myWebsites && }
+ {tab === TABS.teamWebsites && (
+
+ )}
+ >
+ );
+}
+
+export default WebsitesBrowse;
diff --git a/src/components/pages/websites/WebsiteChart.js b/src/app/(main)/websites/[id]/WebsiteChart.js
similarity index 92%
rename from src/components/pages/websites/WebsiteChart.js
rename to src/app/(main)/websites/[id]/WebsiteChart.js
index 7e20e785..d05ff422 100644
--- a/src/components/pages/websites/WebsiteChart.js
+++ b/src/app/(main)/websites/[id]/WebsiteChart.js
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import PageviewsChart from 'components/metrics/PageviewsChart';
-import { useApi, useDateRange, useTimezone, usePageQuery } from 'components/hooks';
+import { useApi, useDateRange, useTimezone, useNavigation } from 'components/hooks';
import { getDateArray } from 'lib/date';
export function WebsiteChart({ websiteId }) {
@@ -9,7 +9,7 @@ export function WebsiteChart({ websiteId }) {
const [timezone] = useTimezone();
const {
query: { url, referrer, os, browser, device, country, region, city, title },
- } = usePageQuery();
+ } = useNavigation();
const { get, useQuery } = useApi();
const { data, isLoading } = useQuery(
diff --git a/src/components/pages/websites/WebsiteChart.module.css b/src/app/(main)/websites/[id]/WebsiteChart.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteChart.module.css
rename to src/app/(main)/websites/[id]/WebsiteChart.module.css
diff --git a/src/components/pages/websites/WebsiteChartList.js b/src/app/(main)/websites/[id]/WebsiteChartList.js
similarity index 90%
rename from src/components/pages/websites/WebsiteChartList.js
rename to src/app/(main)/websites/[id]/WebsiteChartList.js
index 56cbe157..23764dbb 100644
--- a/src/components/pages/websites/WebsiteChartList.js
+++ b/src/app/(main)/websites/[id]/WebsiteChartList.js
@@ -2,9 +2,8 @@ import { Button, Text, Icon } from 'react-basics';
import { useMemo } from 'react';
import { firstBy } from 'thenby';
import Link from 'next/link';
-import WebsiteChart from 'components/pages/websites/WebsiteChart';
+import WebsiteChart from './WebsiteChart';
import useDashboard from 'store/dashboard';
-import styles from './WebsiteList.module.css';
import WebsiteHeader from './WebsiteHeader';
import { WebsiteMetricsBar } from './WebsiteMetricsBar';
import { useMessages, useLocale } from 'components/hooks';
@@ -27,7 +26,7 @@ export default function WebsiteChartList({ websites, showCharts, limit }) {
{ordered.map(({ id }, index) => {
return index < limit ? (
-
+
diff --git a/src/components/pages/websites/WebsiteDetailsPage.js b/src/app/(main)/websites/[id]/WebsiteDetails.js
similarity index 69%
rename from src/components/pages/websites/WebsiteDetailsPage.js
rename to src/app/(main)/websites/[id]/WebsiteDetails.js
index 222d94d9..78996b78 100644
--- a/src/components/pages/websites/WebsiteDetailsPage.js
+++ b/src/app/(main)/websites/[id]/WebsiteDetails.js
@@ -1,26 +1,31 @@
+'use client';
import { Loading } from 'react-basics';
-import { useRouter } from 'next/router';
+import { usePathname } from 'next/navigation';
import Page from 'components/layout/Page';
-import WebsiteChart from 'components/pages/websites/WebsiteChart';
import FilterTags from 'components/metrics/FilterTags';
-import usePageQuery from 'components/hooks/usePageQuery';
-import WebsiteTableView from './WebsiteTableView';
-import WebsiteMenuView from './WebsiteMenuView';
+import useNavigation from 'components/hooks/useNavigation';
import { useWebsite } from 'components/hooks';
+import WebsiteChart from './WebsiteChart';
+import WebsiteMenuView from './WebsiteMenuView';
import WebsiteHeader from './WebsiteHeader';
-import { WebsiteMetricsBar } from './WebsiteMetricsBar';
+import WebsiteMetricsBar from './WebsiteMetricsBar';
+import WebsiteTableView from '../WebsiteTableView';
-export default function WebsiteDetailsPage({ websiteId }) {
+export default function WebsiteDetails({ websiteId }) {
const { data: website, isLoading, error } = useWebsite(websiteId);
- const { pathname } = useRouter();
+ const pathname = usePathname();
const showLinks = !pathname.includes('/share/');
const {
query: { view, url, referrer, os, browser, device, country, region, city, title },
- } = usePageQuery();
+ } = useNavigation();
+
+ if (isLoading || error) {
+ return ;
+ }
return (
-
+ <>
}
>
)}
-
+ >
);
}
diff --git a/src/components/pages/websites/WebsiteHeader.js b/src/app/(main)/websites/[id]/WebsiteHeader.js
similarity index 80%
rename from src/components/pages/websites/WebsiteHeader.js
rename to src/app/(main)/websites/[id]/WebsiteHeader.js
index fb4e0986..bf34a253 100644
--- a/src/components/pages/websites/WebsiteHeader.js
+++ b/src/app/(main)/websites/[id]/WebsiteHeader.js
@@ -1,7 +1,8 @@
+'use client';
import classNames from 'classnames';
-import { Row, Column, Text, Button, Icon } from 'react-basics';
+import { Text, Button, Icon } from 'react-basics';
import Link from 'next/link';
-import { useRouter } from 'next/router';
+import { usePathname } from 'next/navigation';
import Favicon from 'components/common/Favicon';
import ActiveUsers from 'components/metrics/ActiveUsers';
import Icons from 'components/icons';
@@ -10,7 +11,7 @@ import styles from './WebsiteHeader.module.css';
export function WebsiteHeader({ websiteId, showLinks = true, children }) {
const { formatMessage, labels } = useMessages();
- const { pathname } = useRouter();
+ const pathname = usePathname();
const { data: website } = useWebsite(websiteId);
const { name, domain } = website || {};
@@ -38,17 +39,19 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) {
];
return (
-
-
+
+
+
{showLinks && (
{links.map(({ label, icon, path }) => {
- const selected = path ? pathname.endsWith(path) : pathname === '/websites/[id]';
+ const selected = path
+ ? pathname.endsWith(path)
+ : pathname.match(/^\/websites\/[\w-]+$/);
return (
@@ -67,8 +70,8 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) {
)}
{children}
-
-
+
+
);
}
diff --git a/src/components/pages/websites/WebsiteHeader.module.css b/src/app/(main)/websites/[id]/WebsiteHeader.module.css
similarity index 83%
rename from src/components/pages/websites/WebsiteHeader.module.css
rename to src/app/(main)/websites/[id]/WebsiteHeader.module.css
index 93e622d9..3e58c8a3 100644
--- a/src/components/pages/websites/WebsiteHeader.module.css
+++ b/src/app/(main)/websites/[id]/WebsiteHeader.module.css
@@ -1,6 +1,6 @@
.header {
- display: flex;
- flex-direction: row;
+ display: grid;
+ grid-template-columns: 1fr max-content;
align-items: center;
}
@@ -35,6 +35,10 @@
}
@media only screen and (max-width: 768px) {
+ .header {
+ grid-template-columns: 1fr;
+ }
+
.links {
justify-content: space-evenly;
flex: 1;
@@ -49,7 +53,7 @@
.icon,
.icon svg {
- width: 30px;
- height: 30px;
+ width: 20px;
+ height: 20px;
}
}
diff --git a/src/components/pages/websites/WebsiteMenuView.js b/src/app/(main)/websites/[id]/WebsiteMenuView.js
similarity index 64%
rename from src/components/pages/websites/WebsiteMenuView.js
rename to src/app/(main)/websites/[id]/WebsiteMenuView.js
index 8c74d615..51c5e8c7 100644
--- a/src/components/pages/websites/WebsiteMenuView.js
+++ b/src/app/(main)/websites/[id]/WebsiteMenuView.js
@@ -1,6 +1,4 @@
-import { Icon, Button, Flexbox, Text } from 'react-basics';
-import Link from 'next/link';
-import { GridRow, GridColumn } from 'components/layout/Grid';
+import { Text } from 'react-basics';
import BrowsersTable from 'components/metrics/BrowsersTable';
import CountriesTable from 'components/metrics/CountriesTable';
import RegionsTable from 'components/metrics/RegionsTable';
@@ -13,12 +11,11 @@ import QueryParametersTable from 'components/metrics/QueryParametersTable';
import ReferrersTable from 'components/metrics/ReferrersTable';
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 'components/hooks/usePageQuery';
+import useNavigation from 'components/hooks/useNavigation';
import useMessages from 'components/hooks/useMessages';
import styles from './WebsiteMenuView.module.css';
-import useLocale from 'components/hooks/useLocale';
+import LinkButton from 'components/common/LinkButton';
const views = {
url: PagesTable,
@@ -38,93 +35,86 @@ const views = {
export default function WebsiteMenuView({ websiteId, websiteDomain }) {
const { formatMessage, labels } = useMessages();
- const { dir } = useLocale();
const {
- resolveUrl,
+ makeUrl,
+ pathname,
query: { view },
- } = usePageQuery();
+ } = useNavigation();
const items = [
{
key: 'url',
label: formatMessage(labels.pages),
- url: resolveUrl({ view: 'url' }),
+ url: makeUrl({ view: 'url' }),
},
{
key: 'referrer',
label: formatMessage(labels.referrers),
- url: resolveUrl({ view: 'referrer' }),
+ url: makeUrl({ view: 'referrer' }),
},
{
key: 'browser',
label: formatMessage(labels.browsers),
- url: resolveUrl({ view: 'browser' }),
+ url: makeUrl({ view: 'browser' }),
},
{
key: 'os',
label: formatMessage(labels.os),
- url: resolveUrl({ view: 'os' }),
+ url: makeUrl({ view: 'os' }),
},
{
key: 'device',
label: formatMessage(labels.devices),
- url: resolveUrl({ view: 'device' }),
+ url: makeUrl({ view: 'device' }),
},
{
key: 'country',
label: formatMessage(labels.countries),
- url: resolveUrl({ view: 'country' }),
+ url: makeUrl({ view: 'country' }),
},
{
key: 'region',
label: formatMessage(labels.regions),
- url: resolveUrl({ view: 'region' }),
+ url: makeUrl({ view: 'region' }),
},
{
key: 'city',
label: formatMessage(labels.cities),
- url: resolveUrl({ view: 'city' }),
+ url: makeUrl({ view: 'city' }),
},
{
key: 'language',
label: formatMessage(labels.languages),
- url: resolveUrl({ view: 'language' }),
+ url: makeUrl({ view: 'language' }),
},
{
key: 'screen',
label: formatMessage(labels.screens),
- url: resolveUrl({ view: 'screen' }),
+ url: makeUrl({ view: 'screen' }),
},
{
key: 'event',
label: formatMessage(labels.events),
- url: resolveUrl({ view: 'event' }),
+ url: makeUrl({ view: 'event' }),
},
{
key: 'query',
label: formatMessage(labels.queryParameters),
- url: resolveUrl({ view: 'query' }),
+ url: makeUrl({ view: 'query' }),
},
];
const DetailsComponent = views[view] || (() => null);
return (
-
-
-
-
-
-
-
-
- {formatMessage(labels.back)}
-
-
-
+
+
+
+ {formatMessage(labels.back)}
+
-
-
+
+
-
-
+
+
);
}
diff --git a/src/app/(main)/websites/[id]/WebsiteMenuView.module.css b/src/app/(main)/websites/[id]/WebsiteMenuView.module.css
new file mode 100644
index 00000000..5710ac73
--- /dev/null
+++ b/src/app/(main)/websites/[id]/WebsiteMenuView.module.css
@@ -0,0 +1,25 @@
+.layout {
+ display: grid;
+ grid-template-columns: 300px 1fr;
+ border-top: 1px solid var(--base300);
+}
+
+.menu {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ padding: 20px 20px 20px 0;
+}
+
+.back {
+ display: inline-flex;
+ align-items: center;
+ align-self: center;
+ margin-bottom: 20px;
+}
+
+.content {
+ min-height: 800px;
+ padding: 20px 0 20px 20px;
+ border-left: 1px solid var(--base300);
+}
diff --git a/src/components/pages/websites/WebsiteMetricsBar.js b/src/app/(main)/websites/[id]/WebsiteMetricsBar.js
similarity index 55%
rename from src/components/pages/websites/WebsiteMetricsBar.js
rename to src/app/(main)/websites/[id]/WebsiteMetricsBar.js
index 7ba4a801..3fba284e 100644
--- a/src/components/pages/websites/WebsiteMetricsBar.js
+++ b/src/app/(main)/websites/[id]/WebsiteMetricsBar.js
@@ -1,12 +1,12 @@
import classNames from 'classnames';
-import { useApi, useDateRange, useMessages, usePageQuery, useSticky } from 'components/hooks';
+import { useApi, useDateRange, useMessages, useNavigation, useSticky } from 'components/hooks';
import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
import MetricCard from 'components/metrics/MetricCard';
import MetricsBar from 'components/metrics/MetricsBar';
-import FilterSelectForm from 'components/pages/reports/FilterSelectForm';
-import PopupForm from 'components/pages/reports/PopupForm';
+import FilterSelectForm from 'app/(main)/reports/[id]/FilterSelectForm';
+import PopupForm from 'app/(main)/reports/[id]/PopupForm';
import { formatShortTime } from 'lib/format';
-import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row } from 'react-basics';
+import { Button, Icon, Icons, Popup, PopupTrigger } from 'react-basics';
import styles from './WebsiteMetricsBar.module.css';
export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
@@ -17,10 +17,10 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
const { startDate, endDate, modified } = dateRange;
const { ref, isSticky } = useSticky({ enabled: sticky });
const {
- resolveUrl,
+ makeUrl,
router,
query: { url, referrer, title, os, browser, device, country, region, city },
- } = usePageQuery();
+ } = useNavigation();
const { data, error, isLoading, isFetched } = useQuery(
[
@@ -64,7 +64,7 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
};
const handleAddFilter = ({ name, value }) => {
- router.push(resolveUrl({ [name]: value }));
+ router.push(makeUrl({ [name]: value }));
};
const WebsiteFilterButton = () => {
@@ -98,72 +98,64 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
};
return (
-
-
-
- {!error && isFetched && (
- <>
-
-
- Number(n).toFixed(0) + '%'}
- reverseColors
- />
-
- `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`
- }
- />
- >
- )}
-
-
-
-
- {showFilter && }
-
-
-
-
+
+ {pageviews && uniques && (
+ <>
+
+
+ Number(n).toFixed(0) + '%'}
+ reverseColors
+ />
+ `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`}
+ />
+ >
+ )}
+
+
+ {showFilter && }
+
+
+
);
}
diff --git a/src/components/pages/websites/WebsiteMetricsBar.module.css b/src/app/(main)/websites/[id]/WebsiteMetricsBar.module.css
similarity index 70%
rename from src/components/pages/websites/WebsiteMetricsBar.module.css
rename to src/app/(main)/websites/[id]/WebsiteMetricsBar.module.css
index 52decfc6..4d642d70 100644
--- a/src/components/pages/websites/WebsiteMetricsBar.module.css
+++ b/src/app/(main)/websites/[id]/WebsiteMetricsBar.module.css
@@ -1,12 +1,12 @@
.container {
- display: flex;
+ display: grid;
+ grid-template-columns: 1fr max-content;
justify-content: space-between;
align-items: center;
- padding: 10px 0;
- min-height: 90px;
- margin-bottom: 20px;
background: var(--base50);
z-index: var(--z-index-above);
+ min-height: 120px;
+ padding-bottom: 20px;
}
.actions {
@@ -18,8 +18,12 @@
}
@media only screen and (max-width: 1200px) {
+ .container {
+ grid-template-columns: 1fr;
+ }
+
.actions {
- margin-top: 40px;
+ margin: 20px 0;
}
}
@@ -30,6 +34,7 @@
}
.isSticky {
+ padding: 10px 0;
border-bottom: 1px solid var(--base300);
}
}
diff --git a/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js
new file mode 100644
index 00000000..5be19185
--- /dev/null
+++ b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js
@@ -0,0 +1,38 @@
+import { useApi, useDateRange } from 'components/hooks';
+import MetricCard from 'components/metrics/MetricCard';
+import useMessages from 'components/hooks/useMessages';
+import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
+import MetricsBar from 'components/metrics/MetricsBar';
+import styles from './EventDataMetricsBar.module.css';
+
+export function EventDataMetricsBar({ websiteId }) {
+ const { formatMessage, labels } = useMessages();
+ const { get, useQuery } = useApi();
+ const [dateRange] = useDateRange(websiteId);
+ const { startDate, endDate, modified } = dateRange;
+
+ const { data, error, isLoading, isFetched } = useQuery(
+ ['event-data:stats', { websiteId, startDate, endDate, modified }],
+ () =>
+ get(`/event-data/stats`, {
+ websiteId,
+ startAt: +startDate,
+ endAt: +endDate,
+ }),
+ );
+
+ return (
+
+ );
+}
+
+export default EventDataMetricsBar;
diff --git a/src/components/pages/event-data/EventDataMetricsBar.module.css b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.module.css
similarity index 52%
rename from src/components/pages/event-data/EventDataMetricsBar.module.css
rename to src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.module.css
index 43b14580..408396c3 100644
--- a/src/components/pages/event-data/EventDataMetricsBar.module.css
+++ b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.module.css
@@ -1,5 +1,6 @@
.container {
- display: flex;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
justify-content: space-between;
align-items: center;
padding: 10px 0;
@@ -9,12 +10,6 @@
z-index: var(--z-index-above);
}
-.metrics {
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
.actions {
display: flex;
flex-direction: row;
@@ -23,24 +18,9 @@
flex: 1;
}
-.bar {
- display: flex;
- cursor: pointer;
- min-height: 110px;
- gap: 20px;
- flex-wrap: wrap;
-}
-
-.card {
- justify-self: flex-start;
-}
-
@media only screen and (max-width: 992px) {
- .card {
- flex-basis: calc(50% - 20px);
+ .container {
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr 1fr;
}
}
-
-.row {
- border-bottom: 1px solid var(--border-color);
-}
diff --git a/src/components/pages/event-data/EventDataTable.js b/src/app/(main)/websites/[id]/event-data/EventDataTable.js
similarity index 84%
rename from src/components/pages/event-data/EventDataTable.js
rename to src/app/(main)/websites/[id]/event-data/EventDataTable.js
index c79916ce..fb98e7e7 100644
--- a/src/components/pages/event-data/EventDataTable.js
+++ b/src/app/(main)/websites/[id]/event-data/EventDataTable.js
@@ -1,12 +1,12 @@
import Link from 'next/link';
import { GridTable, GridColumn } from 'react-basics';
-import { useMessages, usePageQuery } from 'components/hooks';
+import { useMessages, useNavigation } from 'components/hooks';
import Empty from 'components/common/Empty';
import { DATA_TYPES } from 'lib/constants';
export function EventDataTable({ data = [] }) {
const { formatMessage, labels } = useMessages();
- const { resolveUrl } = usePageQuery();
+ const { makeUrl } = useNavigation();
if (data.length === 0) {
return
;
@@ -16,7 +16,7 @@ export function EventDataTable({ data = [] }) {
{row => (
-
+
{row.eventName}
)}
diff --git a/src/components/pages/event-data/EventDataValueTable.js b/src/app/(main)/websites/[id]/event-data/EventDataValueTable.js
similarity index 89%
rename from src/components/pages/event-data/EventDataValueTable.js
rename to src/app/(main)/websites/[id]/event-data/EventDataValueTable.js
index 75c11e32..4e50f5b9 100644
--- a/src/components/pages/event-data/EventDataValueTable.js
+++ b/src/app/(main)/websites/[id]/event-data/EventDataValueTable.js
@@ -1,5 +1,5 @@
import { GridTable, GridColumn, Button, Icon, Text } from 'react-basics';
-import { useMessages, usePageQuery } from 'components/hooks';
+import { useMessages, useNavigation } from 'components/hooks';
import Link from 'next/link';
import Icons from 'components/icons';
import PageHeader from 'components/layout/PageHeader';
@@ -8,12 +8,12 @@ import { DATA_TYPES } from 'lib/constants';
export function EventDataValueTable({ data = [], event }) {
const { formatMessage, labels } = useMessages();
- const { resolveUrl } = usePageQuery();
+ const { makeUrl } = useNavigation();
const Title = () => {
return (
<>
-
+
diff --git a/src/components/pages/websites/WebsiteEventData.js b/src/app/(main)/websites/[id]/event-data/WebsiteEventData.js
similarity index 76%
rename from src/components/pages/websites/WebsiteEventData.js
rename to src/app/(main)/websites/[id]/event-data/WebsiteEventData.js
index d38ca1ad..b5982e32 100644
--- a/src/components/pages/websites/WebsiteEventData.js
+++ b/src/app/(main)/websites/[id]/event-data/WebsiteEventData.js
@@ -1,8 +1,9 @@
+'use client';
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 'components/hooks';
+import EventDataTable from './EventDataTable';
+import EventDataValueTable from './EventDataValueTable';
+import { EventDataMetricsBar } from './EventDataMetricsBar';
+import { useDateRange, useApi, useNavigation } from 'components/hooks';
import styles from './WebsiteEventData.module.css';
function useData(websiteId, event) {
@@ -27,7 +28,7 @@ function useData(websiteId, event) {
export default function WebsiteEventData({ websiteId }) {
const {
query: { event },
- } = usePageQuery();
+ } = useNavigation();
const { data, isLoading } = useData(websiteId, event);
return (
diff --git a/src/components/pages/websites/WebsiteEventData.module.css b/src/app/(main)/websites/[id]/event-data/WebsiteEventData.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteEventData.module.css
rename to src/app/(main)/websites/[id]/event-data/WebsiteEventData.module.css
diff --git a/src/app/(main)/websites/[id]/event-data/page.js b/src/app/(main)/websites/[id]/event-data/page.js
new file mode 100644
index 00000000..14e878c6
--- /dev/null
+++ b/src/app/(main)/websites/[id]/event-data/page.js
@@ -0,0 +1,15 @@
+import WebsiteHeader from '../WebsiteHeader';
+import WebsiteEventData from './WebsiteEventData';
+
+export default function WebsiteEventDataPage({ params: { id } }) {
+ if (!id) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/app/(main)/websites/[id]/page.tsx b/src/app/(main)/websites/[id]/page.tsx
new file mode 100644
index 00000000..4b7d6ff3
--- /dev/null
+++ b/src/app/(main)/websites/[id]/page.tsx
@@ -0,0 +1,5 @@
+import WebsiteDetails from './WebsiteDetails';
+
+export default function WebsiteReportsPage({ params: { id } }) {
+ return ;
+}
diff --git a/src/components/pages/realtime/RealtimePage.js b/src/app/(main)/websites/[id]/realtime/Realtime.js
similarity index 75%
rename from src/components/pages/realtime/RealtimePage.js
rename to src/app/(main)/websites/[id]/realtime/Realtime.js
index f26a2869..b4219b0a 100644
--- a/src/components/pages/realtime/RealtimePage.js
+++ b/src/app/(main)/websites/[id]/realtime/Realtime.js
@@ -1,20 +1,21 @@
-import { useState, useEffect, useMemo } from 'react';
+'use client';
+import { useMemo, useState, useEffect } from 'react';
import { subMinutes, startOfMinute } from 'date-fns';
import firstBy from 'thenby';
-import { GridRow, GridColumn } from 'components/layout/Grid';
+import { Grid, GridRow } from 'components/layout/Grid';
import Page from 'components/layout/Page';
import RealtimeChart from 'components/metrics/RealtimeChart';
import WorldMap from 'components/common/WorldMap';
-import RealtimeLog from 'components/pages/realtime/RealtimeLog';
-import RealtimeHeader from 'components/pages/realtime/RealtimeHeader';
-import RealtimeUrls from 'components/pages/realtime/RealtimeUrls';
-import RealtimeCountries from 'components/pages/realtime/RealtimeCountries';
-import WebsiteHeader from 'components/pages/websites/WebsiteHeader';
+import RealtimeLog from './RealtimeLog';
+import RealtimeHeader from './RealtimeHeader';
+import RealtimeUrls from './RealtimeUrls';
+import RealtimeCountries from './RealtimeCountries';
+import WebsiteHeader from '../WebsiteHeader';
import useApi from 'components/hooks/useApi';
import { percentFilter } from 'lib/filters';
import { REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants';
-import styles from './RealtimePage.module.css';
import { useWebsite } from 'components/hooks';
+import styles from './Realtime.module.css';
function mergeData(state = [], data = [], time) {
const ids = state.map(({ __id }) => __id);
@@ -23,7 +24,7 @@ function mergeData(state = [], data = [], time) {
.filter(({ timestamp }) => timestamp >= time);
}
-export function RealtimePage({ websiteId }) {
+export function Realtime({ websiteId }) {
const [currentData, setCurrentData] = useState();
const { get, useQuery } = useApi();
const { data: website } = useWebsite(websiteId);
@@ -89,29 +90,27 @@ export function RealtimePage({ websiteId }) {
return currentData;
}, [currentData]);
+ if (isLoading || error) {
+ return ;
+ }
+
return (
-
+ <>
-
-
+
+
-
-
-
-
-
-
+
+
-
-
-
-
-
+
+
+ >
);
}
-export default RealtimePage;
+export default Realtime;
diff --git a/src/components/pages/realtime/RealtimePage.module.css b/src/app/(main)/websites/[id]/realtime/Realtime.module.css
similarity index 100%
rename from src/components/pages/realtime/RealtimePage.module.css
rename to src/app/(main)/websites/[id]/realtime/Realtime.module.css
diff --git a/src/components/pages/realtime/RealtimeCountries.js b/src/app/(main)/websites/[id]/realtime/RealtimeCountries.js
similarity index 81%
rename from src/components/pages/realtime/RealtimeCountries.js
rename to src/app/(main)/websites/[id]/realtime/RealtimeCountries.js
index 7a61651a..6f484b09 100644
--- a/src/components/pages/realtime/RealtimeCountries.js
+++ b/src/app/(main)/websites/[id]/realtime/RealtimeCountries.js
@@ -1,5 +1,4 @@
import { useCallback } from 'react';
-import { useRouter } from 'next/router';
import ListTable from 'components/metrics/ListTable';
import useLocale from 'components/hooks/useLocale';
import useCountryNames from 'components/hooks/useCountryNames';
@@ -11,16 +10,18 @@ export function RealtimeCountries({ data }) {
const { formatMessage, labels } = useMessages();
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
- const { basePath } = useRouter();
const renderCountryName = useCallback(
({ x: code }) => (
-
+
{countryNames[code]}
),
- [countryNames, locale, basePath],
+ [countryNames, locale],
);
return (
diff --git a/src/components/pages/realtime/RealtimeCountries.module.css b/src/app/(main)/websites/[id]/realtime/RealtimeCountries.module.css
similarity index 100%
rename from src/components/pages/realtime/RealtimeCountries.module.css
rename to src/app/(main)/websites/[id]/realtime/RealtimeCountries.module.css
diff --git a/src/components/pages/realtime/RealtimeHeader.js b/src/app/(main)/websites/[id]/realtime/RealtimeHeader.js
similarity index 100%
rename from src/components/pages/realtime/RealtimeHeader.js
rename to src/app/(main)/websites/[id]/realtime/RealtimeHeader.js
diff --git a/src/components/pages/realtime/RealtimeHeader.module.css b/src/app/(main)/websites/[id]/realtime/RealtimeHeader.module.css
similarity index 100%
rename from src/components/pages/realtime/RealtimeHeader.module.css
rename to src/app/(main)/websites/[id]/realtime/RealtimeHeader.module.css
diff --git a/src/components/pages/realtime/RealtimeHome.js b/src/app/(main)/websites/[id]/realtime/RealtimeHome.js
similarity index 95%
rename from src/components/pages/realtime/RealtimeHome.js
rename to src/app/(main)/websites/[id]/realtime/RealtimeHome.js
index 4f2f6279..dbaeb541 100644
--- a/src/components/pages/realtime/RealtimeHome.js
+++ b/src/app/(main)/websites/[id]/realtime/RealtimeHome.js
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader';
import useApi from 'components/hooks/useApi';
diff --git a/src/components/pages/realtime/RealtimeLog.js b/src/app/(main)/websites/[id]/realtime/RealtimeLog.js
similarity index 100%
rename from src/components/pages/realtime/RealtimeLog.js
rename to src/app/(main)/websites/[id]/realtime/RealtimeLog.js
diff --git a/src/components/pages/realtime/RealtimeLog.module.css b/src/app/(main)/websites/[id]/realtime/RealtimeLog.module.css
similarity index 98%
rename from src/components/pages/realtime/RealtimeLog.module.css
rename to src/app/(main)/websites/[id]/realtime/RealtimeLog.module.css
index dc78f818..f400cc1b 100644
--- a/src/components/pages/realtime/RealtimeLog.module.css
+++ b/src/app/(main)/websites/[id]/realtime/RealtimeLog.module.css
@@ -55,7 +55,6 @@
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
- line-clamp: 2;
-webkit-box-orient: vertical;
}
diff --git a/src/components/pages/realtime/RealtimeUrls.js b/src/app/(main)/websites/[id]/realtime/RealtimeUrls.js
similarity index 100%
rename from src/components/pages/realtime/RealtimeUrls.js
rename to src/app/(main)/websites/[id]/realtime/RealtimeUrls.js
diff --git a/src/app/(main)/websites/[id]/realtime/page.tsx b/src/app/(main)/websites/[id]/realtime/page.tsx
new file mode 100644
index 00000000..b2957fa0
--- /dev/null
+++ b/src/app/(main)/websites/[id]/realtime/page.tsx
@@ -0,0 +1,9 @@
+import Realtime from './Realtime';
+
+export default function WebsiteRealtimePage({ params: { id } }) {
+ if (!id) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/components/pages/websites/WebsiteReportsPage.js b/src/app/(main)/websites/[id]/reports/WebsiteReports.js
similarity index 76%
rename from src/components/pages/websites/WebsiteReportsPage.js
rename to src/app/(main)/websites/[id]/reports/WebsiteReports.js
index 40631839..e7e14468 100644
--- a/src/components/pages/websites/WebsiteReportsPage.js
+++ b/src/app/(main)/websites/[id]/reports/WebsiteReports.js
@@ -1,12 +1,13 @@
+'use client';
import Page from 'components/layout/Page';
import Empty from 'components/common/Empty';
-import ReportsTable from 'components/pages/reports/ReportsTable';
+import ReportsTable from '../../../../(main)/reports/ReportsTable';
import { useMessages, useWebsiteReports } from 'components/hooks';
import Link from 'next/link';
import { Button, Flexbox, Icon, Icons, Text } from 'react-basics';
-import WebsiteHeader from './WebsiteHeader';
+import WebsiteHeader from '../WebsiteHeader';
-export function WebsiteReportsPage({ websiteId }) {
+export function WebsiteReports({ websiteId }) {
const { formatMessage, labels } = useMessages();
const {
reports,
@@ -25,11 +26,15 @@ export function WebsiteReportsPage({ websiteId }) {
await deleteReport(id);
};
+ if (isLoading || error) {
+ return ;
+ }
+
return (
-
+ <>
-
+
@@ -40,7 +45,7 @@ export function WebsiteReportsPage({ websiteId }) {
{hasData && (
)}
{!hasData && }
-
+ >
);
}
-export default WebsiteReportsPage;
+export default WebsiteReports;
diff --git a/src/app/(main)/websites/[id]/reports/page.tsx b/src/app/(main)/websites/[id]/reports/page.tsx
new file mode 100644
index 00000000..bf564025
--- /dev/null
+++ b/src/app/(main)/websites/[id]/reports/page.tsx
@@ -0,0 +1,9 @@
+import WebsiteReports from './WebsiteReports';
+
+export default function WebsiteReportsPage({ params: { id } }) {
+ if (!id) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/app/(main)/websites/page.tsx b/src/app/(main)/websites/page.tsx
new file mode 100644
index 00000000..a1542510
--- /dev/null
+++ b/src/app/(main)/websites/page.tsx
@@ -0,0 +1,16 @@
+import WebsitesHeader from 'app/(main)/settings/websites/WebsitesHeader';
+import WebsitesBrowse from './WebsitesBrowse';
+import { Metadata } from 'next';
+
+export default function WebsitesPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export const metadata: Metadata = {
+ title: 'Websites | umami',
+};
diff --git a/src/app/Providers.tsx b/src/app/Providers.tsx
new file mode 100644
index 00000000..c3d62699
--- /dev/null
+++ b/src/app/Providers.tsx
@@ -0,0 +1,39 @@
+'use client';
+import { IntlProvider } from 'react-intl';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactBasicsProvider } from 'react-basics';
+import ErrorBoundary from 'components/common/ErrorBoundary';
+import useLocale from 'components/hooks/useLocale';
+import 'chartjs-adapter-date-fns';
+
+const client = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ refetchOnWindowFocus: false,
+ },
+ },
+});
+
+function MessagesProvider({ children }) {
+ const { locale, messages } = useLocale();
+ return (
+ null}>
+ {children}
+
+ );
+}
+
+export function Providers({ children }) {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
+
+export default Providers;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 00000000..e2478a95
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,36 @@
+import { Metadata } from 'next';
+import Providers from './Providers';
+import '@fontsource/inter/400.css';
+import '@fontsource/inter/700.css';
+import '@fontsource/inter/800.css';
+import 'react-basics/dist/styles.css';
+import 'styles/locale.css';
+import 'styles/index.css';
+import 'styles/variables.css';
+
+export default function RootLayout({ children }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+ );
+}
+
+export const metadata: Metadata = {
+ title: 'umami',
+};
diff --git a/src/components/pages/login/LoginForm.js b/src/app/login/LoginForm.js
similarity index 96%
rename from src/components/pages/login/LoginForm.js
rename to src/app/login/LoginForm.js
index 797eea14..59d145bf 100644
--- a/src/components/pages/login/LoginForm.js
+++ b/src/app/login/LoginForm.js
@@ -1,3 +1,4 @@
+'use client';
import { useMutation } from '@tanstack/react-query';
import {
Form,
@@ -9,7 +10,7 @@ import {
SubmitButton,
Icon,
} from 'react-basics';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import useApi from 'components/hooks/useApi';
import { setUser } from 'store/app';
import { setClientAuthToken } from 'lib/client';
diff --git a/src/components/pages/login/LoginForm.module.css b/src/app/login/LoginForm.module.css
similarity index 100%
rename from src/components/pages/login/LoginForm.module.css
rename to src/app/login/LoginForm.module.css
diff --git a/src/components/pages/login/LoginLayout.module.css b/src/app/login/page.module.css
similarity index 76%
rename from src/components/pages/login/LoginLayout.module.css
rename to src/app/login/page.module.css
index d12306ea..45115d5b 100644
--- a/src/components/pages/login/LoginLayout.module.css
+++ b/src/app/login/page.module.css
@@ -1,6 +1,5 @@
-.layout {
+.page {
display: flex;
- flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
new file mode 100644
index 00000000..2ac3f724
--- /dev/null
+++ b/src/app/login/page.tsx
@@ -0,0 +1,25 @@
+import LoginForm from './LoginForm';
+import { Metadata } from 'next';
+import styles from './page.module.css';
+
+async function getDisabled() {
+ return !!process.env.LOGIN_DISABLED;
+}
+
+export default async function LoginPage() {
+ const disabled = await getDisabled();
+
+ if (disabled) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
+
+export const metadata: Metadata = {
+ title: 'Login | umami',
+};
diff --git a/src/pages/logout.js b/src/app/logout/Logout.js
similarity index 68%
rename from src/pages/logout.js
rename to src/app/logout/Logout.js
index ef89080c..e9da0373 100644
--- a/src/pages/logout.js
+++ b/src/app/logout/Logout.js
@@ -1,10 +1,12 @@
+'use client';
import { useEffect } from 'react';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import useApi from 'components/hooks/useApi';
import { setUser } from 'store/app';
import { removeClientAuthToken } from 'lib/client';
-export default function ({ disabled }) {
+export function Logout() {
+ const disabled = !!(process.env.disableLogin || process.env.cloudMode);
const router = useRouter();
const { post } = useApi();
@@ -27,10 +29,4 @@ export default function ({ disabled }) {
return null;
}
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!(process.env.DISABLE_LOGIN || process.env.CLOUD_MODE),
- },
- };
-}
+export default Logout;
diff --git a/src/app/logout/page.tsx b/src/app/logout/page.tsx
new file mode 100644
index 00000000..bce24736
--- /dev/null
+++ b/src/app/logout/page.tsx
@@ -0,0 +1,5 @@
+import Logout from './Logout';
+
+export default function () {
+ return ;
+}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
new file mode 100644
index 00000000..16c5bbcb
--- /dev/null
+++ b/src/app/not-found.tsx
@@ -0,0 +1,13 @@
+'use client';
+import { Flexbox } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+
+export default function () {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+ {formatMessage(labels.pageNotFound)}
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 00000000..6a146801
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,6 @@
+'use client';
+import { redirect } from 'next/navigation';
+
+export default function RootPage() {
+ redirect('/dashboard');
+}
diff --git a/src/components/layout/Footer.js b/src/app/share/[...id]/Footer.js
similarity index 95%
rename from src/components/layout/Footer.js
rename to src/app/share/[...id]/Footer.js
index 3a07c12a..84d4162f 100644
--- a/src/components/layout/Footer.js
+++ b/src/app/share/[...id]/Footer.js
@@ -1,3 +1,4 @@
+'use client';
import { CURRENT_VERSION, HOMEPAGE_URL } from 'lib/constants';
import styles from './Footer.module.css';
diff --git a/src/components/layout/Footer.module.css b/src/app/share/[...id]/Footer.module.css
similarity index 80%
rename from src/components/layout/Footer.module.css
rename to src/app/share/[...id]/Footer.module.css
index 348c92d8..5dc2d584 100644
--- a/src/components/layout/Footer.module.css
+++ b/src/app/share/[...id]/Footer.module.css
@@ -1,10 +1,10 @@
.footer {
display: flex;
flex-direction: row;
+ align-items: center;
justify-content: flex-end;
font-size: var(--font-size-sm);
- line-height: 30px;
- margin: 40px 0;
+ height: 100px;
}
.footer a {
diff --git a/src/app/share/[...id]/Header.js b/src/app/share/[...id]/Header.js
new file mode 100644
index 00000000..41e93f52
--- /dev/null
+++ b/src/app/share/[...id]/Header.js
@@ -0,0 +1,30 @@
+'use client';
+import { Icon, Text } from 'react-basics';
+import Link from 'next/link';
+import LanguageButton from 'components/input/LanguageButton';
+import ThemeButton from 'components/input/ThemeButton';
+import SettingsButton from 'components/input/SettingsButton';
+import Icons from 'components/icons';
+import styles from './Header.module.css';
+
+export function Header() {
+ return (
+
+
+
+
+
+
+ umami
+
+
+
+
+
+
+
+
+ );
+}
+
+export default Header;
diff --git a/src/components/layout/Header.module.css b/src/app/share/[...id]/Header.module.css
similarity index 86%
rename from src/components/layout/Header.module.css
rename to src/app/share/[...id]/Header.module.css
index 26f30552..d353d79a 100644
--- a/src/components/layout/Header.module.css
+++ b/src/app/share/[...id]/Header.module.css
@@ -2,6 +2,7 @@
display: flex;
flex-direction: row;
align-items: center;
+ justify-content: space-between;
width: 100%;
height: 100px;
}
@@ -38,10 +39,3 @@
min-width: 100%;
}
}
-
-@media only screen and (max-width: 768px) {
- .buttons,
- .links {
- display: none;
- }
-}
diff --git a/src/app/share/[...id]/Share.js b/src/app/share/[...id]/Share.js
new file mode 100644
index 00000000..99ba6407
--- /dev/null
+++ b/src/app/share/[...id]/Share.js
@@ -0,0 +1,25 @@
+'use client';
+import WebsiteDetails from 'app/(main)/websites/[id]/WebsiteDetails';
+import useShareToken from 'components/hooks/useShareToken';
+import styles from './Share.module.css';
+import Page from 'components/layout/Page';
+import Header from './Header';
+import Footer from './Footer';
+
+export default function Share({ shareId }) {
+ const { shareToken, isLoading } = useShareToken(shareId);
+
+ if (isLoading || !shareToken) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/app/share/[...id]/Share.module.css b/src/app/share/[...id]/Share.module.css
new file mode 100644
index 00000000..d985435c
--- /dev/null
+++ b/src/app/share/[...id]/Share.module.css
@@ -0,0 +1,4 @@
+.container {
+ flex: 1;
+ min-height: calc(100vh - 200px);
+}
diff --git a/src/app/share/[...id]/page.tsx b/src/app/share/[...id]/page.tsx
new file mode 100644
index 00000000..ca154165
--- /dev/null
+++ b/src/app/share/[...id]/page.tsx
@@ -0,0 +1,5 @@
+import Share from './Share';
+
+export default function ({ params: { id } }) {
+ return ;
+}
diff --git a/src/pages/sso.js b/src/app/sso/page.tsx
similarity index 60%
rename from src/pages/sso.js
rename to src/app/sso/page.tsx
index 6e635206..75ea945d 100644
--- a/src/pages/sso.js
+++ b/src/app/sso/page.tsx
@@ -1,11 +1,14 @@
+'use client';
import { useEffect } from 'react';
import { Loading } from 'react-basics';
-import { useRouter } from 'next/router';
+import { useRouter, useSearchParams } from 'next/navigation';
import { setClientAuthToken } from 'lib/client';
-export default function () {
+export default function SSOPage() {
const router = useRouter();
- const { token, url } = router.query;
+ const search = useSearchParams();
+ const url = search.get('url');
+ const token = search.get('token');
useEffect(() => {
if (url && token) {
diff --git a/src/components/common/DataTable.module.css b/src/components/common/DataTable.module.css
new file mode 100644
index 00000000..e738c895
--- /dev/null
+++ b/src/components/common/DataTable.module.css
@@ -0,0 +1,52 @@
+.table {
+ grid-template-rows: repeat(auto-fit, max-content);
+}
+
+.table td {
+ align-items: center;
+ max-height: max-content;
+}
+
+.search {
+ max-width: 300px;
+ margin: 20px 0;
+}
+
+.action {
+ justify-content: flex-end;
+ gap: 5px;
+}
+
+.body {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow-x: auto;
+}
+
+.body td {
+ display: flex;
+ gap: 10px;
+ min-height: 70px;
+ align-items: center;
+ min-width: min-content;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.body > div > div > div {
+ display: flex;
+ gap: 10px;
+}
+
+.pager {
+ margin: 20px 0;
+}
+
+.status {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 200px;
+}
diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx
new file mode 100644
index 00000000..ad76f18b
--- /dev/null
+++ b/src/components/common/DataTable.tsx
@@ -0,0 +1,92 @@
+import { ReactNode, Dispatch, SetStateAction } from 'react';
+import classNames from 'classnames';
+import { Banner, Loading, SearchField } from 'react-basics';
+import { useMessages } from 'components/hooks';
+import Empty from 'components/common/Empty';
+import Pager from 'components/common/Pager';
+import styles from './DataTable.module.css';
+
+const DEFAULT_SEARCH_DELAY = 600;
+
+export interface DataTableProps {
+ queryResult: {
+ result: {
+ page: number;
+ pageSize: number;
+ count: number;
+ data: any[];
+ };
+ params: {
+ query: string;
+ page: number;
+ };
+ setParams: Dispatch>;
+ isLoading: boolean;
+ error: unknown;
+ };
+ searchDelay?: number;
+ allowSearch?: boolean;
+ allowPaging?: boolean;
+ children: ReactNode | ((data: any) => ReactNode);
+}
+
+export function DataTable({
+ queryResult,
+ searchDelay = 600,
+ allowSearch = true,
+ allowPaging = true,
+ children,
+}: DataTableProps) {
+ const { formatMessage, labels, messages } = useMessages();
+ const { result, error, isLoading, params, setParams } = queryResult || {};
+ const { page, pageSize, count, data } = result || {};
+ const { query } = params || {};
+ const hasData = Boolean(!isLoading && data?.length);
+ const noResults = Boolean(!isLoading && query && !hasData);
+
+ const handleSearch = query => {
+ setParams({ ...params, query, page: params.query ? page : 1 });
+ };
+
+ const handlePageChange = page => {
+ setParams({ ...params, page });
+ };
+
+ if (error) {
+ return {formatMessage(messages.error)} ;
+ }
+
+ return (
+ <>
+ {allowSearch && (hasData || query) && (
+
+ )}
+
+ {hasData ? (typeof children === 'function' ? children(result) : children) : null}
+ {isLoading && }
+ {!isLoading && !hasData && !query && }
+ {noResults && }
+
+ {allowPaging && hasData && (
+
+ )}
+ >
+ );
+}
+
+export default DataTable;
diff --git a/src/components/common/Empty.js b/src/components/common/Empty.tsx
similarity index 72%
rename from src/components/common/Empty.js
rename to src/components/common/Empty.tsx
index c0be761a..2c7fcd4a 100644
--- a/src/components/common/Empty.js
+++ b/src/components/common/Empty.tsx
@@ -2,7 +2,12 @@ import classNames from 'classnames';
import styles from './Empty.module.css';
import useMessages from 'components/hooks/useMessages';
-export function Empty({ message, className }) {
+export interface EmptyProps {
+ message?: string;
+ className?: string;
+}
+
+export function Empty({ message, className }: EmptyProps) {
const { formatMessage, messages } = useMessages();
return (
diff --git a/src/components/common/FilterLink.js b/src/components/common/FilterLink.js
index 2a95e011..89648255 100644
--- a/src/components/common/FilterLink.js
+++ b/src/components/common/FilterLink.js
@@ -2,13 +2,13 @@ import { Icon, Icons } from 'react-basics';
import classNames from 'classnames';
import Link from 'next/link';
import { safeDecodeURI } from 'next-basics';
-import usePageQuery from 'components/hooks/usePageQuery';
+import useNavigation from 'components/hooks/useNavigation';
import useMessages from 'components/hooks/useMessages';
import styles from './FilterLink.module.css';
export function FilterLink({ id, value, label, externalUrl, children, className }) {
const { formatMessage, labels } = useMessages();
- const { resolveUrl, query } = usePageQuery();
+ const { makeUrl, query } = useNavigation();
const active = query[id] !== undefined;
const selected = query[id] === value;
@@ -22,7 +22,7 @@ export function FilterLink({ id, value, label, externalUrl, children, className
{children}
{!value && `(${label || formatMessage(labels.unknown)})`}
{value && (
-
+
{safeDecodeURI(label || value)}
)}
diff --git a/src/components/common/LinkButton.js b/src/components/common/LinkButton.js
index 54c7fa63..26f7251f 100644
--- a/src/components/common/LinkButton.js
+++ b/src/components/common/LinkButton.js
@@ -1,12 +1,14 @@
+import classNames from 'classnames';
import Link from 'next/link';
-import { Icon, Icons, Text } from 'react-basics';
+import { useLocale } from 'components/hooks';
import styles from './LinkButton.module.css';
-export function LinkButton({ href, icon, children }) {
+export function LinkButton({ href, className, children }) {
+ const { dir } = useLocale();
+
return (
-
- {icon || }
- {children}
+
+ {children}
);
}
diff --git a/src/components/common/MobileMenu.js b/src/components/common/MobileMenu.js
index de1e9ffa..83a05dff 100644
--- a/src/components/common/MobileMenu.js
+++ b/src/components/common/MobileMenu.js
@@ -1,11 +1,11 @@
import { createPortal } from 'react-dom';
import classNames from 'classnames';
-import { useRouter } from 'next/router';
+import { usePathname } from 'next/navigation';
import Link from 'next/link';
import styles from './MobileMenu.module.css';
export function MobileMenu({ items = [], onClose }) {
- const { pathname } = useRouter();
+ const pathname = usePathname();
const Items = ({ items, className }) => (
diff --git a/src/components/common/Pager.js b/src/components/common/Pager.js
index 7a5e7ed5..f35c2ab0 100644
--- a/src/components/common/Pager.js
+++ b/src/components/common/Pager.js
@@ -1,14 +1,15 @@
-import styles from './Pager.module.css';
-import { Button, Flexbox, Icon, Icons } from 'react-basics';
+import classNames from 'classnames';
+import { Button, Icon, Icons } from 'react-basics';
import useMessages from 'components/hooks/useMessages';
+import styles from './Pager.module.css';
-export function Pager({ page, pageSize, count, onPageChange }) {
+export function Pager({ page, pageSize, count, onPageChange, className }) {
const { formatMessage, labels } = useMessages();
- const maxPage = Math.ceil(count / pageSize);
+ const maxPage = pageSize && count ? Math.ceil(count / pageSize) : 0;
const lastPage = page === maxPage;
const firstPage = page === 1;
- if (count === 0) {
+ if (count === 0 || !maxPage) {
return null;
}
@@ -24,21 +25,25 @@ export function Pager({ page, pageSize, count, onPageChange }) {
}
return (
-
- handlePageChange(-1)} disabled={firstPage}>
-
-
-
-
-
- {formatMessage(labels.pageOf, { current: page, total: maxPage })}
-
- handlePageChange(1)} disabled={lastPage}>
-
-
-
-
-
+
+
{formatMessage(labels.numberOfRecords, { x: count })}
+
+
handlePageChange(-1)} disabled={firstPage}>
+
+
+
+
+
+ {formatMessage(labels.pageOf, { current: page, total: maxPage })}
+
+
handlePageChange(1)} disabled={lastPage}>
+
+
+
+
+
+
+
);
}
diff --git a/src/components/common/Pager.module.css b/src/components/common/Pager.module.css
index 99eb70ce..0ed5e1f4 100644
--- a/src/components/common/Pager.module.css
+++ b/src/components/common/Pager.module.css
@@ -1,7 +1,27 @@
-.container {
- margin-top: 20px;
+.pager {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ align-items: center;
+}
+
+.nav {
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
.text {
+ font-size: var(--font-size-md);
margin: 0 16px;
+ justify-content: center;
+}
+
+@media only screen and (max-width: 992px) {
+ .pager {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .nav {
+ justify-content: end;
+ }
}
diff --git a/src/components/common/SettingsTable.js b/src/components/common/SettingsTable.js
deleted file mode 100644
index 701dbe13..00000000
--- a/src/components/common/SettingsTable.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import Empty from 'components/common/Empty';
-import useMessages from 'components/hooks/useMessages';
-import { useState } from 'react';
-import {
- SearchField,
- Table,
- TableBody,
- TableCell,
- TableColumn,
- TableHeader,
- TableRow,
-} from 'react-basics';
-import styles from './SettingsTable.module.css';
-import Pager from 'components/common/Pager';
-
-export function SettingsTable({
- columns = [],
- data,
- children,
- cellRender,
- showSearch,
- showPaging,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
- filterValue,
-}) {
- const { formatMessage, labels, messages } = useMessages();
- const [filter, setFilter] = useState(filterValue);
- const { data: value, page, count, pageSize } = data;
-
- const handleFilterChange = value => {
- setFilter(value);
- onFilterChange(value);
- };
-
- return (
- <>
- {showSearch && (value.length > 0 || filterValue) && (
-
- )}
- {value.length === 0 && filterValue && (
-
- )}
- {value.length > 0 && (
-
-
- {(column, index) => {
- return (
-
- {column.label}
-
- );
- }}
-
-
- {(row, keys, rowIndex) => {
- row.action = children(row, keys, rowIndex);
-
- return (
-
- {(data, key, colIndex) => {
- return (
-
- {columns[colIndex].label}
- {cellRender ? cellRender(row, data, key, colIndex) : data[key]}
-
- );
- }}
-
- );
- }}
-
- {showPaging && (
-
- )}
-
- )}
- >
- );
-}
-
-export default SettingsTable;
diff --git a/src/components/common/SettingsTable.module.css b/src/components/common/SettingsTable.module.css
deleted file mode 100644
index fd6cddfa..00000000
--- a/src/components/common/SettingsTable.module.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.cell {
- align-items: center;
-}
-
-.row .cell:last-child {
- gap: 10px;
- justify-content: flex-end;
-}
-
-.label {
- display: none;
- font-weight: 700;
-}
-
-@media screen and (max-width: 992px) {
- .header .cell {
- display: none;
- }
-
- .label {
- display: block;
- min-width: 100px;
- }
-
- .row .cell {
- padding-left: 0;
- flex-basis: 100%;
- }
-}
-
-@media screen and (max-width: 1200px) {
- .row {
- flex-wrap: wrap;
- }
-
- .header .cell:last-child {
- display: none;
- }
-
- .row .cell:last-child {
- padding-left: 0;
- flex-basis: 100%;
- }
-}
diff --git a/src/components/common/UpdateNotice.js b/src/components/common/UpdateNotice.js
index 23907948..509df95c 100644
--- a/src/components/common/UpdateNotice.js
+++ b/src/components/common/UpdateNotice.js
@@ -1,17 +1,18 @@
+'use client';
import { useEffect, useCallback, useState } from 'react';
import { createPortal } from 'react-dom';
-import { Button, Row, Column } from 'react-basics';
+import { Button } from 'react-basics';
import { setItem } from 'next-basics';
import useStore, { checkVersion } from 'store/version';
import { REPO_URL, VERSION_CHECK } from 'lib/constants';
import styles from './UpdateNotice.module.css';
import useMessages from 'components/hooks/useMessages';
-import { useRouter } from 'next/router';
+import { usePathname } from 'next/navigation';
export function UpdateNotice({ user, config }) {
const { formatMessage, labels, messages } = useMessages();
const { latest, checked, hasUpdate, releaseUrl } = useStore();
- const { pathname } = useRouter();
+ const pathname = usePathname();
const [dismissed, setDismissed] = useState(checked);
const allowUpdate =
user?.isAdmin &&
@@ -46,17 +47,17 @@ export function UpdateNotice({ user, config }) {
}
return createPortal(
-
-
+
+
{formatMessage(messages.newVersionAvailable, { version: `v${latest}` })}
-
-
+
+
{formatMessage(labels.viewDetails)}
{formatMessage(labels.dismiss)}
-
- ,
+
+
,
document.body,
);
}
diff --git a/src/components/common/WorldMap.js b/src/components/common/WorldMap.js
index 6ae84677..ff34d5f2 100644
--- a/src/components/common/WorldMap.js
+++ b/src/components/common/WorldMap.js
@@ -1,5 +1,4 @@
import { useState, useMemo } from 'react';
-import { useRouter } from 'next/router';
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
import classNames from 'classnames';
import { colord } from 'colord';
@@ -14,7 +13,6 @@ import { percentFilter } from 'lib/filters';
import styles from './WorldMap.module.css';
export function WorldMap({ data, className }) {
- const { basePath } = useRouter();
const [tooltip, setTooltipPopup] = useState();
const { theme, colors } = useTheme();
const { locale } = useLocale();
@@ -54,7 +52,7 @@ export function WorldMap({ data, className }) {
>
-
+
{({ geographies }) => {
return geographies.map(geo => {
const code = ISO_COUNTRIES[geo.id];
diff --git a/src/components/hooks/index.js b/src/components/hooks/index.js
index 2596ba57..79c0937c 100644
--- a/src/components/hooks/index.js
+++ b/src/components/hooks/index.js
@@ -10,7 +10,7 @@ export * from './useFormat';
export * from './useLanguageNames';
export * from './useLocale';
export * from './useMessages';
-export * from './usePageQuery';
+export * from './useNavigation';
export * from './useReport';
export * from './useReports';
export * from './useRequireLogin';
diff --git a/src/components/hooks/useApi.ts b/src/components/hooks/useApi.ts
index f41547a9..75a928d5 100644
--- a/src/components/hooks/useApi.ts
+++ b/src/components/hooks/useApi.ts
@@ -1,4 +1,3 @@
-import { useRouter } from 'next/router';
import * as reactQuery from '@tanstack/react-query';
import { useApi as nextUseApi } from 'next-basics';
import { getClientAuthToken } from 'lib/client';
@@ -8,12 +7,11 @@ import useStore from 'store/app';
const selector = state => state.shareToken;
export function useApi() {
- const { basePath } = useRouter();
const shareToken = useStore(selector);
const { get, post, put, del } = nextUseApi(
{ authorization: `Bearer ${getClientAuthToken()}`, [SHARE_TOKEN_HEADER]: shareToken?.token },
- basePath,
+ process.env.basePath,
);
return { get, post, put, del, ...reactQuery };
diff --git a/src/components/hooks/useCountryNames.js b/src/components/hooks/useCountryNames.js
index 51cabf34..40611865 100644
--- a/src/components/hooks/useCountryNames.js
+++ b/src/components/hooks/useCountryNames.js
@@ -1,5 +1,4 @@
import { useState, useEffect } from 'react';
-import { useRouter } from 'next/router';
import { httpGet } from 'next-basics';
import enUS from 'public/intl/country/en-US.json';
@@ -9,10 +8,9 @@ const countryNames = {
export function useCountryNames(locale) {
const [list, setList] = useState(countryNames[locale] || enUS);
- const { basePath } = useRouter();
async function loadData(locale) {
- const { data } = await httpGet(`${basePath}/intl/country/${locale}.json`);
+ const { data } = await httpGet(`${process.env.basePath}/intl/country/${locale}.json`);
if (data) {
countryNames[locale] = data;
diff --git a/src/components/hooks/useFilterQuery.ts b/src/components/hooks/useFilterQuery.ts
new file mode 100644
index 00000000..1879180d
--- /dev/null
+++ b/src/components/hooks/useFilterQuery.ts
@@ -0,0 +1,26 @@
+import { useState } from 'react';
+import { useApi } from 'components/hooks/useApi';
+
+export function useFilterQuery(key: any[], fn, options?: any) {
+ const [params, setParams] = useState({
+ query: '',
+ page: 1,
+ });
+ const { useQuery } = useApi();
+
+ const { data, ...other } = useQuery([...key, params], fn.bind(null, params), options);
+
+ return {
+ result: data as {
+ page: number;
+ pageSize: number;
+ count: number;
+ data: any[];
+ },
+ ...other,
+ params,
+ setParams,
+ };
+}
+
+export default useFilterQuery;
diff --git a/src/components/hooks/useLanguageNames.js b/src/components/hooks/useLanguageNames.js
index ff59e93d..3823a26b 100644
--- a/src/components/hooks/useLanguageNames.js
+++ b/src/components/hooks/useLanguageNames.js
@@ -1,5 +1,4 @@
import { useState, useEffect } from 'react';
-import { useRouter } from 'next/router';
import { httpGet } from 'next-basics';
import enUS from 'public/intl/language/en-US.json';
@@ -9,10 +8,9 @@ const languageNames = {
export function useLanguageNames(locale) {
const [list, setList] = useState(languageNames[locale] || enUS);
- const { basePath } = useRouter();
async function loadData(locale) {
- const { data } = await httpGet(`${basePath}/intl/language/${locale}.json`);
+ const { data } = await httpGet(`${process.env.basePath}/intl/language/${locale}.json`);
if (data) {
languageNames[locale] = data;
diff --git a/src/components/hooks/useLocale.js b/src/components/hooks/useLocale.js
index 1374af81..71574d86 100644
--- a/src/components/hooks/useLocale.js
+++ b/src/components/hooks/useLocale.js
@@ -1,5 +1,4 @@
import { useEffect } from 'react';
-import { useRouter } from 'next/router';
import { httpGet, setItem } from 'next-basics';
import { LOCALE_CONFIG } from 'lib/constants';
import { getDateLocale, getTextDirection } from 'lib/lang';
@@ -15,13 +14,12 @@ const selector = state => state.locale;
export function useLocale() {
const locale = useStore(selector);
- const { basePath } = useRouter();
const forceUpdate = useForceUpdate();
const dir = getTextDirection(locale);
const dateLocale = getDateLocale(locale);
async function loadMessages(locale) {
- const { ok, data } = await httpGet(`${basePath}/intl/messages/${locale}.json`);
+ const { ok, data } = await httpGet(`${process.env.basePath}/intl/messages/${locale}.json`);
if (ok) {
messages[locale] = data;
diff --git a/src/components/hooks/useNavigation.js b/src/components/hooks/useNavigation.js
new file mode 100644
index 00000000..658e81ed
--- /dev/null
+++ b/src/components/hooks/useNavigation.js
@@ -0,0 +1,27 @@
+import { useMemo } from 'react';
+import { usePathname, useRouter, useSearchParams } from 'next/navigation';
+import { buildUrl } from 'next-basics';
+
+export function useNavigation() {
+ const router = useRouter();
+ const pathname = usePathname();
+ const params = useSearchParams();
+
+ const query = useMemo(() => {
+ const obj = {};
+
+ for (const [key, value] of params.entries()) {
+ obj[key] = decodeURIComponent(value);
+ }
+
+ return obj;
+ }, [params]);
+
+ function makeUrl(params, reset) {
+ return reset ? pathname : buildUrl(pathname, { ...query, ...params });
+ }
+
+ return { pathname, query, router, makeUrl };
+}
+
+export default useNavigation;
diff --git a/src/components/hooks/usePageQuery.js b/src/components/hooks/usePageQuery.js
deleted file mode 100644
index b275d580..00000000
--- a/src/components/hooks/usePageQuery.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { useMemo } from 'react';
-import { useRouter } from 'next/router';
-import { buildUrl } from 'next-basics';
-
-export function usePageQuery() {
- const router = useRouter();
- const { pathname, search } = location;
- const { asPath } = router;
-
- const query = useMemo(() => {
- if (!search) {
- return {};
- }
-
- const params = search.substring(1).split('&');
-
- return params.reduce((obj, item) => {
- const [key, value] = item.split('=');
-
- obj[key] = decodeURIComponent(value);
-
- return obj;
- }, {});
- }, [search]);
-
- function resolveUrl(params, reset) {
- return buildUrl(asPath.split('?')[0], { ...(reset ? {} : query), ...params });
- }
-
- return { pathname, query, resolveUrl, router };
-}
-
-export default usePageQuery;
diff --git a/src/components/hooks/useRequireLogin.ts b/src/components/hooks/useRequireLogin.ts
index d2f540d4..76460a55 100644
--- a/src/components/hooks/useRequireLogin.ts
+++ b/src/components/hooks/useRequireLogin.ts
@@ -1,10 +1,8 @@
import { useEffect } from 'react';
-import { useRouter } from 'next/router';
import useApi from 'components/hooks/useApi';
import useUser from 'components/hooks/useUser';
-export function useRequireLogin(handler: (data?: object) => void) {
- const { basePath } = useRouter();
+export function useRequireLogin(handler?: (data?: object) => void) {
const { get } = useApi();
const { user, setUser } = useUser();
@@ -15,7 +13,7 @@ export function useRequireLogin(handler: (data?: object) => void) {
setUser(typeof handler === 'function' ? handler(data) : (data as any)?.user);
} catch {
- location.href = `${basePath}/login`;
+ location.href = `${process.env.basePath || ''}/login`;
}
}
diff --git a/src/components/hooks/useShareToken.js b/src/components/hooks/useShareToken.js
index 3d6b9698..5062c73e 100644
--- a/src/components/hooks/useShareToken.js
+++ b/src/components/hooks/useShareToken.js
@@ -1,4 +1,3 @@
-import { useEffect } from 'react';
import useStore, { setShareToken } from 'store/app';
import useApi from './useApi';
@@ -6,23 +5,16 @@ const selector = state => state.shareToken;
export function useShareToken(shareId) {
const shareToken = useStore(selector);
- const { get } = useApi();
+ const { get, useQuery } = useApi();
+ const { isLoading, error } = useQuery(['share', shareId], async () => {
+ const data = await get(`/share/${shareId}`);
- async function loadToken(id) {
- const data = await get(`/share/${id}`);
+ setShareToken(data);
- if (data) {
- setShareToken(data);
- }
- }
+ return data;
+ });
- useEffect(() => {
- if (shareId) {
- loadToken(shareId);
- }
- }, [shareId]);
-
- return shareToken;
+ return { shareToken, isLoading, error };
}
export default useShareToken;
diff --git a/src/components/input/LanguageButton.module.css b/src/components/input/LanguageButton.module.css
index 3d4c0c56..cc5d649a 100644
--- a/src/components/input/LanguageButton.module.css
+++ b/src/components/input/LanguageButton.module.css
@@ -1,7 +1,6 @@
.menu {
- display: flex;
- flex-flow: row wrap;
- min-width: 640px;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
padding: 10px;
background: var(--base50);
z-index: var(--z-index-popup);
@@ -14,7 +13,7 @@
display: flex;
align-items: center;
justify-content: space-between;
- min-width: calc(100% / 3);
+ min-width: 200px;
border-radius: 5px;
padding: 5px 10px;
}
@@ -32,3 +31,15 @@
.icon {
color: var(--primary400);
}
+
+@media screen and (max-width: 992px) {
+ .menu {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media screen and (max-width: 768px) {
+ .menu {
+ transform: translateX(40px);
+ }
+}
diff --git a/src/components/input/LogoutButton.js b/src/components/input/LogoutButton.js
index 2b04a78a..6ca358a1 100644
--- a/src/components/input/LogoutButton.js
+++ b/src/components/input/LogoutButton.js
@@ -5,7 +5,7 @@ import useMessages from 'components/hooks/useMessages';
export function LogoutButton({ tooltipPosition = 'top' }) {
const { formatMessage, labels } = useMessages();
return (
-
+
diff --git a/src/components/input/ProfileButton.js b/src/components/input/ProfileButton.js
index 35b0eb45..2c3f8629 100644
--- a/src/components/input/ProfileButton.js
+++ b/src/components/input/ProfileButton.js
@@ -1,5 +1,5 @@
import { Icon, Button, PopupTrigger, Popup, Menu, Item, Text } from 'react-basics';
-import { useRouter } from 'next/router';
+import { useRouter } from 'next/navigation';
import Icons from 'components/icons';
import useMessages from 'components/hooks/useMessages';
import useUser from 'components/hooks/useUser';
diff --git a/src/components/input/SettingsButton.js b/src/components/input/SettingsButton.js
index a6d72a2b..46c72597 100644
--- a/src/components/input/SettingsButton.js
+++ b/src/components/input/SettingsButton.js
@@ -1,6 +1,6 @@
import { Button, Icon, PopupTrigger, Popup, Form, FormRow } from 'react-basics';
-import TimezoneSetting from 'components/pages/settings/profile/TimezoneSetting';
-import DateRangeSetting from 'components/pages/settings/profile/DateRangeSetting';
+import TimezoneSetting from 'app/(main)/settings/profile/TimezoneSetting';
+import DateRangeSetting from 'app/(main)/settings/profile/DateRangeSetting';
import Icons from 'components/icons';
import useMessages from 'components/hooks/useMessages';
import styles from './SettingsButton.module.css';
diff --git a/src/components/input/ThemeButton.js b/src/components/input/ThemeButton.js
index 3a6a9d14..76d1b370 100644
--- a/src/components/input/ThemeButton.js
+++ b/src/components/input/ThemeButton.js
@@ -1,4 +1,4 @@
-import { useTransition, animated } from 'react-spring';
+import { useTransition, animated } from '@react-spring/web';
import { Button, Icon } from 'react-basics';
import useTheme from 'components/hooks/useTheme';
import Icons from 'components/icons';
diff --git a/src/components/input/WebsiteDateFilter.js b/src/components/input/WebsiteDateFilter.js
index 6903a708..1725ca3b 100644
--- a/src/components/input/WebsiteDateFilter.js
+++ b/src/components/input/WebsiteDateFilter.js
@@ -1,7 +1,7 @@
import useDateRange from 'components/hooks/useDateRange';
import { isAfter } from 'date-fns';
import { incrementDateRange } from 'lib/date';
-import { Button, Flexbox, Icon, Icons } from 'react-basics';
+import { Button, Icon, Icons } from 'react-basics';
import DateFilter from './DateFilter';
import styles from './WebsiteDateFilter.module.css';
@@ -22,9 +22,9 @@ export function WebsiteDateFilter({ websiteId }) {
};
return (
-
+
{value !== 'all' && selectedUnit && (
-
+
handleIncrement(1)}>
@@ -35,7 +35,7 @@ export function WebsiteDateFilter({ websiteId }) {
-
+
)}
-
+
);
}
diff --git a/src/components/input/WebsiteDateFilter.module.css b/src/components/input/WebsiteDateFilter.module.css
index 986f5c17..6f2e822d 100644
--- a/src/components/input/WebsiteDateFilter.module.css
+++ b/src/components/input/WebsiteDateFilter.module.css
@@ -1,7 +1,17 @@
+.container {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
.dropdown {
min-width: 200px;
}
+.buttons {
+ display: flex;
+}
+
.buttons button:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
deleted file mode 100644
index 41e2ec0d..00000000
--- a/src/components/layout/AppLayout.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Container } from 'react-basics';
-import Head from 'next/head';
-import NavBar from 'components/layout/NavBar';
-import UpdateNotice from 'components/common/UpdateNotice';
-import { useRequireLogin, useConfig } from 'components/hooks';
-import styles from './AppLayout.module.css';
-
-export function AppLayout({ title, children }) {
- const { user } = useRequireLogin();
- const config = useConfig();
-
- if (!user || !config || config?.uiDisabled) {
- return null;
- }
-
- return (
-
-
-
-
{title ? `${title} | umami` : 'umami'}
-
-
-
-
-
- {children}
-
-
- );
-}
-
-export default AppLayout;
diff --git a/src/components/layout/Grid.js b/src/components/layout/Grid.js
index 0276063b..86b08887 100644
--- a/src/components/layout/Grid.js
+++ b/src/components/layout/Grid.js
@@ -1,13 +1,18 @@
-import { Row, Column } from 'react-basics';
import classNames from 'classnames';
+import { mapChildren } from 'react-basics';
import styles from './Grid.module.css';
-export function GridRow(props) {
- const { className, ...otherProps } = props;
- return
;
+export function Grid({ className, ...otherProps }) {
+ return
;
}
-export function GridColumn(props) {
- const { className, ...otherProps } = props;
- return ;
+export function GridRow(props) {
+ const { columns = 'two', className, children, ...otherProps } = props;
+ return (
+
+ {mapChildren(children, child => {
+ return
{child}
;
+ })}
+
+ );
}
diff --git a/src/components/layout/Grid.module.css b/src/components/layout/Grid.module.css
index dc2e8ff6..f72a5f12 100644
--- a/src/components/layout/Grid.module.css
+++ b/src/components/layout/Grid.module.css
@@ -1,27 +1,52 @@
-.col {
- display: flex;
- flex-direction: column;
- padding: 20px;
+.grid {
+ display: grid;
}
.row {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
border-top: 1px solid var(--base300);
- min-height: 430px;
}
-.row > .col {
+.col {
+ padding: 20px;
+ min-height: 430px;
border-inline-start: 1px solid var(--base300);
}
-.row > .col:first-child {
+.col:first-child {
border-inline-start: 0;
padding-inline-start: 0;
}
-.row > .col:last-child {
+.col:last-child {
padding-inline-end: 0;
}
+.col.two {
+ grid-column: span 3;
+}
+
+.col.three {
+ grid-column: span 2;
+}
+
+.col.two-one:first-child {
+ grid-column: span 4;
+}
+
+.col.two-one:last-child {
+ grid-column: span 2;
+}
+
+.col.one-two:first-child {
+ grid-column: span 2;
+}
+
+.col.one-two:last-child {
+ grid-column: span 4;
+}
+
@media only screen and (max-width: 992px) {
.row {
border: 0;
@@ -33,4 +58,11 @@
border-inline-end: 0;
padding: 20px 0;
}
+
+ .col.two,
+ .col.three,
+ .col.one-two,
+ .col.two-one {
+ grid-column: span 6 !important;
+ }
}
diff --git a/src/components/layout/Header.js b/src/components/layout/Header.js
deleted file mode 100644
index 21cdd251..00000000
--- a/src/components/layout/Header.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Column, Icon, Row, Text } from 'react-basics';
-import Link from 'next/link';
-import LanguageButton from 'components/input/LanguageButton';
-import ThemeButton from 'components/input/ThemeButton';
-import SettingsButton from 'components/input/SettingsButton';
-import Icons from 'components/icons';
-import styles from './Header.module.css';
-
-export function Header() {
- return (
-
-
-
-
-
-
-
- umami
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default Header;
diff --git a/src/components/layout/NavBar.js b/src/components/layout/NavBar.js
deleted file mode 100644
index 07627e2a..00000000
--- a/src/components/layout/NavBar.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Icon, Text, Row, Column } from 'react-basics';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import classNames from 'classnames';
-import Icons from 'components/icons';
-import ThemeButton from 'components/input/ThemeButton';
-import LanguageButton from 'components/input/LanguageButton';
-import ProfileButton from 'components/input/ProfileButton';
-import useMessages from 'components/hooks/useMessages';
-import HamburgerButton from 'components/common/HamburgerButton';
-import styles from './NavBar.module.css';
-
-export function NavBar() {
- const { pathname } = useRouter();
- const { formatMessage, labels } = useMessages();
-
- const links = [
- { label: formatMessage(labels.dashboard), url: '/dashboard' },
- { label: formatMessage(labels.websites), url: '/websites' },
- { label: formatMessage(labels.reports), url: '/reports' },
- { label: formatMessage(labels.settings), url: '/settings' },
- ].filter(n => n);
-
- return (
-
-
-
-
-
-
-
- umami
-
-
- {links.map(({ url, label }) => {
- return (
-
- {label}
-
- );
- })}
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default NavBar;
diff --git a/src/components/layout/NavGroup.js b/src/components/layout/NavGroup.js
index 94f9d8e6..361dffb5 100644
--- a/src/components/layout/NavGroup.js
+++ b/src/components/layout/NavGroup.js
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { Icon, Text, TooltipPopup } from 'react-basics';
import classNames from 'classnames';
-import { useRouter } from 'next/router';
+import { usePathname } from 'next/navigation';
import Link from 'next/link';
import Icons from 'components/icons';
import styles from './NavGroup.module.css';
@@ -13,7 +13,7 @@ export function NavGroup({
allowExpand = true,
minimized = false,
}) {
- const { pathname } = useRouter();
+ const pathname = usePathname();
const [expanded, setExpanded] = useState(defaultExpanded);
const handleExpand = () => setExpanded(state => !state);
diff --git a/src/components/layout/Page.module.css b/src/components/layout/Page.module.css
index c546971b..73f21979 100644
--- a/src/components/layout/Page.module.css
+++ b/src/components/layout/Page.module.css
@@ -2,6 +2,10 @@
flex: 1;
display: flex;
flex-direction: column;
- background: var(--base50);
position: relative;
+ width: 100%;
+ max-width: 1320px;
+ min-height: calc(100vh - 60px);
+ margin: 0 auto;
+ padding: 0 20px;
}
diff --git a/src/components/layout/Page.js b/src/components/layout/Page.tsx
similarity index 68%
rename from src/components/layout/Page.js
rename to src/components/layout/Page.tsx
index 4f42aa55..2f702012 100644
--- a/src/components/layout/Page.js
+++ b/src/components/layout/Page.tsx
@@ -1,16 +1,28 @@
+'use client';
+import { ReactNode } from 'react';
import classNames from 'classnames';
import { Banner, Loading } from 'react-basics';
import useMessages from 'components/hooks/useMessages';
import styles from './Page.module.css';
-export function Page({ className, error, loading, children }) {
+export function Page({
+ className,
+ error,
+ isLoading,
+ children,
+}: {
+ className?: string;
+ error?: unknown;
+ isLoading?: boolean;
+ children?: ReactNode;
+}) {
const { formatMessage, messages } = useMessages();
if (error) {
return {formatMessage(messages.error)} ;
}
- if (loading) {
+ if (isLoading) {
return ;
}
diff --git a/src/components/layout/PageHeader.js b/src/components/layout/PageHeader.tsx
similarity index 58%
rename from src/components/layout/PageHeader.js
rename to src/components/layout/PageHeader.tsx
index f1363140..c92a89a0 100644
--- a/src/components/layout/PageHeader.js
+++ b/src/components/layout/PageHeader.tsx
@@ -1,8 +1,14 @@
import classNames from 'classnames';
-import React from 'react';
+import React, { ReactNode } from 'react';
import styles from './PageHeader.module.css';
-export function PageHeader({ title, children, className }) {
+export interface PageHeaderProps {
+ title?: string;
+ className?: string;
+ children?: ReactNode;
+}
+
+export function PageHeader({ title, className, children }: PageHeaderProps) {
return (
{title &&
{title}
}
diff --git a/src/components/layout/ReportsLayout.js b/src/components/layout/ReportsLayout.js
deleted file mode 100644
index 374da263..00000000
--- a/src/components/layout/ReportsLayout.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Column, Row } from 'react-basics';
-import styles from './ReportsLayout.module.css';
-
-export function ReportsLayout({ children, filter, header }) {
- return (
- <>
-
{header}
-
- {filter && (
-
- Filters
- {filter}
-
- )}
-
- {children}
-
-
- >
- );
-}
-
-export default ReportsLayout;
diff --git a/src/components/layout/ReportsLayout.module.css b/src/components/layout/ReportsLayout.module.css
deleted file mode 100644
index 6922665f..00000000
--- a/src/components/layout/ReportsLayout.module.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.filter {
- margin-top: 30px;
- min-width: 200px;
- max-width: 100vw;
- padding: 10px;
- background: var(--base50);
- border-radius: 5px;
- border: 1px solid var(--border-color);
-}
-
-.filter h2 {
- padding-bottom: 20px;
-}
-
-.content {
- min-height: 50vh;
-}
-
-@media only screen and (max-width: 768px) {
- .menu {
- display: none;
- }
-}
diff --git a/src/components/layout/SettingsLayout.module.css b/src/components/layout/SettingsLayout.module.css
deleted file mode 100644
index 08ff02aa..00000000
--- a/src/components/layout/SettingsLayout.module.css
+++ /dev/null
@@ -1,20 +0,0 @@
-.menu {
- display: flex;
- flex-direction: column;
- padding-top: 40px;
- padding-right: 20px;
-}
-
-.content {
- min-height: 50vh;
-}
-
-@media only screen and (max-width: 768px) {
- .menu {
- display: none;
- }
-
- .content {
- margin-top: 20px;
- }
-}
diff --git a/src/components/layout/ShareLayout.js b/src/components/layout/ShareLayout.js
deleted file mode 100644
index c634e1b6..00000000
--- a/src/components/layout/ShareLayout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Container } from 'react-basics';
-import Header from './Header';
-import Footer from './Footer';
-
-export function ShareLayout({ children }) {
- return (
-
-
- {children}
-
-
- );
-}
-
-export default ShareLayout;
diff --git a/src/components/layout/SideNav.js b/src/components/layout/SideNav.js
index ccb6f360..1f1ab14f 100644
--- a/src/components/layout/SideNav.js
+++ b/src/components/layout/SideNav.js
@@ -1,6 +1,6 @@
import classNames from 'classnames';
import { Menu, Item } from 'react-basics';
-import { useRouter } from 'next/router';
+import { usePathname } from 'next/navigation';
import Link from 'next/link';
import styles from './SideNav.module.css';
@@ -11,13 +11,13 @@ export function SideNav({
scroll = false,
onSelect = () => {},
}) {
- const { asPath } = useRouter();
+ const pathname = usePathname();
return (
{({ key, label, url }) => (
-
{label}
diff --git a/src/components/messages.js b/src/components/messages.js
index 7f432eb3..04a29a4c 100644
--- a/src/components/messages.js
+++ b/src/components/messages.js
@@ -193,6 +193,10 @@ export const labels = defineMessages({
pageOf: { id: 'label.page-of', defaultMessage: 'Page {current} of {total}' },
create: { id: 'label.create', defaultMessage: 'Create' },
search: { id: 'label.search', defaultMessage: 'Search' },
+ numberOfRecords: {
+ id: 'label.number-of-records',
+ defaultMessage: '{x} {x, plural, one {record} other {records}}',
+ },
});
export const messages = defineMessages({
diff --git a/src/components/metrics/BarChart.js b/src/components/metrics/BarChart.js
index 7b94c147..8341fbc7 100644
--- a/src/components/metrics/BarChart.js
+++ b/src/components/metrics/BarChart.js
@@ -145,7 +145,7 @@ export function BarChart({
}, [datasets, unit, theme, animationDuration, locale]);
return (
- <>
+
{loading &&
}
@@ -156,7 +156,7 @@ export function BarChart({
{tooltip}
)}
- >
+
);
}
diff --git a/src/components/metrics/BarChart.module.css b/src/components/metrics/BarChart.module.css
index f2e26db1..6af22abe 100644
--- a/src/components/metrics/BarChart.module.css
+++ b/src/components/metrics/BarChart.module.css
@@ -1,3 +1,7 @@
+.container {
+ display: grid;
+}
+
.chart {
position: relative;
height: 400px;
diff --git a/src/components/metrics/BrowsersTable.js b/src/components/metrics/BrowsersTable.js
index e68b159f..afc53663 100644
--- a/src/components/metrics/BrowsersTable.js
+++ b/src/components/metrics/BrowsersTable.js
@@ -1,4 +1,3 @@
-import { useRouter } from 'next/router';
import FilterLink from 'components/common/FilterLink';
import MetricsTable from 'components/metrics/MetricsTable';
import useMessages from 'components/hooks/useMessages';
@@ -6,14 +5,13 @@ import useFormat from 'components/hooks/useFormat';
export function BrowsersTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
- const { basePath } = useRouter();
const { formatBrowser } = useFormat();
function renderLink({ x: browser }) {
return (
{
@@ -22,7 +20,7 @@ export function CitiesTable({ websiteId, ...props }) {
{country && (
)}
diff --git a/src/components/metrics/CountriesTable.js b/src/components/metrics/CountriesTable.js
index ec7ab73d..6f3b75b0 100644
--- a/src/components/metrics/CountriesTable.js
+++ b/src/components/metrics/CountriesTable.js
@@ -1,4 +1,3 @@
-import { useRouter } from 'next/router';
import FilterLink from 'components/common/FilterLink';
import useCountryNames from 'components/hooks/useCountryNames';
import { useLocale, useMessages, useFormat } from 'components/hooks';
@@ -8,7 +7,6 @@ export function CountriesTable({ websiteId, ...props }) {
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
const { formatMessage, labels } = useMessages();
- const { basePath } = useRouter();
const { formatCountry } = useFormat();
function renderLink({ x: code }) {
@@ -19,7 +17,10 @@ export function CountriesTable({ websiteId, ...props }) {
value={countryNames[code] && code}
label={formatCountry(code)}
>
-
+
);
}
diff --git a/src/components/metrics/DevicesTable.js b/src/components/metrics/DevicesTable.js
index 7b591552..606b020a 100644
--- a/src/components/metrics/DevicesTable.js
+++ b/src/components/metrics/DevicesTable.js
@@ -1,19 +1,17 @@
import MetricsTable from './MetricsTable';
import FilterLink from 'components/common/FilterLink';
import useMessages from 'components/hooks/useMessages';
-import { useRouter } from 'next/router';
import { useFormat } from 'components/hooks';
export function DevicesTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
- const { basePath } = useRouter();
const { formatDevice } = useFormat();
function renderLink({ x: device }) {
return (
+ const { data, isLoading } = useQuery(['events', websiteId, modified, event], () =>
get(`/websites/${websiteId}/events`, {
startAt: +startDate,
endAt: +endDate,
unit,
timezone,
url,
- eventName,
+ event,
token,
}),
);
diff --git a/src/components/metrics/FilterTags.js b/src/components/metrics/FilterTags.js
index cb88a7db..554c223a 100644
--- a/src/components/metrics/FilterTags.js
+++ b/src/components/metrics/FilterTags.js
@@ -1,6 +1,6 @@
import { safeDecodeURI } from 'next-basics';
import { Button, Icon, Icons, Text } from 'react-basics';
-import usePageQuery from 'components/hooks/usePageQuery';
+import useNavigation from 'components/hooks/useNavigation';
import useMessages from 'components/hooks/useMessages';
import styles from './FilterTags.module.css';
@@ -8,9 +8,9 @@ export function FilterTags({ params }) {
const { formatMessage, labels } = useMessages();
const {
router,
- resolveUrl,
+ makeUrl,
query: { view },
- } = usePageQuery();
+ } = useNavigation();
if (Object.keys(params).filter(key => params[key]).length === 0) {
return null;
@@ -18,9 +18,9 @@ export function FilterTags({ params }) {
function handleCloseFilter(param) {
if (!param) {
- router.push(resolveUrl({ view }, true));
+ router.push(makeUrl({ view }, true));
} else {
- router.push(resolveUrl({ [param]: undefined }));
+ router.push(makeUrl({ [param]: undefined }));
}
}
diff --git a/src/components/metrics/ListTable.js b/src/components/metrics/ListTable.js
index 33ca59eb..e4ea5ea8 100644
--- a/src/components/metrics/ListTable.js
+++ b/src/components/metrics/ListTable.js
@@ -1,12 +1,11 @@
-import { useState } from 'react';
import useMeasure from 'react-use-measure';
import { FixedSizeList } from 'react-window';
-import { useSpring, animated, config } from 'react-spring';
+import { useSpring, animated, config } from '@react-spring/web';
import classNames from 'classnames';
import Empty from 'components/common/Empty';
-import { formatNumber, formatLongNumber } from 'lib/format';
+import { formatLongNumber } from 'lib/format';
import useMessages from 'components/hooks/useMessages';
-import styles from './DataTable.module.css';
+import styles from './ListTable.module.css';
export function ListTable({
data = [],
@@ -20,10 +19,6 @@ export function ListTable({
}) {
const { formatMessage, labels } = useMessages();
const [ref, bounds] = useMeasure();
- const [format, setFormat] = useState(true);
- const formatFunc = format ? formatLongNumber : formatNumber;
-
- const handleSetFormat = () => setFormat(state => !state);
const getRow = row => {
const { x: label, y: value, z: percent } = row;
@@ -35,8 +30,6 @@ export function ListTable({
value={value}
percent={percent}
animate={animate && !virtualize}
- format={formatFunc}
- onClick={handleSetFormat}
showPercentage={showPercentage}
/>
);
@@ -50,9 +43,7 @@ export function ListTable({
{title}
-
- {metric}
-
+
{metric}
{data?.length === 0 &&
}
@@ -68,15 +59,7 @@ export function ListTable({
);
}
-const AnimatedRow = ({
- label,
- value = 0,
- percent,
- animate,
- format,
- onClick,
- showPercentage = true,
-}) => {
+const AnimatedRow = ({ label, value = 0, percent, animate, showPercentage = true }) => {
const props = useSpring({
width: percent,
y: value,
@@ -87,8 +70,10 @@ const AnimatedRow = ({
return (
{label}
-
-
{props.y?.to(format)}
+
+
+ {props.y?.to(formatLongNumber)}
+
{showPercentage && (
diff --git a/src/components/metrics/DataTable.module.css b/src/components/metrics/ListTable.module.css
similarity index 97%
rename from src/components/metrics/DataTable.module.css
rename to src/components/metrics/ListTable.module.css
index 04e12e9b..9d840235 100644
--- a/src/components/metrics/DataTable.module.css
+++ b/src/components/metrics/ListTable.module.css
@@ -28,7 +28,6 @@
font-weight: 600;
text-align: center;
width: 100px;
- cursor: pointer;
}
.row {
@@ -71,7 +70,6 @@
text-align: end;
margin-inline-end: 10px;
font-weight: 600;
- cursor: pointer;
}
.percent {
diff --git a/src/components/metrics/MetricCard.js b/src/components/metrics/MetricCard.js
index 8a1806c9..5fff3960 100644
--- a/src/components/metrics/MetricCard.js
+++ b/src/components/metrics/MetricCard.js
@@ -1,5 +1,5 @@
import classNames from 'classnames';
-import { useSpring, animated } from 'react-spring';
+import { useSpring, animated } from '@react-spring/web';
import { formatNumber } from 'lib/format';
import styles from './MetricCard.module.css';
@@ -17,7 +17,9 @@ export const MetricCard = ({
return (
-
{props.x.to(x => format(x))}
+
+ {props?.x?.to(x => format(x))}
+
{label}
{~~change !== 0 && !hideComparison && (
@@ -27,8 +29,9 @@ export const MetricCard = ({
[styles.negative]: change * (reverseColors ? -1 : 1) < 0,
[styles.plusSign]: change > 0,
})}
+ title={changeProps?.x}
>
- {changeProps.x.to(x => format(x))}
+ {changeProps?.x?.to(x => format(x))}
)}
diff --git a/src/components/metrics/MetricsBar.js b/src/components/metrics/MetricsBar.js
index 2b74e425..1160c8be 100644
--- a/src/components/metrics/MetricsBar.js
+++ b/src/components/metrics/MetricsBar.js
@@ -1,27 +1,21 @@
-import { useState } from 'react';
import { Loading, cloneChildren } from 'react-basics';
import ErrorMessage from 'components/common/ErrorMessage';
+import { formatLongNumber } from 'lib/format';
import styles from './MetricsBar.module.css';
-import { formatLongNumber, formatNumber } from 'lib/format';
export function MetricsBar({ children, isLoading, isFetched, error }) {
- const [format, setFormat] = useState(true);
-
- const formatFunc = format
- ? n => (n >= 0 ? formatLongNumber(n) : `-${formatLongNumber(Math.abs(n))}`)
- : formatNumber;
-
- const handleSetFormat = () => {
- setFormat(state => !state);
- };
+ const formatFunc = n => (n >= 0 ? formatLongNumber(n) : `-${formatLongNumber(Math.abs(n))}`);
return (
-
+
{isLoading && !isFetched && }
{error && }
- {cloneChildren(children, child => {
- return { format: child.props.format || formatFunc };
- })}
+ {!isLoading &&
+ !error &&
+ isFetched &&
+ cloneChildren(children, child => {
+ return { format: child.props.format || formatFunc };
+ })}
);
}
diff --git a/src/components/metrics/MetricsBar.module.css b/src/components/metrics/MetricsBar.module.css
index eb33a324..21c9e802 100644
--- a/src/components/metrics/MetricsBar.module.css
+++ b/src/components/metrics/MetricsBar.module.css
@@ -1,18 +1,11 @@
.bar {
- display: flex;
- flex-direction: row;
- cursor: pointer;
- min-height: 110px;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(140px, max-content));
gap: 20px;
- flex-wrap: wrap;
}
-.card {
- justify-self: flex-start;
-}
-
-@media only screen and (max-width: 992px) {
- .card {
- flex-basis: calc(50% - 20px);
+@media screen and (max-width: 768px) {
+ .bar {
+ grid-template-columns: 1fr 1fr;
}
}
diff --git a/src/components/metrics/MetricsTable.js b/src/components/metrics/MetricsTable.js
index 6521c415..d36bc91e 100644
--- a/src/components/metrics/MetricsTable.js
+++ b/src/components/metrics/MetricsTable.js
@@ -6,7 +6,7 @@ import classNames from 'classnames';
import useApi from 'components/hooks/useApi';
import { percentFilter } from 'lib/filters';
import useDateRange from 'components/hooks/useDateRange';
-import usePageQuery from 'components/hooks/usePageQuery';
+import useNavigation from 'components/hooks/useNavigation';
import ErrorMessage from 'components/common/ErrorMessage';
import ListTable from './ListTable';
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
@@ -28,10 +28,9 @@ export function MetricsTable({
}) {
const [{ startDate, endDate, modified }] = useDateRange(websiteId);
const {
- resolveUrl,
- router,
+ makeUrl,
query: { url, referrer, title, os, browser, device, country, region, city },
- } = usePageQuery();
+ } = useNavigation();
const { formatMessage, labels } = useMessages();
const { get, useQuery } = useApi();
const { dir } = useLocale();
@@ -104,7 +103,7 @@ export function MetricsTable({
{data && !error &&
}
{data && !error && limit && (
-
+
{formatMessage(labels.more)}
diff --git a/src/components/metrics/OSTable.js b/src/components/metrics/OSTable.js
index 90135124..15570d85 100644
--- a/src/components/metrics/OSTable.js
+++ b/src/components/metrics/OSTable.js
@@ -1,17 +1,15 @@
import MetricsTable from './MetricsTable';
import FilterLink from 'components/common/FilterLink';
import useMessages from 'components/hooks/useMessages';
-import { useRouter } from 'next/router';
export function OSTable({ websiteId, ...props }) {
const { formatMessage, labels } = useMessages();
- const { basePath } = useRouter();
function renderLink({ x: os }) {
return (
{
- router.push(resolveUrl({ view: key }), null, { shallow: true });
+ router.push(makeUrl({ view: key }), { scroll: true });
};
const buttons = [
diff --git a/src/components/metrics/PageviewsChart.js b/src/components/metrics/PageviewsChart.js
index 096278f3..1ea4b044 100644
--- a/src/components/metrics/PageviewsChart.js
+++ b/src/components/metrics/PageviewsChart.js
@@ -13,13 +13,13 @@ export function PageviewsChart({ websiteId, data, unit, loading, ...props }) {
return [
{
- label: formatMessage(labels.uniqueVisitors),
+ label: formatMessage(labels.visitors),
data: data.sessions,
borderWidth: 1,
...colors.chart.visitors,
},
{
- label: formatMessage(labels.pageViews),
+ label: formatMessage(labels.views),
data: data.pageviews,
borderWidth: 1,
...colors.chart.views,
diff --git a/src/components/metrics/RegionsTable.js b/src/components/metrics/RegionsTable.js
index c2ee9b3f..50b1c3d4 100644
--- a/src/components/metrics/RegionsTable.js
+++ b/src/components/metrics/RegionsTable.js
@@ -1,4 +1,3 @@
-import { useRouter } from 'next/router';
import FilterLink from 'components/common/FilterLink';
import { emptyFilter } from 'lib/filters';
import useLocale from 'components/hooks/useLocale';
@@ -11,7 +10,6 @@ export function RegionsTable({ websiteId, ...props }) {
const { locale } = useLocale();
const { formatMessage, labels } = useMessages();
const countryNames = useCountryNames(locale);
- const { basePath } = useRouter();
const renderLabel = (code, country) => {
const region = code.includes('-') ? code : `${country}-${code}`;
@@ -21,7 +19,10 @@ export function RegionsTable({ websiteId, ...props }) {
const renderLink = ({ x: code, country }) => {
return (
-
+
);
};
diff --git a/src/components/pages/event-data/EventDataMetricsBar.js b/src/components/pages/event-data/EventDataMetricsBar.js
deleted file mode 100644
index 5d984bea..00000000
--- a/src/components/pages/event-data/EventDataMetricsBar.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Column, Row } from 'react-basics';
-import { useApi, useDateRange } from 'components/hooks';
-import MetricCard from 'components/metrics/MetricCard';
-import useMessages from 'components/hooks/useMessages';
-import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
-import MetricsBar from 'components/metrics/MetricsBar';
-import styles from './EventDataMetricsBar.module.css';
-
-export function EventDataMetricsBar({ websiteId }) {
- const { formatMessage, labels } = useMessages();
- const { get, useQuery } = useApi();
- const [dateRange] = useDateRange(websiteId);
- const { startDate, endDate, modified } = dateRange;
-
- const { data, error, isLoading, isFetched } = useQuery(
- ['event-data:stats', { websiteId, startDate, endDate, modified }],
- () =>
- get(`/event-data/stats`, {
- websiteId,
- startAt: +startDate,
- endAt: +endDate,
- }),
- );
-
- return (
-
-
-
- {!error && isFetched && (
- <>
-
-
-
- >
- )}
-
-
-
-
-
-
-
-
- );
-}
-
-export default EventDataMetricsBar;
diff --git a/src/components/pages/login/LoginLayout.js b/src/components/pages/login/LoginLayout.js
deleted file mode 100644
index d1bf47bb..00000000
--- a/src/components/pages/login/LoginLayout.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import Head from 'next/head';
-import useLocale from 'components/hooks/useLocale';
-import styles from './LoginLayout.module.css';
-
-export function LoginLayout({ children }) {
- const { dir } = useLocale();
-
- return (
-
-
-
{`Login | umami`}
-
- {children}
-
- );
-}
-
-export default LoginLayout;
diff --git a/src/components/pages/reports/ReportDetails.js b/src/components/pages/reports/ReportDetails.js
deleted file mode 100644
index df1589af..00000000
--- a/src/components/pages/reports/ReportDetails.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import FunnelReport from './funnel/FunnelReport';
-import EventDataReport from './event-data/EventDataReport';
-import InsightsReport from './insights/InsightsReport';
-import RetentionReport from './retention/RetentionReport';
-
-const reports = {
- funnel: FunnelReport,
- 'event-data': EventDataReport,
- insights: InsightsReport,
- retention: RetentionReport,
-};
-
-export default function ReportDetails({ reportId, reportType }) {
- const Report = reports[reportType];
-
- return ;
-}
diff --git a/src/components/pages/reports/ReportsPage.js b/src/components/pages/reports/ReportsPage.js
deleted file mode 100644
index bbb15a36..00000000
--- a/src/components/pages/reports/ReportsPage.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
-import Page from 'components/layout/Page';
-import PageHeader from 'components/layout/PageHeader';
-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 } = useMessages();
- const {
- reports,
- error,
- isLoading,
- deleteReport,
- filter,
- handleFilterChange,
- handlePageChange,
- handlePageSizeChange,
- } = useReports();
-
- const hasData = (reports && reports?.data.length !== 0) || filter;
-
- return (
-
-
-
-
-
-
-
- {formatMessage(labels.createReport)}
-
-
-
-
- {hasData && (
-
- )}
- {!hasData && }
-
- );
-}
-
-export default ReportsPage;
diff --git a/src/components/pages/reports/ReportsTable.js b/src/components/pages/reports/ReportsTable.js
deleted file mode 100644
index 52488c11..00000000
--- a/src/components/pages/reports/ReportsTable.js
+++ /dev/null
@@ -1,97 +0,0 @@
-import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm';
-import LinkButton from 'components/common/LinkButton';
-import SettingsTable from 'components/common/SettingsTable';
-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';
-import { REPORT_TYPES } from 'lib/constants';
-
-export function ReportsTable({
- data = [],
- onDelete = () => {},
- filterValue,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
- showDomain,
-}) {
- const [report, setReport] = useState(null);
- const { formatMessage, labels } = useMessages();
- const { user } = useUser();
-
- const domainColumn = [
- {
- name: 'domain',
- label: formatMessage(labels.domain),
- },
- ];
-
- const columns = [
- { name: 'name', label: formatMessage(labels.name) },
- { name: 'description', label: formatMessage(labels.description) },
- { name: 'type', label: formatMessage(labels.type) },
- ...(showDomain ? domainColumn : []),
- { name: 'action', label: ' ' },
- ];
-
- const cellRender = (row, data, key) => {
- if (key === 'type') {
- return formatMessage(
- labels[Object.keys(REPORT_TYPES).find(key => REPORT_TYPES[key] === row.type)],
- );
- }
- return data[key];
- };
-
- const handleConfirm = () => {
- onDelete(report.id);
- };
-
- return (
- <>
-
- {row => {
- const { id, userId: reportOwnerId, website } = row;
- if (showDomain) {
- row.domain = website.domain;
- }
-
- return (
-
- {formatMessage(labels.view)}
- {!showDomain || user.id === reportOwnerId || user.id === website?.userId}
- setReport(row)}>
-
-
-
- {formatMessage(labels.delete)}
-
-
- );
- }}
-
- {report && (
-
- setReport(null)}
- />
-
- )}
- >
- );
-}
-
-export default ReportsTable;
diff --git a/src/components/pages/settings/profile/ProfileSettings.js b/src/components/pages/settings/profile/ProfileSettings.js
deleted file mode 100644
index a217e52c..00000000
--- a/src/components/pages/settings/profile/ProfileSettings.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import Page from 'components/layout/Page';
-import PageHeader from 'components/layout/PageHeader';
-import ProfileDetails from './ProfileDetails';
-import useMessages from 'components/hooks/useMessages';
-
-export function ProfileSettings() {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
-
- );
-}
-
-export default ProfileSettings;
diff --git a/src/components/pages/settings/teams/TeamAddWebsiteForm.js b/src/components/pages/settings/teams/TeamAddWebsiteForm.js
deleted file mode 100644
index 23e3e136..00000000
--- a/src/components/pages/settings/teams/TeamAddWebsiteForm.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import useApi from 'components/hooks/useApi';
-import { useRef, useState } from 'react';
-import { Button, Dropdown, Form, FormButtons, FormRow, Item, SubmitButton } from 'react-basics';
-import WebsiteTags from './WebsiteTags';
-import useMessages from 'components/hooks/useMessages';
-
-export function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
- const { formatMessage, labels } = useMessages();
- const { get, post, useQuery, useMutation } = useApi();
- const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data));
- const { data: websites } = useQuery(['websites'], () => get('/websites'));
- const [newWebsites, setNewWebsites] = useState([]);
- const formRef = useRef();
-
- const hasData = websites && websites.data.length > 0;
-
- const handleSubmit = () => {
- mutate(
- { websiteIds: newWebsites },
- {
- onSuccess: async () => {
- onSave();
- onClose();
- },
- },
- );
- };
-
- const handleAddWebsite = value => {
- if (!newWebsites.some(a => a === value)) {
- const nextValue = [...newWebsites];
-
- nextValue.push(value);
-
- setNewWebsites(nextValue);
- }
- };
-
- const handleRemoveWebsite = value => {
- const newValue = newWebsites.filter(a => a !== value);
-
- setNewWebsites(newValue);
- };
-
- return (
- <>
- {hasData && (
-
- )}
- >
- );
-}
-
-export default TeamAddWebsiteForm;
diff --git a/src/components/pages/settings/teams/TeamMembers.js b/src/components/pages/settings/teams/TeamMembers.js
deleted file mode 100644
index 207ad72d..00000000
--- a/src/components/pages/settings/teams/TeamMembers.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Loading, useToasts } from 'react-basics';
-import TeamMembersTable from 'components/pages/settings/teams/TeamMembersTable';
-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();
- const { formatMessage, messages } = useMessages();
- const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
- useApiFilter();
- const { get, useQuery } = useApi();
- const { data, isLoading, refetch } = useQuery(
- ['teams:users', teamId, filter, page, pageSize],
- () =>
- get(`/teams/${teamId}/users`, {
- filter,
- page,
- pageSize,
- }),
- );
-
- if (isLoading) {
- return ;
- }
-
- const handleSave = async () => {
- await refetch();
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- return (
- <>
-
- >
- );
-}
-
-export default TeamMembers;
diff --git a/src/components/pages/settings/teams/TeamMembersTable.js b/src/components/pages/settings/teams/TeamMembersTable.js
deleted file mode 100644
index 0755c193..00000000
--- a/src/components/pages/settings/teams/TeamMembersTable.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import useMessages from 'components/hooks/useMessages';
-import useUser from 'components/hooks/useUser';
-import { ROLES } from 'lib/constants';
-import TeamMemberRemoveButton from './TeamMemberRemoveButton';
-import SettingsTable from 'components/common/SettingsTable';
-
-export function TeamMembersTable({
- data = [],
- teamId,
- onSave,
- readOnly,
- filterValue,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
-}) {
- const { formatMessage, labels } = useMessages();
- const { user } = useUser();
-
- const columns = [
- { name: 'username', label: formatMessage(labels.username) },
- { name: 'role', label: formatMessage(labels.role) },
- { name: 'action', label: ' ' },
- ];
-
- const cellRender = (row, data, key) => {
- if (key === 'username') {
- return row?.username;
- }
- if (key === 'role') {
- return formatMessage(
- labels[
- Object.keys(ROLES).find(key => ROLES[key] === row?.teamUser[0]?.role) || labels.unknown
- ],
- );
- }
- return data[key];
- };
-
- return (
-
- {row => {
- return (
- !readOnly && (
-
- )
- );
- }}
-
- );
-}
-
-export default TeamMembersTable;
diff --git a/src/components/pages/settings/teams/TeamWebsites.js b/src/components/pages/settings/teams/TeamWebsites.js
deleted file mode 100644
index 27fe2f85..00000000
--- a/src/components/pages/settings/teams/TeamWebsites.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import {
- ActionForm,
- Button,
- Icon,
- Icons,
- Loading,
- Modal,
- ModalTrigger,
- Text,
- useToasts,
-} from 'react-basics';
-import TeamWebsitesTable from 'components/pages/settings/teams/TeamWebsitesTable';
-import TeamAddWebsiteForm from 'components/pages/settings/teams/TeamAddWebsiteForm';
-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();
- const { formatMessage, labels, messages } = useMessages();
- const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
- useApiFilter();
- const { get, useQuery } = useApi();
- const { data, isLoading, refetch } = useQuery(
- ['teams:websites', teamId, filter, page, pageSize],
- () =>
- get(`/teams/${teamId}/websites`, {
- filter,
- page,
- pageSize,
- }),
- );
- const hasData = data && data.length !== 0;
-
- if (isLoading) {
- return ;
- }
-
- const handleSave = async () => {
- await refetch();
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- const addButton = (
-
-
-
-
-
- {formatMessage(labels.addWebsite)}
-
-
- {close => }
-
-
- );
-
- return (
-
-
{addButton}
- {hasData && (
-
- )}
-
- );
-}
-
-export default TeamWebsites;
diff --git a/src/components/pages/settings/teams/TeamWebsitesTable.js b/src/components/pages/settings/teams/TeamWebsitesTable.js
deleted file mode 100644
index 5ce08f35..00000000
--- a/src/components/pages/settings/teams/TeamWebsitesTable.js
+++ /dev/null
@@ -1,66 +0,0 @@
-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';
-
-export function TeamWebsitesTable({
- data = [],
- onSave,
- filterValue,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
- openExternal = false,
-}) {
- const { formatMessage, labels } = useMessages();
-
- const { user } = useUser();
- const columns = [
- { name: 'name', label: formatMessage(labels.name) },
- { name: 'domain', label: formatMessage(labels.domain) },
- { name: 'action', label: ' ' },
- ];
-
- return (
-
- {row => {
- const { id: teamId, teamUser } = row.teamWebsite[0].team;
- const { id: websiteId, name, domain, userId } = row;
- const owner = teamUser[0];
- const canRemove = user.id === userId || user.id === owner.userId;
-
- row.name = name;
- row.domain = domain;
-
- return (
- <>
-
-
-
-
-
- {formatMessage(labels.view)}
-
-
- {canRemove && (
-
- )}
- >
- );
- }}
-
- );
-}
-
-export default TeamWebsitesTable;
diff --git a/src/components/pages/settings/teams/TeamsList.js b/src/components/pages/settings/teams/TeamsList.js
deleted file mode 100644
index 76a87b0c..00000000
--- a/src/components/pages/settings/teams/TeamsList.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
-import Icons from 'components/icons';
-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 '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 'components/hooks/useApiFilter';
-
-export function TeamsList() {
- const { user } = useUser();
- const { formatMessage, labels, messages } = useMessages();
- const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
- useApiFilter();
- const [update, setUpdate] = useState(0);
-
- const { get, useQuery } = useApi();
- const { data, isLoading, error } = useQuery(['teams', update, filter, page, pageSize], () => {
- return get(`/teams`, {
- filter,
- page,
- pageSize,
- });
- });
-
- const hasData = data && data?.data.length !== 0;
- const isFiltered = filter;
-
- const { showToast } = useToasts();
-
- const handleSave = () => {
- setUpdate(state => state + 1);
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- const handleJoin = () => {
- setUpdate(state => state + 1);
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- const handleDelete = () => {
- setUpdate(state => state + 1);
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- const joinButton = (
-
-
-
-
-
- {formatMessage(labels.joinTeam)}
-
-
- {close => }
-
-
- );
-
- const createButton = (
- <>
- {user.role !== ROLES.viewOnly && (
-
-
-
-
-
- {formatMessage(labels.createTeam)}
-
-
- {close => }
-
-
- )}
- >
- );
-
- return (
-
-
- {(hasData || isFiltered) && (
-
- {joinButton}
- {createButton}
-
- )}
-
-
- {(hasData || isFiltered) && (
-
- )}
-
- {!hasData && !isFiltered && (
-
-
- {joinButton}
- {createButton}
-
-
- )}
-
- );
-}
-
-export default TeamsList;
diff --git a/src/components/pages/settings/teams/TeamsTable.js b/src/components/pages/settings/teams/TeamsTable.js
deleted file mode 100644
index e1710783..00000000
--- a/src/components/pages/settings/teams/TeamsTable.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import SettingsTable from 'components/common/SettingsTable';
-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';
-import TeamDeleteForm from './TeamDeleteForm';
-import TeamLeaveForm from './TeamLeaveForm';
-
-export function TeamsTable({
- data = { data: [] },
- onDelete,
- filterValue,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
-}) {
- const { formatMessage, labels } = useMessages();
- const { user } = useUser();
- const { dir } = useLocale();
-
- const columns = [
- { name: 'name', label: formatMessage(labels.name) },
- { name: 'owner', label: formatMessage(labels.owner) },
- { name: 'action', label: ' ' },
- ];
-
- const cellRender = (row, data, key) => {
- if (key === 'owner') {
- return row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username;
- }
- return data[key];
- };
-
- return (
-
- {row => {
- const { id, teamUser } = row;
- const owner = teamUser.find(({ role }) => role === ROLES.teamOwner);
- const showDelete = user.id === owner?.userId;
-
- return (
- <>
-
-
-
-
-
- {formatMessage(labels.view)}
-
-
- {showDelete && (
-
-
-
-
-
- {formatMessage(labels.delete)}
-
-
- {close => (
-
- )}
-
-
- )}
- {!showDelete && (
-
-
-
-
-
- {formatMessage(labels.leave)}
-
-
- {close => (
-
- )}
-
-
- )}
- >
- );
- }}
-
- );
-}
-
-export default TeamsTable;
diff --git a/src/components/pages/settings/users/UsersList.js b/src/components/pages/settings/users/UsersList.js
deleted file mode 100644
index 0bc8612e..00000000
--- a/src/components/pages/settings/users/UsersList.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { useToasts } from 'react-basics';
-import Page from 'components/layout/Page';
-import PageHeader from 'components/layout/PageHeader';
-import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
-import UsersTable from './UsersTable';
-import UserAddButton from './UserAddButton';
-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();
- const { user } = useUser();
- const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
- useApiFilter();
-
- const { get, useQuery } = useApi();
- const { data, isLoading, error, refetch } = useQuery(
- ['user', filter, page, pageSize],
- () =>
- get(`/users`, {
- filter,
- page,
- pageSize,
- }),
- {
- enabled: !!user,
- },
- );
- const { showToast } = useToasts();
- const hasData = data && data.length !== 0;
-
- const handleSave = () => {
- refetch().then(() => showToast({ message: formatMessage(messages.saved), variant: 'success' }));
- };
-
- const handleDelete = () => {
- refetch().then(() =>
- showToast({ message: formatMessage(messages.userDeleted), variant: 'success' }),
- );
- };
-
- return (
-
-
-
-
- {(hasData || filter) && (
-
- )}
- {!hasData && !filter && (
-
-
-
- )}
-
- );
-}
-
-export default UsersList;
diff --git a/src/components/pages/settings/users/UsersTable.js b/src/components/pages/settings/users/UsersTable.js
deleted file mode 100644
index 1a93710d..00000000
--- a/src/components/pages/settings/users/UsersTable.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import { Button, Text, Icon, Icons, ModalTrigger, Modal } from 'react-basics';
-import { formatDistance } from 'date-fns';
-import Link from 'next/link';
-import useUser from 'components/hooks/useUser';
-import UserDeleteForm from './UserDeleteForm';
-import { ROLES } from 'lib/constants';
-import useMessages from 'components/hooks/useMessages';
-import SettingsTable from 'components/common/SettingsTable';
-import useLocale from 'components/hooks/useLocale';
-
-export function UsersTable({
- data = { data: [] },
- onDelete,
- filterValue,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
-}) {
- const { formatMessage, labels } = useMessages();
- const { user } = useUser();
- const { dateLocale } = useLocale();
-
- const columns = [
- { name: 'username', label: formatMessage(labels.username) },
- { name: 'role', label: formatMessage(labels.role) },
- { name: 'created', label: formatMessage(labels.created) },
- { name: 'action', label: ' ' },
- ];
-
- const cellRender = (row, data, key) => {
- if (key === 'created') {
- return formatDistance(new Date(row.createdAt), new Date(), {
- addSuffix: true,
- locale: dateLocale,
- });
- }
- if (key === 'role') {
- return formatMessage(
- labels[Object.keys(ROLES).find(key => ROLES[key] === row.role)] || labels.unknown,
- );
- }
- return data[key];
- };
-
- return (
-
- {row => {
- return (
- <>
-
-
-
-
-
- {formatMessage(labels.edit)}
-
-
-
-
-
-
-
- {formatMessage(labels.delete)}
-
-
- {close => (
-
- )}
-
-
- >
- );
- }}
-
- );
-}
-
-export default UsersTable;
diff --git a/src/components/pages/settings/websites/WebsitesList.js b/src/components/pages/settings/websites/WebsitesList.js
deleted file mode 100644
index 0cec3832..00000000
--- a/src/components/pages/settings/websites/WebsitesList.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import Page from 'components/layout/Page';
-import PageHeader from 'components/layout/PageHeader';
-import WebsiteAddForm from 'components/pages/settings/websites/WebsiteAddForm';
-import WebsitesTable from 'components/pages/settings/websites/WebsitesTable';
-import useApi from 'components/hooks/useApi';
-import useApiFilter from 'components/hooks/useApiFilter';
-import useMessages from 'components/hooks/useMessages';
-import useUser from 'components/hooks/useUser';
-import { ROLES } from 'lib/constants';
-import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
-
-export function WebsitesList({
- showTeam,
- showEditButton = true,
- showHeader = true,
- includeTeams,
- onlyTeams,
- fetch,
-}) {
- const { formatMessage, labels, messages } = useMessages();
- const { user } = useUser();
-
- const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } =
- useApiFilter();
- const { get, useQuery } = useApi();
- const { data, isLoading, error, refetch } = useQuery(
- ['websites', fetch, user?.id, filter, page, pageSize, includeTeams, onlyTeams],
- () =>
- get(`/users/${user?.id}/websites`, {
- filter,
- page,
- pageSize,
- includeTeams,
- onlyTeams,
- }),
- { enabled: !!user },
- );
- const { showToast } = useToasts();
-
- const handleSave = async () => {
- await refetch();
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- const addButton = (
- <>
- {user.role !== ROLES.viewOnly && (
-
-
-
-
-
- {formatMessage(labels.addWebsite)}
-
-
- {close => }
-
-
- )}
- >
- );
-
- return (
-
- {showHeader && {addButton} }
-
-
- );
-}
-
-export default WebsitesList;
diff --git a/src/components/pages/settings/websites/WebsitesTable.js b/src/components/pages/settings/websites/WebsitesTable.js
deleted file mode 100644
index 7fa50716..00000000
--- a/src/components/pages/settings/websites/WebsitesTable.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import Link from 'next/link';
-import { Button, Text, Icon, Icons } from 'react-basics';
-import SettingsTable from 'components/common/SettingsTable';
-import Empty from 'components/common/Empty';
-import useMessages from 'components/hooks/useMessages';
-import useUser from 'components/hooks/useUser';
-
-export function WebsitesTable({
- data = [],
- filterValue,
- onFilterChange,
- onPageChange,
- onPageSizeChange,
- showTeam,
- showEditButton,
- openExternal = false,
-}) {
- const { formatMessage, labels } = useMessages();
- const { user } = useUser();
-
- const showTable = data && (filterValue || data?.data?.length !== 0);
-
- const teamColumns = [
- { name: 'teamName', label: formatMessage(labels.teamName) },
- { name: 'owner', label: formatMessage(labels.owner) },
- ];
-
- const columns = [
- { name: 'name', label: formatMessage(labels.name) },
- { name: 'domain', label: formatMessage(labels.domain) },
- ...(showTeam ? teamColumns : []),
- { name: 'action', label: ' ' },
- ];
-
- return (
- <>
- {showTable && (
-
- {row => {
- const {
- id,
- teamWebsite,
- user: { username, id: ownerId },
- } = row;
- if (showTeam) {
- row.teamName = teamWebsite[0]?.team.name;
- row.owner = username;
- }
-
- return (
- <>
- {showEditButton && (!showTeam || ownerId === user.id) && (
-
-
-
-
-
- {formatMessage(labels.edit)}
-
-
- )}
-
-
-
-
-
- {formatMessage(labels.view)}
-
-
- >
- );
- }}
-
- )}
- {!showTable && }
- >
- );
-}
-
-export default WebsitesTable;
diff --git a/src/components/pages/websites/WebsiteEventDataPage.js b/src/components/pages/websites/WebsiteEventDataPage.js
deleted file mode 100644
index 08acafb5..00000000
--- a/src/components/pages/websites/WebsiteEventDataPage.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import Page from 'components/layout/Page';
-import WebsiteHeader from './WebsiteHeader';
-import WebsiteEventData from './WebsiteEventData';
-
-export default function WebsiteEventDataPage({ websiteId }) {
- return (
-
-
-
-
- );
-}
diff --git a/src/components/pages/websites/WebsiteMenuView.module.css b/src/components/pages/websites/WebsiteMenuView.module.css
deleted file mode 100644
index 4ba6ab38..00000000
--- a/src/components/pages/websites/WebsiteMenuView.module.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.menu {
- position: relative;
-}
-
-.content {
- min-height: 800px;
-}
diff --git a/src/components/pages/websites/WebsiteTableView.js b/src/components/pages/websites/WebsiteTableView.js
deleted file mode 100644
index c46c8b73..00000000
--- a/src/components/pages/websites/WebsiteTableView.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { useState } from 'react';
-import { GridRow, GridColumn } from 'components/layout/Grid';
-import PagesTable from 'components/metrics/PagesTable';
-import ReferrersTable from 'components/metrics/ReferrersTable';
-import BrowsersTable from 'components/metrics/BrowsersTable';
-import OSTable from 'components/metrics/OSTable';
-import DevicesTable from 'components/metrics/DevicesTable';
-import WorldMap from 'components/common/WorldMap';
-import CountriesTable from 'components/metrics/CountriesTable';
-import EventsTable from 'components/metrics/EventsTable';
-import EventsChart from 'components/metrics/EventsChart';
-
-export default function WebsiteTableView({ websiteId }) {
- const [countryData, setCountryData] = useState();
- const tableProps = {
- websiteId,
- limit: 10,
- };
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/components/pages/websites/WebsiteTableView.module.css b/src/components/pages/websites/WebsiteTableView.module.css
deleted file mode 100644
index 73c5facb..00000000
--- a/src/components/pages/websites/WebsiteTableView.module.css
+++ /dev/null
@@ -1,35 +0,0 @@
-.col {
- display: flex;
- flex-direction: column;
-}
-
-.row {
- border-top: 1px solid var(--base300);
- min-height: 430px;
-}
-
-.row > .col {
- border-left: 1px solid var(--base300);
- padding: 20px;
-}
-
-.row > .col:first-child {
- border-left: 0;
- padding-left: 0;
-}
-
-.row > .col:last-child {
- padding-right: 0;
-}
-
-@media only screen and (max-width: 992px) {
- .row {
- border: 0;
- }
-
- .row > .col {
- border-top: 1px solid var(--base300);
- border-left: 0;
- padding: 20px 0;
- }
-}
diff --git a/src/components/pages/websites/WebsitesPage.js b/src/components/pages/websites/WebsitesPage.js
deleted file mode 100644
index 61c29448..00000000
--- a/src/components/pages/websites/WebsitesPage.js
+++ /dev/null
@@ -1,76 +0,0 @@
-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 'components/hooks';
-import useUser from 'components/hooks/useUser';
-import { ROLES } from 'lib/constants';
-import { useState } from 'react';
-import {
- Button,
- Icon,
- Icons,
- Item,
- Modal,
- ModalTrigger,
- Tabs,
- Text,
- useToasts,
-} from 'react-basics';
-
-export function WebsitesPage() {
- const { formatMessage, labels, messages } = useMessages();
- const [tab, setTab] = useState('my-websites');
- const [fetch, setFetch] = useState(1);
- const { user } = useUser();
- const { showToast } = useToasts();
- const cloudMode = Boolean(process.env.cloudMode);
-
- const handleSave = async () => {
- setFetch(fetch + 1);
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
- };
-
- const addButton = (
- <>
- {user.role !== ROLES.viewOnly && (
-
-
-
-
-
- {formatMessage(labels.addWebsite)}
-
-
- {close => }
-
-
- )}
- >
- );
-
- return (
-
- {!cloudMode && addButton}
-
- - {formatMessage(labels.myWebsites)}
- - {formatMessage(labels.teamWebsites)}
-
-
- {tab === 'my-websites' && (
-
- )}
- {tab === 'team-webaites' && (
-
- )}
-
- );
-}
-
-export default WebsitesPage;
diff --git a/src/index.ts b/src/index.ts
index f2ef13ca..87f2c0d6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,21 +1,3 @@
-/*
-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';
@@ -28,7 +10,7 @@ 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/useNavigation';
export * from 'components/hooks/useReport';
export * from 'components/hooks/useReports';
export * from 'components/hooks/useRequireLogin';
@@ -40,78 +22,31 @@ 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 './app/(main)/settings/teams/[id]/TeamWebsiteAddForm';
+export * from 'app/(main)/settings/teams/[id]/TeamEditForm';
+export * from 'app/(main)/settings/teams/[id]/TeamMemberRemoveButton';
+export * from 'app/(main)/settings/teams/[id]/TeamMembers';
+export * from 'app/(main)/settings/teams/[id]/TeamMembersTable';
+export * from 'app/(main)/settings/teams/[id]/TeamSettings';
+export * from 'app/(main)/settings/teams/[id]/TeamWebsiteRemoveButton';
+export * from 'app/(main)/settings/teams/[id]/TeamWebsites';
+export * from 'app/(main)/settings/teams/[id]/TeamWebsitesTable';
+export * from 'app/(main)/settings/teams/TeamAddForm';
+export * from 'app/(main)/settings/teams/TeamDeleteForm';
+export * from 'app/(main)/settings/teams/TeamsHeader';
+export * from 'app/(main)/settings/teams/TeamJoinForm';
+export * from 'app/(main)/settings/teams/TeamLeaveForm';
+export * from 'app/(main)/settings/teams/TeamsDataTable';
+export * from 'app/(main)/settings/teams/TeamsTable';
+export * from 'app/(main)/settings/teams/WebsiteTags';
-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';
+export * from 'app/(main)/settings/websites/[id]/ShareUrl';
+export * from 'app/(main)/settings/websites/[id]/TrackingCode';
+export * from 'app/(main)/settings/websites/[id]/WebsiteDeleteForm';
+export * from 'app/(main)/settings/websites/[id]/WebsiteEditForm';
+export * from 'app/(main)/settings/websites/[id]/WebsiteResetForm';
+export * from 'app/(main)/settings/websites/WebsiteAddForm';
+export * from 'app/(main)/settings/websites/WebsitesHeader';
+export * from 'app/(main)/settings/websites/WebsiteSettings';
+export * from './app/(main)/settings/websites/WebsitesDataTable';
+export * from 'app/(main)/settings/websites/WebsitesTable';
diff --git a/src/lang/mn-MN.json b/src/lang/mn-MN.json
index ecb4e1fb..100d43d8 100644
--- a/src/lang/mn-MN.json
+++ b/src/lang/mn-MN.json
@@ -2,35 +2,35 @@
"label.access-code": "Хандалтын код",
"label.actions": "Үйлдлүүд",
"label.activity-log": "Үйл ажиллагааны бүртгэл",
- "label.add": "Add",
- "label.add-description": "Add description",
+ "label.add": "Нэмэх",
+ "label.add-description": "Тайлбар нэмэх",
"label.add-website": "Веб нэмэх",
"label.admin": "Админ",
- "label.after": "After",
+ "label.after": "Хойно",
"label.all": "Бүх",
"label.all-time": "Бүх цаг үеийн",
"label.analytics": "Analytics",
- "label.average": "Average",
+ "label.average": "Дундаж",
"label.average-visit-time": "Зочилсон дундаж хугацаа",
"label.back": "Буцах",
- "label.before": "Before",
+ "label.before": "Өмнө",
"label.bounce-rate": "Нэг хуудас үзээд гарсан",
- "label.breakdown": "Breakdown",
- "label.browser": "Browser",
+ "label.breakdown": "Задаргаа",
+ "label.browser": "Хөтөч",
"label.browsers": "Хөтөч",
"label.cancel": "Цуцлах",
"label.change-password": "Нууц үг солих",
"label.cities": "Хотууд",
- "label.city": "City",
+ "label.city": "Хот",
"label.clear-all": "Бүгдийг арилгах",
"label.confirm": "Батлах",
"label.confirm-password": "Шинэ нууц үгээ давтах",
- "label.contains": "Contains",
+ "label.contains": "Агуулах",
"label.continue": "Үргэлжлүүлэх",
"label.countries": "Улс",
- "label.country": "Country",
- "label.create": "Create",
- "label.create-report": "Create report",
+ "label.country": "Улс",
+ "label.create": "Үүсгэх",
+ "label.create-report": "Тайлан үүсгэх",
"label.create-team": "Баг үүсгэх",
"label.create-user": "Хэрэглэгч үүсгэх",
"label.created": "Үүсгэсэн",
@@ -38,46 +38,46 @@
"label.custom-range": "Дурын хугацаа",
"label.dashboard": "Хянах самбар",
"label.data": "Өгөгдөл",
- "label.date": "Date",
+ "label.date": "Огноо",
"label.date-range": "Хугацааны муж",
- "label.day": "Day",
+ "label.day": "Өдөр",
"label.default-date-range": "Өгөгдмөл хугацааны муж",
"label.delete": "Устгах",
"label.delete-team": "Баг устгах",
"label.delete-user": "Хэрэглэгч устгах",
"label.delete-website": "Веб устгах",
- "label.description": "Description",
+ "label.description": "Тайлбар",
"label.desktop": "Суурин компьютер",
"label.details": "Мэдээлэл",
- "label.device": "Device",
+ "label.device": "Төхөөрөмж",
"label.devices": "Төхөөрөмж",
"label.dismiss": "Үл хэргэсэх",
- "label.does-not-contain": "Does not contain",
+ "label.does-not-contain": "Агуулахгүй",
"label.domain": "Домэйн",
- "label.dropoff": "Dropoff",
+ "label.dropoff": "Уналт",
"label.edit": "Засах",
"label.edit-dashboard": "Хянах самбар засах",
"label.enable-share-url": "Хуваалцах холбоос идэвхжүүлэх",
- "label.event": "Event",
- "label.event-data": "Event data",
+ "label.event": "Үйлдэл",
+ "label.event-data": "Үйлдлийн өгөгдөл",
"label.events": "Үйлдэл",
- "label.false": "False",
- "label.field": "Field",
- "label.fields": "Fields",
- "label.filter": "Filter",
+ "label.false": "Худал",
+ "label.field": "Талбар",
+ "label.fields": "Талбар",
+ "label.filter": "Шүүлтүүр",
"label.filter-combined": "Нэгтгэсэн",
"label.filter-raw": "Түүхий",
- "label.filters": "Filters",
- "label.funnel": "Funnel",
- "label.funnel-description": "Understand the conversion and drop-off rate of users.",
- "label.greater-than": "Greater than",
- "label.greater-than-equals": "Greater than or equals",
- "label.insights": "Insights",
- "label.insights-description": "Dive deeper into your data by using segments and filters.",
- "label.is": "Is",
- "label.is-not": "Is not",
- "label.is-not-set": "Is not set",
- "label.is-set": "Is set",
+ "label.filters": "Шүүлтүүр",
+ "label.funnel": "Цутгал",
+ "label.funnel-description": "Хэрэглэгчдийн шилжилт, уналтын хэмжээг шижнлэх.",
+ "label.greater-than": "Их",
+ "label.greater-than-equals": "Их буюу тэнцүү",
+ "label.insights": "Шинжлэх",
+ "label.insights-description": "Өгөгдлөө хэсэгчлэн хуваах, шүүх байдлаар задлах шинжлэх.",
+ "label.is": "Бол",
+ "label.is-not": "Биш",
+ "label.is-not-set": "Утга оноогоогүй",
+ "label.is-set": "Утга оноосон",
"label.join": "Нэгдэх",
"label.join-team": "Багт нэгдэх",
"label.language": "Хэл",
@@ -87,8 +87,8 @@
"label.last-hours": "Сүүлийн {x} цаг",
"label.leave": "Гарах",
"label.leave-team": "Багаас гарах",
- "label.less-than": "Less than",
- "label.less-than-equals": "Less than or equals",
+ "label.less-than": "Бага",
+ "label.less-than-equals": "Бага буюу тэнцүү",
"label.login": "Нэвтрэх",
"label.logout": "Гарах",
"label.max": "Max",
@@ -96,16 +96,16 @@
"label.min": "Min",
"label.mobile": "Утас",
"label.more": "Цааш",
- "label.my-websites": "My websites",
+ "label.my-websites": "Миний вебүүд",
"label.name": "Нэр",
"label.new-password": "Шинэ нууц үг",
"label.none": "Байхгүй",
"label.os": "OS",
- "label.overview": "Overview",
+ "label.overview": "Тойм",
"label.owner": "Эзэмшигч",
- "label.page-of": "Page {current} of {total}",
+ "label.page-of": "Хуудас {total}-с {current}",
"label.page-views": "Хуудас үзсэн",
- "label.pageTitle": "Page title",
+ "label.pageTitle": "Хуудасны гарчиг",
"label.pages": "Хуудас",
"label.password": "Нууц үг",
"label.powered-by": "{name} дээр суурилсан",
@@ -114,39 +114,39 @@
"label.query": "Query",
"label.query-parameters": "Query параметр",
"label.realtime": "Яг одоо",
- "label.referrer": "Referrer",
+ "label.referrer": "Чиглүүлэгч",
"label.referrers": "Чиглүүлэгч",
"label.refresh": "Сэргээх",
"label.regenerate": "Дахин үүсгэх",
- "label.region": "Region",
+ "label.region": "Бүс",
"label.regions": "Бүсүүд",
"label.remove": "Устгах",
- "label.reports": "Reports",
+ "label.reports": "Тайлан",
"label.required": "Шаардлагатай",
"label.reset": "Дахин эхлүүлэх",
"label.reset-website": "Тоон үзүүлэлтийг дахин эхлүүлэх",
- "label.retention": "Retention",
- "label.retention-description": "Measure your website stickiness by tracking how often users return.",
+ "label.retention": "Барилт",
+ "label.retention-description": "Хэрэглэгчид таны веб рүү дахин хандах буюу хэрэглэгчидээ хэр тогтоож буйг хэмжих.",
"label.role": "Эрх",
- "label.run-query": "Run query",
+ "label.run-query": "Query ажиллуулах",
"label.save": "Хадгалах",
"label.screens": "Дэлгэц",
- "label.search": "Search",
- "label.select-date": "Select date",
+ "label.search": "Хайх",
+ "label.select-date": "Огноо сонгох",
"label.select-website": "Веб сонгох",
"label.sessions": "Sessions",
"label.settings": "Тохиргоо",
"label.share-url": "Хуваалцах холбоос",
"label.single-day": "Нэг өдөр",
- "label.sum": "Sum",
+ "label.sum": "Нийлбэр",
"label.tablet": "Таблет",
"label.team": "Баг",
"label.team-guest": "Багийн зочин",
"label.team-id": "Багийн ID",
"label.team-member": "Багийн гишүүн",
- "label.team-name": "Team name",
+ "label.team-name": "Багийн нэр",
"label.team-owner": "Багийн эзэмшигч",
- "label.team-websites": "Team websites",
+ "label.team-websites": "Багийн вебүүд",
"label.teams": "Багууд",
"label.theme": "Загвар",
"label.this-month": "Энэ сар",
@@ -156,37 +156,37 @@
"label.title": "Гарчиг",
"label.today": "Өнөөдөр",
"label.toggle-charts": "Графикийг харуулах/нуух",
- "label.total": "Total",
- "label.total-records": "Total records",
+ "label.total": "Нийт",
+ "label.total-records": "Нийт мөриийн тоо",
"label.tracking-code": "Мөрдөх код",
- "label.true": "True",
- "label.type": "Type",
+ "label.true": "Үнэн",
+ "label.type": "Төрөл",
"label.unique": "Unique",
"label.unique-visitors": "Зочин",
"label.unknown": "Тодорхойгүй",
- "label.untitled": "Untitled",
+ "label.untitled": "Гарчиггүй",
"label.url": "URL",
"label.urls": "URLs",
"label.user": "Хэрэглэгч",
"label.username": "Хэрэглэгчийн нэр",
"label.users": "Хэрэглэгчид",
- "label.value": "Value",
+ "label.value": "Утга",
"label.view": "Харах",
"label.view-details": "Дэлгэрүүлж харах",
- "label.view-only": "View only",
+ "label.view-only": "Зөвхөн үзэх",
"label.views": "Үзсэн",
"label.visitors": "Зочин",
- "label.website": "Website",
+ "label.website": "Веб",
"label.website-id": "Вебийн ID",
"label.websites": "Вебүүд",
- "label.window": "Window",
+ "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": "To delete this account, type {confirmation} in the box below to confirm.",
- "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.",
+ "message.delete-account": "Энэ бүртгэлийг устгахын тулд доорх хэсэгт {confirmation} гэж бичиж баталгаажуулна уу.",
+ "message.delete-website": "Энэ вебийг устгахын тулд доорх хэсэгт {confirmation} гэж бичиж баталгаажуулна уу.",
"message.delete-website-warning": "Энэ вебтэй холбоотой бүх өгөгдөл устах болно.",
"message.error": "Ямар нэг зүйл буруу боллоо.",
"message.event-log": "{url}-д {event}",
@@ -194,11 +194,11 @@
"message.incorrect-username-password": "Буруу хэрэглэгчийн нэр/нууц үг.",
"message.invalid-domain": "Буруу домэйн",
"message.min-password-length": "Хамгийн багадаа {n} тэмдэгт",
- "message.new-version-available": "A new version of Umami {version} is available!",
+ "message.new-version-available": "Umami-н шинэ хувилбар {version} гарсан байна!",
"message.no-data-available": "Өгөгдөл алга.",
- "message.no-event-data": "No event data is available.",
+ "message.no-event-data": "Үйлдлийн өгөгдөл алга.",
"message.no-match-password": "Нууц үг тохирохгүй байна.",
- "message.no-results-found": "No results were found.",
+ "message.no-results-found": "Ямар ч үр дүн олдсонгүй.",
"message.no-team-websites": "Энэ багт ямар ч веб алга.",
"message.no-teams": "Та ямар ч баг үүсгээгүй байна.",
"message.no-users": "Хэрэглэгч байхгүй байна.",
diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json
index c4160280..5eda0949 100644
--- a/src/lang/zh-CN.json
+++ b/src/lang/zh-CN.json
@@ -29,7 +29,7 @@
"label.continue": "继续",
"label.countries": "国家/地区",
"label.country": "国家/地区",
- "label.create": "Create",
+ "label.create": "创建",
"label.create-report": "创建报告",
"label.create-team": "创建团队",
"label.create-user": "创建用户",
@@ -62,18 +62,18 @@
"label.event-data": "事件数据",
"label.events": "行为类别",
"label.false": "否",
- "label.field": "Field",
- "label.fields": "Fields",
- "label.filter": "Filter",
+ "label.field": "字段",
+ "label.fields": "字段",
+ "label.filter": "筛选器",
"label.filter-combined": "合并",
"label.filter-raw": "原始",
"label.filters": "筛选",
"label.funnel": "分析",
- "label.funnel-description": "Understand the conversion and drop-off rate of users.",
- "label.greater-than": "Greater than",
- "label.greater-than-equals": "Greater than or equals",
+ "label.funnel-description": "了解用户的转换率和退出率。",
+ "label.greater-than": "大于",
+ "label.greater-than-equals": "大于或等于",
"label.insights": "见解",
- "label.insights-description": "Dive deeper into your data by using segments and filters.",
+ "label.insights-description": "通过使用筛选器和划分时间段来更深入地研究数据。",
"label.is": "等于",
"label.is-not": "不等于",
"label.is-not-set": "未设置",
@@ -126,12 +126,12 @@
"label.reset": "重置",
"label.reset-website": "重置统计数据",
"label.retention": "保留",
- "label.retention-description": "Measure your website stickiness by tracking how often users return.",
+ "label.retention-description": "通过跟踪用户返回的频率来衡量网站的用户粘性。",
"label.role": "角色",
"label.run-query": "查询",
"label.save": "保存",
"label.screens": "屏幕尺寸",
- "label.search": "Search",
+ "label.search": "搜索",
"label.select-date": "选择数据",
"label.select-website": "选择网站",
"label.sessions": "会话",
diff --git a/src/lang/zh-TW.json b/src/lang/zh-TW.json
index 6a56ea78..aed04d0c 100644
--- a/src/lang/zh-TW.json
+++ b/src/lang/zh-TW.json
@@ -29,7 +29,7 @@
"label.continue": "繼續",
"label.countries": "國家",
"label.country": "國家",
- "label.create": "Create",
+ "label.create": "建立",
"label.create-report": "建立報告",
"label.create-team": "建立團隊",
"label.create-user": "建立使用者",
@@ -64,16 +64,16 @@
"label.false": "否",
"label.field": "欄位",
"label.fields": "欄位",
- "label.filter": "Filter",
+ "label.filter": "篩選器",
"label.filter-combined": "組合",
"label.filter-raw": "原始",
"label.filters": "篩選器",
"label.funnel": "漏斗",
- "label.funnel-description": "Understand the conversion and drop-off rate of users.",
+ "label.funnel-description": "瞭解使用者的轉換率和退出率",
"label.greater-than": "大於",
"label.greater-than-equals": "大於或等於",
"label.insights": "洞察",
- "label.insights-description": "Dive deeper into your data by using segments and filters.",
+ "label.insights-description": "透過使用區段和篩選器來深入探索你的數據",
"label.is": "是",
"label.is-not": "不是",
"label.is-not-set": "未設定",
@@ -126,7 +126,7 @@
"label.reset": "重設",
"label.reset-website": "重設網站",
"label.retention": "保留",
- "label.retention-description": "Measure your website stickiness by tracking how often users return.",
+ "label.retention-description": "透過追蹤使用者回訪的頻率來衡量您的網站黏著度。",
"label.role": "角色",
"label.run-query": "執行查詢",
"label.save": "儲存",
diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts
index 186a085d..2eed340e 100644
--- a/src/lib/clickhouse.ts
+++ b/src/lib/clickhouse.ts
@@ -1,4 +1,4 @@
-import { ClickHouse } from 'clickhouse';
+import { ClickHouseClient, createClient } from '@clickhouse/client';
import dateFormat from 'dateformat';
import debug from 'debug';
import { CLICKHOUSE } from 'lib/db';
@@ -17,7 +17,7 @@ export const CLICKHOUSE_DATE_FORMATS = {
const log = debug('umami:clickhouse');
-let clickhouse: ClickHouse;
+let clickhouse: ClickHouseClient;
const enabled = Boolean(process.env.CLICKHOUSE_URL);
function getClient() {
@@ -25,18 +25,16 @@ function getClient() {
hostname,
port,
pathname,
+ protocol,
username = 'default',
password,
} = new URL(process.env.CLICKHOUSE_URL);
- const client = new ClickHouse({
- url: hostname,
- port: Number(port),
- format: 'json',
- config: {
- database: pathname.replace('/', ''),
- },
- basicAuth: password ? { username, password } : null,
+ const client = createClient({
+ host: `${protocol}//${hostname}:${port}`,
+ database: pathname.replace('/', ''),
+ username: username,
+ password,
});
if (process.env.NODE_ENV !== 'production') {
@@ -118,7 +116,7 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio
};
}
-async function rawQuery(query: string, params: object = {}): Promise {
+async function rawQuery(query: string, params: Record = {}): Promise {
if (process.env.LOG_QUERY) {
log('QUERY:\n', query);
log('PARAMETERS:\n', params);
@@ -126,7 +124,15 @@ async function rawQuery(query: string, params: object = {}): Promise {
await connect();
- return clickhouse.query(query, { params }).toPromise() as Promise;
+ const resultSet = await clickhouse.query({
+ query: query,
+ query_params: params,
+ format: 'JSONEachRow',
+ });
+
+ const data = await resultSet.json();
+
+ return data;
}
async function findUnique(data) {
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 888c1484..4c468c1c 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -19,6 +19,7 @@ export const DEFAULT_ANIMATION_DURATION = 300;
export const DEFAULT_DATE_RANGE = '24hour';
export const DEFAULT_WEBSITE_LIMIT = 10;
export const DEFAULT_RESET_DATE = '2000-01-01';
+export const DEFAULT_PAGE_SIZE = 10;
export const REALTIME_RANGE = 30;
export const REALTIME_INTERVAL = 5000;
@@ -29,23 +30,7 @@ export const FILTER_DAY = 'filter-day';
export const FILTER_RANGE = 'filter-range';
export const FILTER_REFERRERS = 'filter-referrers';
export const FILTER_PAGES = 'filter-pages';
-
-export const USER_FILTER_TYPES = {
- all: 'All',
- username: 'Username',
-} as const;
-export const WEBSITE_FILTER_TYPES = { all: 'All', name: 'Name', domain: 'Domain' } as const;
-export const TEAM_FILTER_TYPES = { all: 'All', name: 'Name', 'user:username': 'Owner' } as const;
-export const REPORT_FILTER_TYPES = {
- all: 'All',
- name: 'Name',
- description: 'Description',
- type: 'Type',
- 'user:username': 'Username',
- 'website:name': 'Website Name',
- 'website:domain': 'Website Domain',
-} as const;
-
+export const UNIT_TYPES = ['year', 'month', 'hour', 'day'];
export const EVENT_COLUMNS = ['url', 'referrer', 'title', 'query', 'event'];
export const SESSION_COLUMNS = [
@@ -213,7 +198,7 @@ export const EVENT_COLORS = [
export const DOMAIN_REGEX =
/^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+(-[a-z0-9-]+)*\.)+(xn--)?[a-z0-9-]{2,63})$/;
-export const SHARE_ID_REGEX = /^[a-zA-Z0-9]{16}$/;
+export const SHARE_ID_REGEX = /^[a-zA-Z0-9]{8,16}$/;
export const UUID_REGEX =
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/;
export const HOSTNAME_REGEX =
diff --git a/src/lib/date.ts b/src/lib/date.ts
index bfbfc0b9..a6c2b17b 100644
--- a/src/lib/date.ts
+++ b/src/lib/date.ts
@@ -238,7 +238,7 @@ export function incrementDateRange(value, increment) {
export function getAllowedUnits(startDate, endDate) {
const units = ['minute', 'hour', 'day', 'month', 'year'];
const minUnit = getMinimumUnit(startDate, endDate);
- const index = units.indexOf(minUnit);
+ const index = units.indexOf(minUnit === 'year' ? 'month' : minUnit);
return index >= 0 ? units.splice(index) : [];
}
@@ -248,7 +248,7 @@ export function getMinimumUnit(startDate, endDate) {
return 'minute';
} else if (differenceInHours(endDate, startDate) <= 48) {
return 'hour';
- } else if (differenceInCalendarDays(endDate, startDate) <= 90) {
+ } else if (differenceInCalendarMonths(endDate, startDate) <= 12) {
return 'day';
} else if (differenceInCalendarMonths(endDate, startDate) <= 24) {
return 'month';
diff --git a/src/lib/db.js b/src/lib/db.js
index 750cdec0..6657ad97 100644
--- a/src/lib/db.js
+++ b/src/lib/db.js
@@ -17,6 +17,10 @@ export function getDatabaseType(url = process.env.DATABASE_URL) {
return POSTGRESQL;
}
+ if (process.env.CLICKHOUSE_URL) {
+ return CLICKHOUSE;
+ }
+
return type;
}
diff --git a/src/lib/middleware.ts b/src/lib/middleware.ts
index 4be958b6..5a12eb6a 100644
--- a/src/lib/middleware.ts
+++ b/src/lib/middleware.ts
@@ -14,7 +14,6 @@ import {
} from 'next-basics';
import { NextApiRequestCollect } from 'pages/api/send';
import { getUserById } from '../queries';
-import { NextApiRequestQueryBody } from './types';
const log = debug('umami:middleware');
@@ -83,14 +82,18 @@ export const useAuth = createMiddleware(async (req, res, next) => {
next();
});
-export const useValidate = createMiddleware(async (req: any, res, next) => {
- try {
- const { yup } = req as NextApiRequestQueryBody;
+export const useValidate = async (schema, req, res) => {
+ return createMiddleware(async (req: any, res, next) => {
+ try {
+ const rules = schema[req.method];
- yup[req.method] && yup[req.method].validateSync({ ...req.query, ...req.body });
- } catch (e: any) {
- return badRequest(res, e.message);
- }
+ if (rules) {
+ rules.validateSync({ ...req.query, ...req.body });
+ }
+ } catch (e: any) {
+ return badRequest(res, e.message);
+ }
- next();
-});
+ next();
+ })(req, res);
+};
diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts
index 59638dbd..4b910f00 100644
--- a/src/lib/prisma.ts
+++ b/src/lib/prisma.ts
@@ -1,11 +1,11 @@
+import { Prisma } from '@prisma/client';
import prisma from '@umami/prisma-client';
import moment from 'moment-timezone';
import { MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db';
-import { FILTER_COLUMNS, SESSION_COLUMNS, OPERATORS } from './constants';
+import { FILTER_COLUMNS, SESSION_COLUMNS, OPERATORS, DEFAULT_PAGE_SIZE } from './constants';
import { loadWebsite } from './load';
import { maxDate } from './date';
import { QueryFilters, QueryOptions, SearchFilter } from './types';
-import { Prisma } from '@prisma/client';
const MYSQL_DATE_FORMATS = {
minute: '%Y-%m-%d %H:%i:00',
@@ -171,7 +171,7 @@ async function rawQuery(sql: string, data: object): Promise {
return prisma.rawQuery(query, params);
}
-function getPageFilters(filters: SearchFilter): [
+function getPageFilters(filters: SearchFilter): [
{
orderBy: {
[x: string]: string;
@@ -185,20 +185,20 @@ function getPageFilters(filters: SearchFilter): [
orderBy: string;
},
] {
- const { pageSize = 10, page = 1, orderBy } = filters || {};
+ const { page = 1, pageSize = DEFAULT_PAGE_SIZE, orderBy, sortDescending = false } = filters || {};
return [
{
- ...(pageSize > 0 && { take: pageSize, skip: pageSize * (page - 1) }),
+ ...(pageSize > 0 && { take: +pageSize, skip: +pageSize * (page - 1) }),
...(orderBy && {
orderBy: [
{
- [orderBy]: 'asc',
+ [orderBy]: sortDescending ? 'desc' : 'asc',
},
],
}),
},
- { pageSize, page: +page, orderBy },
+ { page: +page, pageSize, orderBy },
];
}
diff --git a/src/lib/schema.ts b/src/lib/schema.ts
new file mode 100644
index 00000000..739128b3
--- /dev/null
+++ b/src/lib/schema.ts
@@ -0,0 +1,13 @@
+import * as yup from 'yup';
+
+export const dateRange = {
+ startAt: yup.number().integer().required(),
+ endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
+};
+
+export const pageInfo = {
+ query: yup.string(),
+ page: yup.number().integer().positive(),
+ pageSize: yup.number().integer().positive().max(200),
+ orderBy: yup.string(),
+};
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 3685753e..98fbc29b 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -5,12 +5,8 @@ import {
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';
@@ -27,46 +23,42 @@ export type DynamicDataType = ObjectValues;
export type KafkaTopic = ObjectValues;
export type ReportType = ObjectValues;
-export type ReportSearchFilterType = ObjectValues;
-export type UserSearchFilterType = ObjectValues;
-export type WebsiteSearchFilterType = ObjectValues;
-export type TeamSearchFilterType = ObjectValues;
-
-export interface WebsiteSearchFilter extends SearchFilter {
+export interface WebsiteSearchFilter extends SearchFilter {
userId?: string;
teamId?: string;
includeTeams?: boolean;
onlyTeams?: boolean;
}
-export interface UserSearchFilter extends SearchFilter {
+export interface UserSearchFilter extends SearchFilter {
teamId?: string;
}
-export interface TeamSearchFilter extends SearchFilter {
+export interface TeamSearchFilter extends SearchFilter {
userId?: string;
}
-export interface ReportSearchFilter extends SearchFilter {
+export interface ReportSearchFilter extends SearchFilter {
userId?: string;
websiteId?: string;
includeTeams?: boolean;
}
-export interface SearchFilter {
- filter?: string;
- filterType?: T;
- pageSize: number;
- page: number;
+export interface SearchFilter {
+ query?: string;
+ page?: number;
+ pageSize?: number;
orderBy?: string;
+ sortDescending?: boolean;
}
export interface FilterResult {
data: T;
count: number;
- pageSize: number;
page: number;
+ pageSize: number;
orderBy?: string;
+ sortDescending?: boolean;
}
export interface DynamicData {
diff --git a/src/lib/yup.ts b/src/lib/yup.ts
index a9d21028..5bd0aa18 100644
--- a/src/lib/yup.ts
+++ b/src/lib/yup.ts
@@ -1,19 +1,18 @@
+import moment from 'moment';
import * as yup from 'yup';
+import { UNIT_TYPES } from './constants';
-export function getDateRangeValidation() {
- return {
- startAt: yup.number().integer().required(),
- endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
- };
-}
+export const TimezoneTest = yup
+ .string()
+ .default('UTC')
+ .test(
+ 'timezone',
+ () => `Invalid timezone`,
+ value => moment.tz.zone(value) !== null,
+ );
-// 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(),
- };
-}
+export const UnitTypeTest = yup.string().test(
+ 'unit',
+ () => `Invalid unit`,
+ value => UNIT_TYPES.includes(value),
+);
diff --git a/src/pages/404.js b/src/pages/404.js
deleted file mode 100644
index 8fa13a9c..00000000
--- a/src/pages/404.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Row, Column, Flexbox } from 'react-basics';
-import AppLayout from 'components/layout/AppLayout';
-import useMessages from 'components/hooks/useMessages';
-
-export default function Custom404() {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
-
- {formatMessage(labels.pageNotFound)}
-
-
-
-
- );
-}
diff --git a/src/pages/_app.js b/src/pages/_app.js
deleted file mode 100644
index 7022772c..00000000
--- a/src/pages/_app.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { IntlProvider } from 'react-intl';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { ReactBasicsProvider } from 'react-basics';
-import Head from 'next/head';
-import Script from 'next/script';
-import { useRouter } from 'next/router';
-import ErrorBoundary from 'components/common/ErrorBoundary';
-import useLocale from 'components/hooks/useLocale';
-import '@fontsource/inter/400.css';
-import '@fontsource/inter/700.css';
-import 'react-basics/dist/styles.css';
-import 'styles/variables.css';
-import 'styles/locale.css';
-import 'styles/index.css';
-import 'chartjs-adapter-date-fns';
-
-const client = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- refetchOnWindowFocus: false,
- },
- },
-});
-
-export default function App({ Component, pageProps }) {
- const { locale, messages } = useLocale();
- const { basePath, pathname } = useRouter();
-
- return (
-
- null}>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!pathname.includes('/share/') && }
-
-
-
-
- );
-}
diff --git a/src/pages/api/auth/login.ts b/src/pages/api/auth/login.ts
index 74661e33..0946ae75 100644
--- a/src/pages/api/auth/login.ts
+++ b/src/pages/api/auth/login.ts
@@ -15,6 +15,7 @@ import {
} from 'next-basics';
import { getUserByUsername } from 'queries';
import * as yup from 'yup';
+import { ROLES } from 'lib/constants';
const log = debug('umami:auth');
@@ -43,8 +44,7 @@ export default async (
return forbidden(res);
}
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'POST') {
const { username, password } = req.body;
@@ -63,7 +63,7 @@ export default async (
return ok(res, {
token,
- user: { id, username, role, createdAt },
+ user: { id, username, role, createdAt, isAdmin: role === ROLES.admin },
});
}
diff --git a/src/pages/api/event-data/events.ts b/src/pages/api/event-data/events.ts
index 1d1d3787..19f0c8f1 100644
--- a/src/pages/api/event-data/events.ts
+++ b/src/pages/api/event-data/events.ts
@@ -28,9 +28,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
const { websiteId, startAt, endAt, event } = req.query;
diff --git a/src/pages/api/event-data/fields.ts b/src/pages/api/event-data/fields.ts
index 1cd24fe6..b2af7511 100644
--- a/src/pages/api/event-data/fields.ts
+++ b/src/pages/api/event-data/fields.ts
@@ -28,9 +28,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
const { websiteId, startAt, endAt, field } = req.query;
diff --git a/src/pages/api/event-data/stats.ts b/src/pages/api/event-data/stats.ts
index 7f694bc6..a12ad92f 100644
--- a/src/pages/api/event-data/stats.ts
+++ b/src/pages/api/event-data/stats.ts
@@ -26,9 +26,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
const { websiteId, startAt, endAt } = req.query;
diff --git a/src/pages/api/me/password.ts b/src/pages/api/me/password.ts
index a601f5d6..5b7a8e05 100644
--- a/src/pages/api/me/password.ts
+++ b/src/pages/api/me/password.ts
@@ -37,9 +37,7 @@ export default async (
}
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { currentPassword, newPassword } = req.body;
const { id } = req.auth.user;
diff --git a/src/pages/api/me/teams.ts b/src/pages/api/me/teams.ts
index d394ef07..b0803277 100644
--- a/src/pages/api/me/teams.ts
+++ b/src/pages/api/me/teams.ts
@@ -1,18 +1,18 @@
import { useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
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),
+ ...pageInfo,
}),
};
@@ -21,9 +21,7 @@ export default async (
res: NextApiResponse,
) => {
await useCors(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
req.query.id = req.auth.user.id;
diff --git a/src/pages/api/me/websites.ts b/src/pages/api/me/websites.ts
index d4a803a0..e5a9ad32 100644
--- a/src/pages/api/me/websites.ts
+++ b/src/pages/api/me/websites.ts
@@ -1,18 +1,18 @@
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
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),
+ ...pageInfo,
}),
};
@@ -22,9 +22,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
req.query.id = req.auth.user.id;
diff --git a/src/pages/api/realtime/[id].ts b/src/pages/api/realtime/[id].ts
index 5b1e1e05..212d4a0f 100644
--- a/src/pages/api/realtime/[id].ts
+++ b/src/pages/api/realtime/[id].ts
@@ -23,9 +23,7 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
const { id: websiteId, startAt } = req.query;
diff --git a/src/pages/api/reports/[id].ts b/src/pages/api/reports/[id].ts
index eb4199bc..02eb7363 100644
--- a/src/pages/api/reports/[id].ts
+++ b/src/pages/api/reports/[id].ts
@@ -46,9 +46,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: reportId } = req.query;
const {
diff --git a/src/pages/api/reports/funnel.ts b/src/pages/api/reports/funnel.ts
index a51817bf..9071b962 100644
--- a/src/pages/api/reports/funnel.ts
+++ b/src/pages/api/reports/funnel.ts
@@ -44,9 +44,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'POST') {
const {
diff --git a/src/pages/api/reports/index.ts b/src/pages/api/reports/index.ts
index b36c5638..2d59c9d6 100644
--- a/src/pages/api/reports/index.ts
+++ b/src/pages/api/reports/index.ts
@@ -1,13 +1,13 @@
import { uuid } from 'lib/crypto';
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok } from 'next-basics';
import { createReport, getReportsByUserId } from 'queries';
import * as yup from 'yup';
-export interface ReportsRequestQuery extends SearchFilter {}
+export interface ReportsRequestQuery extends SearchFilter {}
export interface ReportRequestBody {
websiteId: string;
@@ -21,7 +21,7 @@ export interface ReportRequestBody {
const schema = {
GET: yup.object().shape({
- ...getFilterValidation(/All|Name|Description|Type|Username|Website Name|Website Domain/i),
+ ...pageInfo,
}),
POST: yup.object().shape({
websiteId: yup.string().uuid().required(),
@@ -43,21 +43,18 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
user: { id: userId },
} = req.auth;
if (req.method === 'GET') {
- const { page, filter, pageSize } = req.query;
+ const { page, query } = req.query;
const data = await getReportsByUserId(userId, {
page,
- filter,
- pageSize: +pageSize || undefined,
+ query,
includeTeams: true,
});
diff --git a/src/pages/api/reports/insights.ts b/src/pages/api/reports/insights.ts
index 4d17c922..4344422a 100644
--- a/src/pages/api/reports/insights.ts
+++ b/src/pages/api/reports/insights.ts
@@ -69,9 +69,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'POST') {
const {
diff --git a/src/pages/api/reports/retention.ts b/src/pages/api/reports/retention.ts
index 4006ab12..6ff7bbe1 100644
--- a/src/pages/api/reports/retention.ts
+++ b/src/pages/api/reports/retention.ts
@@ -1,6 +1,7 @@
import { canViewWebsite } from 'lib/auth';
import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
+import { TimezoneTest } from 'lib/yup';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getRetention } from 'queries';
@@ -8,7 +9,7 @@ import * as yup from 'yup';
export interface RetentionRequestBody {
websiteId: string;
- dateRange: { startDate: string; endDate: string };
+ dateRange: { startDate: string; endDate: string; timezone: string };
}
const schema = {
@@ -19,6 +20,7 @@ const schema = {
.shape({
startDate: yup.date().required(),
endDate: yup.date().required(),
+ timezone: TimezoneTest,
})
.required(),
}),
@@ -30,14 +32,12 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'POST') {
const {
websiteId,
- dateRange: { startDate, endDate },
+ dateRange: { startDate, endDate, timezone },
} = req.body;
if (!(await canViewWebsite(req.auth, websiteId))) {
@@ -47,6 +47,7 @@ export default async (
const data = await getRetention(websiteId, {
startDate: new Date(startDate),
endDate: new Date(endDate),
+ timezone,
});
return ok(res, data);
diff --git a/src/pages/api/scripts/telemetry.js b/src/pages/api/scripts/telemetry.js
index 954d5058..6a249de0 100644
--- a/src/pages/api/scripts/telemetry.js
+++ b/src/pages/api/scripts/telemetry.js
@@ -1,18 +1,23 @@
+import { ok } from 'next-basics';
import { CURRENT_VERSION, TELEMETRY_PIXEL } from 'lib/constants';
export default function handler(req, res) {
- res.setHeader('content-type', 'text/javascript');
+ if (process.env.NODE_ENV === 'production') {
+ res.setHeader('content-type', 'text/javascript');
- if (process.env.DISABLE_TELEMETRY) {
- return res.send('/* telemetry disabled */');
- }
+ if (process.env.DISABLE_TELEMETRY) {
+ return res.send('/* telemetry disabled */');
+ }
- const script = `
+ const script = `
(()=>{const i=document.createElement('img');
i.setAttribute('src','${TELEMETRY_PIXEL}?v=${CURRENT_VERSION}');
i.setAttribute('style','width:0;height:0;position:absolute;pointer-events:none;');
document.body.appendChild(i);})();
`;
- return res.send(script.replace(/\s\s+/g, ''));
+ return res.send(script.replace(/\s\s+/g, ''));
+ }
+
+ return ok(res);
}
diff --git a/src/pages/api/send.ts b/src/pages/api/send.ts
index 00d72104..e8d3e386 100644
--- a/src/pages/api/send.ts
+++ b/src/pages/api/send.ts
@@ -80,8 +80,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
const { type, payload } = req.body;
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (await hasBlockedIp(req)) {
return forbidden(res);
diff --git a/src/pages/api/share/[id].ts b/src/pages/api/share/[id].ts
index cc782246..876fbef5 100644
--- a/src/pages/api/share/[id].ts
+++ b/src/pages/api/share/[id].ts
@@ -25,8 +25,7 @@ export default async (
req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: shareId } = req.query;
diff --git a/src/pages/api/teams/[id]/index.ts b/src/pages/api/teams/[id]/index.ts
index a5527580..a4f80aba 100644
--- a/src/pages/api/teams/[id]/index.ts
+++ b/src/pages/api/teams/[id]/index.ts
@@ -35,9 +35,7 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: teamId } = req.query;
diff --git a/src/pages/api/teams/[id]/users/[userId].ts b/src/pages/api/teams/[id]/users/[userId].ts
index adb635d5..3b16ac05 100644
--- a/src/pages/api/teams/[id]/users/[userId].ts
+++ b/src/pages/api/teams/[id]/users/[userId].ts
@@ -5,6 +5,7 @@ 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;
@@ -19,9 +20,7 @@ const schema = {
export default async (req: NextApiRequestQueryBody, res: NextApiResponse) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'DELETE') {
const { id: teamId, userId } = req.query;
diff --git a/src/pages/api/teams/[id]/users/index.ts b/src/pages/api/teams/[id]/users/index.ts
index d0efba25..c3181e6a 100644
--- a/src/pages/api/teams/[id]/users/index.ts
+++ b/src/pages/api/teams/[id]/users/index.ts
@@ -1,24 +1,27 @@
+import * as yup from 'yup';
import { canViewTeam } from 'lib/auth';
-import { useAuth } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
+import { useAuth, useValidate } from 'lib/middleware';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getUsersByTeamId } from 'queries';
-export interface TeamUserRequestQuery extends SearchFilter {
+export interface TeamUserRequestQuery extends SearchFilter {
id: string;
}
-export interface TeamUserRequestBody {
- email: string;
- roleId: string;
-}
+const schema = {
+ GET: yup.object().shape({
+ id: yup.string().uuid().required(),
+ }),
+};
export default async (
- req: NextApiRequestQueryBody,
+ req: NextApiRequestQueryBody,
res: NextApiResponse,
) => {
await useAuth(req, res);
+ await useValidate(schema, req, res);
const { id: teamId } = req.query;
@@ -27,12 +30,11 @@ export default async (
return unauthorized(res);
}
- const { page, filter, pageSize } = req.query;
+ const { query, page } = req.query;
const users = await getUsersByTeamId(teamId, {
+ query,
page,
- filter,
- pageSize: +pageSize || undefined,
});
return ok(res, users);
diff --git a/src/pages/api/teams/[id]/websites/[websiteId].ts b/src/pages/api/teams/[id]/websites/[websiteId].ts
index ada1efdc..865ce0d2 100644
--- a/src/pages/api/teams/[id]/websites/[websiteId].ts
+++ b/src/pages/api/teams/[id]/websites/[websiteId].ts
@@ -23,9 +23,7 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: teamId, websiteId } = req.query;
diff --git a/src/pages/api/teams/[id]/websites/index.ts b/src/pages/api/teams/[id]/websites/index.ts
index d4b70b7f..5ff8c665 100644
--- a/src/pages/api/teams/[id]/websites/index.ts
+++ b/src/pages/api/teams/[id]/websites/index.ts
@@ -1,13 +1,14 @@
+import * as yup from 'yup';
import { canViewTeam } from 'lib/auth';
import { useAuth, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getWebsitesByTeamId } from 'queries';
import { createTeamWebsites } from 'queries/admin/teamWebsite';
-export interface TeamWebsiteRequestQuery extends SearchFilter {
+export interface TeamWebsiteRequestQuery extends SearchFilter {
id: string;
}
@@ -15,12 +16,10 @@ 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),
+ ...pageInfo,
}),
POST: yup.object().shape({
id: yup.string().uuid().required(),
@@ -33,9 +32,7 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: teamId } = req.query;
@@ -44,13 +41,7 @@ export default async (
return unauthorized(res);
}
- const { page, filter, pageSize } = req.query;
-
- const websites = await getWebsitesByTeamId(teamId, {
- page,
- filter,
- pageSize: +pageSize || undefined,
- });
+ const websites = await getWebsitesByTeamId(teamId, { ...req.query });
return ok(res, websites);
}
diff --git a/src/pages/api/teams/index.ts b/src/pages/api/teams/index.ts
index 7ef6f533..4a9f4bb4 100644
--- a/src/pages/api/teams/index.ts
+++ b/src/pages/api/teams/index.ts
@@ -2,23 +2,21 @@ import { Team } from '@prisma/client';
import { canCreateTeam } from 'lib/auth';
import { uuid } from 'lib/crypto';
import { useAuth, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
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 TeamsRequestQuery extends SearchFilter {}
export interface TeamsRequestBody {
name: string;
}
-export interface MyTeamsRequestQuery extends SearchFilter {}
-
const schema = {
GET: yup.object().shape({
- ...getFilterValidation(/All|Name|Owner/i),
+ ...pageInfo,
}),
POST: yup.object().shape({
name: yup.string().max(50).required(),
@@ -30,21 +28,18 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
user: { id: userId },
} = req.auth;
if (req.method === 'GET') {
- const { page, filter, pageSize } = req.query;
+ const { page, query } = req.query;
const results = await getTeamsByUserId(userId, {
page,
- filter,
- pageSize: +pageSize || undefined,
+ query,
});
return ok(res, results);
diff --git a/src/pages/api/teams/join.ts b/src/pages/api/teams/join.ts
index 06feda8a..0f9d01fa 100644
--- a/src/pages/api/teams/join.ts
+++ b/src/pages/api/teams/join.ts
@@ -21,9 +21,7 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'POST') {
const { accessCode } = req.body;
diff --git a/src/pages/api/users/[id]/index.ts b/src/pages/api/users/[id]/index.ts
index 3ac560ed..7926d34a 100644
--- a/src/pages/api/users/[id]/index.ts
+++ b/src/pages/api/users/[id]/index.ts
@@ -33,9 +33,7 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
user: { id: userId, isAdmin },
diff --git a/src/pages/api/users/[id]/teams.ts b/src/pages/api/users/[id]/teams.ts
index 72b99b86..bfa0d31a 100644
--- a/src/pages/api/users/[id]/teams.ts
+++ b/src/pages/api/users/[id]/teams.ts
@@ -1,11 +1,12 @@
+import * as yup from 'yup';
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
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 {
+
+export interface UserTeamsRequestQuery extends SearchFilter {
id: string;
}
@@ -18,7 +19,7 @@ export interface UserTeamsRequestBody {
const schema = {
GET: yup.object().shape({
id: yup.string().uuid().required(),
- ...getFilterValidation('/All|Name|Owner/i'),
+ ...pageInfo,
}),
};
@@ -28,9 +29,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { user } = req.auth;
const { id: userId } = req.query;
@@ -40,12 +39,12 @@ export default async (
return unauthorized(res);
}
- const { page, filter, pageSize } = req.query;
+ const { page, query, pageSize } = req.query;
const teams = await getTeamsByUserId(userId, {
+ query,
page,
- filter,
- pageSize: +pageSize || undefined,
+ pageSize,
});
return ok(res, teams);
diff --git a/src/pages/api/users/[id]/usage.ts b/src/pages/api/users/[id]/usage.ts
index b0fc2055..7cb40d61 100644
--- a/src/pages/api/users/[id]/usage.ts
+++ b/src/pages/api/users/[id]/usage.ts
@@ -36,9 +36,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { user } = req.auth;
diff --git a/src/pages/api/users/[id]/websites.ts b/src/pages/api/users/[id]/websites.ts
index ab7d88ef..4eb659c8 100644
--- a/src/pages/api/users/[id]/websites.ts
+++ b/src/pages/api/users/[id]/websites.ts
@@ -1,12 +1,12 @@
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
+import { pageInfo } from 'lib/schema';
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 {
+export interface UserWebsitesRequestQuery extends SearchFilter {
id: string;
includeTeams?: boolean;
onlyTeams?: boolean;
@@ -17,7 +17,7 @@ const schema = {
id: yup.string().uuid().required(),
includeTeams: yup.boolean(),
onlyTeams: yup.boolean(),
- ...getFilterValidation(/All|Name|Domain/i),
+ ...pageInfo,
}),
};
@@ -27,12 +27,18 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { user } = req.auth;
- const { id: userId, page, filter, pageSize, includeTeams, onlyTeams } = req.query;
+ const {
+ id: userId,
+ page = 1,
+ pageSize,
+ orderBy,
+ query = '',
+ includeTeams,
+ onlyTeams,
+ } = req.query;
if (req.method === 'GET') {
if (!user.isAdmin && user.id !== userId) {
@@ -41,8 +47,9 @@ export default async (
const websites = await getWebsitesByUserId(userId, {
page,
- filter,
- pageSize: +pageSize || undefined,
+ pageSize,
+ query,
+ orderBy,
includeTeams,
onlyTeams,
});
diff --git a/src/pages/api/users/index.ts b/src/pages/api/users/index.ts
index 991986e8..b6d81271 100644
--- a/src/pages/api/users/index.ts
+++ b/src/pages/api/users/index.ts
@@ -2,13 +2,14 @@ import { canCreateUser, canViewUsers } from 'lib/auth';
import { ROLES } from 'lib/constants';
import { uuid } from 'lib/crypto';
import { useAuth, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, Role, SearchFilter, User, UserSearchFilterType } from 'lib/types';
-import { getFilterValidation } from 'lib/yup';
+import { NextApiRequestQueryBody, Role, SearchFilter, User } from 'lib/types';
+import { pageInfo } from 'lib/schema';
import { NextApiResponse } from 'next';
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createUser, getUserByUsername, getUsers } from 'queries';
+import * as yup from 'yup';
-export interface UsersRequestQuery extends SearchFilter {}
+export interface UsersRequestQuery extends SearchFilter {}
export interface UsersRequestBody {
username: string;
password: string;
@@ -16,10 +17,9 @@ export interface UsersRequestBody {
role: Role;
}
-import * as yup from 'yup';
const schema = {
GET: yup.object().shape({
- ...getFilterValidation(/All|Username/i),
+ ...pageInfo,
}),
POST: yup.object().shape({
username: yup.string().max(255).required(),
@@ -37,18 +37,16 @@ export default async (
res: NextApiResponse,
) => {
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
if (req.method === 'GET') {
if (!(await canViewUsers(req.auth))) {
return unauthorized(res);
}
- const { page, filter, pageSize } = req.query;
+ const { page, query } = req.query;
- const users = await getUsers({ page, filter, pageSize: pageSize ? +pageSize : null });
+ const users = await getUsers({ page, query });
return ok(res, users);
}
diff --git a/src/pages/api/websites/[id]/active.ts b/src/pages/api/websites/[id]/active.ts
index abc23dd7..ef631a0e 100644
--- a/src/pages/api/websites/[id]/active.ts
+++ b/src/pages/api/websites/[id]/active.ts
@@ -22,9 +22,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId } = req.query;
diff --git a/src/pages/api/websites/[id]/daterange.ts b/src/pages/api/websites/[id]/daterange.ts
index bfa5338e..5bf76a91 100644
--- a/src/pages/api/websites/[id]/daterange.ts
+++ b/src/pages/api/websites/[id]/daterange.ts
@@ -22,9 +22,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId } = req.query;
diff --git a/src/pages/api/websites/[id]/events.ts b/src/pages/api/websites/[id]/events.ts
index 427cb40e..05a651ab 100644
--- a/src/pages/api/websites/[id]/events.ts
+++ b/src/pages/api/websites/[id]/events.ts
@@ -1,32 +1,29 @@
-import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite } from 'lib/auth';
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import moment from 'moment-timezone';
-import { NextApiResponse } from 'next';
-import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
-import { getEventMetrics } from 'queries';
import { parseDateRangeQuery } from 'lib/query';
-
-const unitTypes = ['year', 'month', 'hour', 'day'];
+import { NextApiRequestQueryBody, WebsiteMetric } from 'lib/types';
+import { TimezoneTest, UnitTypeTest } from 'lib/yup';
+import { NextApiResponse } from 'next';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
+import { getEventMetrics } from 'queries';
+import * as yup from 'yup';
export interface WebsiteEventsRequestQuery {
id: string;
startAt: string;
endAt: string;
- unit: string;
- timezone: string;
+ unit?: string;
+ timezone?: string;
url: 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(),
+ unit: UnitTypeTest,
+ timezone: TimezoneTest,
url: yup.string(),
}),
};
@@ -37,9 +34,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId, timezone, url } = req.query;
const { startDate, endDate, unit } = await parseDateRangeQuery(req);
@@ -49,10 +44,6 @@ export default async (
return unauthorized(res);
}
- if (!moment.tz.zone(timezone) || !unitTypes.includes(unit)) {
- return badRequest(res);
- }
-
const events = await getEventMetrics(websiteId, {
startDate,
endDate,
diff --git a/src/pages/api/websites/[id]/index.ts b/src/pages/api/websites/[id]/index.ts
index 0e5aacce..02e31786 100644
--- a/src/pages/api/websites/[id]/index.ts
+++ b/src/pages/api/websites/[id]/index.ts
@@ -22,6 +22,12 @@ const schema = {
GET: yup.object().shape({
id: yup.string().uuid().required(),
}),
+ POST: yup.object().shape({
+ id: yup.string().uuid().required(),
+ name: yup.string(),
+ domain: yup.string(),
+ shareId: yup.string().matches(SHARE_ID_REGEX, { excludeEmptyString: true }).nullable(),
+ }),
};
export default async (
@@ -30,9 +36,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId } = req.query;
@@ -55,10 +59,6 @@ export default async (
let website;
- if (shareId && !shareId.match(SHARE_ID_REGEX)) {
- return serverError(res, 'Invalid share ID.');
- }
-
try {
website = await updateWebsite(websiteId, { name, domain, shareId });
} catch (e: any) {
diff --git a/src/pages/api/websites/[id]/metrics.ts b/src/pages/api/websites/[id]/metrics.ts
index b8c37339..56b0b066 100644
--- a/src/pages/api/websites/[id]/metrics.ts
+++ b/src/pages/api/websites/[id]/metrics.ts
@@ -33,6 +33,18 @@ const schema = {
type: yup.string().required(),
startAt: yup.number().required(),
endAt: yup.number().required(),
+ url: yup.string(),
+ referrer: yup.string(),
+ title: yup.string(),
+ query: yup.string(),
+ os: yup.string(),
+ browser: yup.string(),
+ device: yup.string(),
+ country: yup.string(),
+ region: yup.string(),
+ city: yup.string(),
+ language: yup.string(),
+ event: yup.string(),
}),
};
@@ -42,9 +54,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
id: websiteId,
diff --git a/src/pages/api/websites/[id]/pageviews.ts b/src/pages/api/websites/[id]/pageviews.ts
index 9985ca89..7356c504 100644
--- a/src/pages/api/websites/[id]/pageviews.ts
+++ b/src/pages/api/websites/[id]/pageviews.ts
@@ -1,18 +1,17 @@
-import moment from 'moment-timezone';
-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, useValidate } from 'lib/middleware';
-import { getPageviewStats, getSessionStats } from 'queries';
import { parseDateRangeQuery } from 'lib/query';
+import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
+import { NextApiResponse } from 'next';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
+import { getPageviewStats, getSessionStats } from 'queries';
export interface WebsitePageviewRequestQuery {
id: string;
startAt: number;
endAt: number;
- unit: string;
- timezone: string;
+ unit?: string;
+ timezone?: string;
url?: string;
referrer?: string;
title?: string;
@@ -24,10 +23,24 @@ export interface WebsitePageviewRequestQuery {
city?: string;
}
+import { TimezoneTest, UnitTypeTest } from 'lib/yup';
import * as yup from 'yup';
const schema = {
GET: yup.object().shape({
id: yup.string().uuid().required(),
+ startAt: yup.number().required(),
+ endAt: yup.number().required(),
+ unit: UnitTypeTest,
+ timezone: TimezoneTest,
+ url: yup.string(),
+ referrer: yup.string(),
+ title: yup.string(),
+ os: yup.string(),
+ browser: yup.string(),
+ device: yup.string(),
+ country: yup.string(),
+ region: yup.string(),
+ city: yup.string(),
}),
};
@@ -37,9 +50,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
id: websiteId,
@@ -62,10 +73,6 @@ export default async (
const { startDate, endDate, unit } = await parseDateRangeQuery(req);
- if (!moment.tz.zone(timezone)) {
- return badRequest(res);
- }
-
const filters = {
startDate,
endDate,
diff --git a/src/pages/api/websites/[id]/reports.ts b/src/pages/api/websites/[id]/reports.ts
index 2c7707e8..da6ef7f1 100644
--- a/src/pages/api/websites/[id]/reports.ts
+++ b/src/pages/api/websites/[id]/reports.ts
@@ -1,11 +1,11 @@
import { canViewWebsite } from 'lib/auth';
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
+import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getReportsByWebsiteId } from 'queries';
-export interface ReportsRequestQuery extends SearchFilter {
+export interface ReportsRequestQuery extends SearchFilter {
id: string;
}
@@ -22,9 +22,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId } = req.query;
@@ -33,12 +31,11 @@ export default async (
return unauthorized(res);
}
- const { page, filter, pageSize } = req.query;
+ const { page, query } = req.query;
const data = await getReportsByWebsiteId(websiteId, {
page,
- filter,
- pageSize: +pageSize || undefined,
+ query,
});
return ok(res, data);
diff --git a/src/pages/api/websites/[id]/reset.ts b/src/pages/api/websites/[id]/reset.ts
index cfd5e767..64757411 100644
--- a/src/pages/api/websites/[id]/reset.ts
+++ b/src/pages/api/websites/[id]/reset.ts
@@ -4,14 +4,14 @@ import { useAuth, useCors, useValidate } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { resetWebsite } from 'queries';
+import * as yup from 'yup';
export interface WebsiteResetRequestQuery {
id: string;
}
-import * as yup from 'yup';
const schema = {
- GET: yup.object().shape({
+ POST: yup.object().shape({
id: yup.string().uuid().required(),
}),
};
@@ -22,9 +22,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId } = req.query;
diff --git a/src/pages/api/websites/[id]/stats.ts b/src/pages/api/websites/[id]/stats.ts
index caf54910..4e8d2a88 100644
--- a/src/pages/api/websites/[id]/stats.ts
+++ b/src/pages/api/websites/[id]/stats.ts
@@ -11,23 +11,36 @@ export interface WebsiteStatsRequestQuery {
id: 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;
+ url?: string;
+ referrer?: string;
+ title?: string;
+ query?: string;
+ event?: string;
+ os?: string;
+ browser?: string;
+ device?: string;
+ country?: string;
+ region?: string;
+ city?: string;
}
import * as yup from 'yup';
const schema = {
GET: yup.object().shape({
id: yup.string().uuid().required(),
+ startAt: yup.number().required(),
+ endAt: yup.number().required(),
+ url: yup.string(),
+ referrer: yup.string(),
+ title: yup.string(),
+ query: yup.string(),
+ event: yup.string(),
+ os: yup.string(),
+ browser: yup.string(),
+ device: yup.string(),
+ country: yup.string(),
+ region: yup.string(),
+ city: yup.string(),
}),
};
@@ -37,9 +50,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
id: websiteId,
diff --git a/src/pages/api/websites/[id]/values.ts b/src/pages/api/websites/[id]/values.ts
index 1f479aeb..64fad1f2 100644
--- a/src/pages/api/websites/[id]/values.ts
+++ b/src/pages/api/websites/[id]/values.ts
@@ -5,26 +5,30 @@ import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { EVENT_COLUMNS, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants';
import { getValues } from 'queries';
+import { parseDateRangeQuery } from 'lib/query';
export interface ValuesRequestQuery {
id: string;
+ startAt: number;
+ endAt: number;
}
import * as yup from 'yup';
const schema = {
GET: yup.object().shape({
id: yup.string().uuid().required(),
+ startAt: yup.number().required(),
+ endAt: yup.number().required(),
}),
};
export default async (req: NextApiRequestQueryBody, res: NextApiResponse) => {
await useCors(req, res);
await useAuth(req, res);
-
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const { id: websiteId, type } = req.query;
+ const { startDate, endDate } = await parseDateRangeQuery(req);
if (req.method === 'GET') {
if (!SESSION_COLUMNS.includes(type as string) && !EVENT_COLUMNS.includes(type as string)) {
@@ -35,7 +39,7 @@ export default async (req: NextApiRequestQueryBody, res: Nex
return unauthorized(res);
}
- const values = await getValues(websiteId, FILTER_COLUMNS[type as string]);
+ const values = await getValues(websiteId, FILTER_COLUMNS[type as string], startDate, endDate);
return ok(
res,
diff --git a/src/pages/api/websites/index.ts b/src/pages/api/websites/index.ts
index d6009caf..b30681cf 100644
--- a/src/pages/api/websites/index.ts
+++ b/src/pages/api/websites/index.ts
@@ -1,15 +1,15 @@
import { canCreateWebsite } from 'lib/auth';
import { uuid } from 'lib/crypto';
import { useAuth, useCors, useValidate } from 'lib/middleware';
-import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
+import { NextApiRequestQueryBody, SearchFilter } 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';
+import { pageInfo } from 'lib/schema';
-export interface WebsitesRequestQuery extends SearchFilter {}
+export interface WebsitesRequestQuery extends SearchFilter {}
export interface WebsitesRequestBody {
name: string;
@@ -19,12 +19,12 @@ export interface WebsitesRequestBody {
const schema = {
GET: yup.object().shape({
- ...getFilterValidation(/All|Name|Domain/i),
+ ...pageInfo,
}),
POST: yup.object().shape({
name: yup.string().max(100).required(),
domain: yup.string().max(500).required(),
- shareId: yup.string().max(50),
+ shareId: yup.string().max(50).nullable(),
}),
};
@@ -34,8 +34,7 @@ export default async (
) => {
await useCors(req, res);
await useAuth(req, res);
- req.yup = schema;
- await useValidate(req, res);
+ await useValidate(schema, req, res);
const {
user: { id: userId },
diff --git a/src/pages/console/[[...id]].js b/src/pages/console/[[...id]].js
deleted file mode 100644
index d13d6f68..00000000
--- a/src/pages/console/[[...id]].js
+++ /dev/null
@@ -1,22 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import TestConsole from 'components/pages/console/TestConsole';
-
-export default function ({ disabled }) {
- if (disabled) {
- return null;
- }
-
- return (
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !process.env.ENABLE_TEST_CONSOLE,
- },
- };
-}
diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js
deleted file mode 100644
index c1a3c09e..00000000
--- a/src/pages/dashboard/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import Dashboard from 'components/pages/dashboard/Dashboard';
-import useMessages from 'components/hooks/useMessages';
-
-export default function DashboardPage() {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/index.js b/src/pages/index.js
deleted file mode 100644
index bd4c74be..00000000
--- a/src/pages/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { useEffect } from 'react';
-import { useRouter } from 'next/router';
-
-export default function () {
- const router = useRouter();
-
- useEffect(() => {
- router.push('/dashboard');
- }, []);
-
- return null;
-}
diff --git a/src/pages/login.js b/src/pages/login.js
deleted file mode 100644
index a43f8c1f..00000000
--- a/src/pages/login.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import LoginLayout from 'components/pages/login/LoginLayout';
-import LoginForm from 'components/pages/login/LoginForm';
-
-export default function ({ disabled }) {
- if (disabled) {
- return null;
- }
-
- return (
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.DISABLE_LOGIN,
- },
- };
-}
diff --git a/src/pages/reports/[id].js b/src/pages/reports/[id].js
deleted file mode 100644
index 101881a1..00000000
--- a/src/pages/reports/[id].js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useRouter } from 'next/router';
-import AppLayout from 'components/layout/AppLayout';
-import ReportDetails from 'components/pages/reports/ReportDetails';
-import { useApi, useMessages } from 'components/hooks';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
- const router = useRouter();
- const { id } = router.query;
- const { get, useQuery } = useApi();
- const { data: report } = useQuery(['reports', id], () => get(`/reports/${id}`), {
- enabled: !!id,
- });
-
- if (!id || !report) {
- return null;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/reports/create.js b/src/pages/reports/create.js
deleted file mode 100644
index 08f97f28..00000000
--- a/src/pages/reports/create.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import ReportTemplates from 'components/pages/reports/ReportTemplates';
-import { useMessages } from 'components/hooks';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/reports/funnel.js b/src/pages/reports/funnel.js
deleted file mode 100644
index 78174f7b..00000000
--- a/src/pages/reports/funnel.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import FunnelReport from 'components/pages/reports/funnel/FunnelReport';
-import useMessages from 'components/hooks/useMessages';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/reports/index.js b/src/pages/reports/index.js
deleted file mode 100644
index a1a13a68..00000000
--- a/src/pages/reports/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import ReportsPage from 'components/pages/reports/ReportsPage';
-import { useMessages } from 'components/hooks';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/reports/insights.js b/src/pages/reports/insights.js
deleted file mode 100644
index c5220721..00000000
--- a/src/pages/reports/insights.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import InsightsReport from 'components/pages/reports/insights/InsightsReport';
-import { useMessages } from 'components/hooks';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/reports/retention.js b/src/pages/reports/retention.js
deleted file mode 100644
index 7f5d4cf2..00000000
--- a/src/pages/reports/retention.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import RetentionReport from 'components/pages/reports/retention/RetentionReport';
-import useMessages from 'components/hooks/useMessages';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/settings/profile/index.js b/src/pages/settings/profile/index.js
deleted file mode 100644
index d340c193..00000000
--- a/src/pages/settings/profile/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import SettingsLayout from 'components/layout/SettingsLayout';
-import ProfileSettings from 'components/pages/settings/profile/ProfileSettings';
-import useMessages from 'components/hooks/useMessages';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
- return (
-
-
-
-
-
- );
-}
diff --git a/src/pages/settings/teams/[id].js b/src/pages/settings/teams/[id].js
deleted file mode 100644
index 775a6a08..00000000
--- a/src/pages/settings/teams/[id].js
+++ /dev/null
@@ -1,31 +0,0 @@
-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 'components/hooks/useMessages';
-
-export default function ({ disabled }) {
- const router = useRouter();
- const { id } = router.query;
- const { formatMessage, labels } = useMessages();
-
- if (!id || disabled) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.CLOUD_MODE,
- },
- };
-}
diff --git a/src/pages/settings/teams/index.js b/src/pages/settings/teams/index.js
deleted file mode 100644
index 7e56a7d7..00000000
--- a/src/pages/settings/teams/index.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import SettingsLayout from 'components/layout/SettingsLayout';
-import TeamsList from 'components/pages/settings/teams/TeamsList';
-import useMessages from 'components/hooks/useMessages';
-
-export default function ({ disabled }) {
- const { formatMessage, labels } = useMessages();
- if (disabled) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.CLOUD_MODE,
- },
- };
-}
diff --git a/src/pages/settings/users/[id].js b/src/pages/settings/users/[id].js
deleted file mode 100644
index fdd708fd..00000000
--- a/src/pages/settings/users/[id].js
+++ /dev/null
@@ -1,31 +0,0 @@
-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 'components/hooks/useMessages';
-
-export default function ({ disabled }) {
- const router = useRouter();
- const { id } = router.query;
- const { formatMessage, labels } = useMessages();
-
- if (!id || disabled) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.CLOUD_MODE,
- },
- };
-}
diff --git a/src/pages/settings/users/index.js b/src/pages/settings/users/index.js
deleted file mode 100644
index 90026d87..00000000
--- a/src/pages/settings/users/index.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import SettingsLayout from 'components/layout/SettingsLayout';
-import UsersList from 'components/pages/settings/users/UsersList';
-import useMessages from 'components/hooks/useMessages';
-
-export default function ({ disabled }) {
- const { formatMessage, labels } = useMessages();
- if (disabled) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.CLOUD_MODE,
- },
- };
-}
diff --git a/src/pages/settings/websites/[id].js b/src/pages/settings/websites/[id].js
deleted file mode 100644
index 506da107..00000000
--- a/src/pages/settings/websites/[id].js
+++ /dev/null
@@ -1,31 +0,0 @@
-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 'components/hooks/useMessages';
-
-export default function ({ disabled }) {
- const router = useRouter();
- const { id } = router.query;
- const { formatMessage, labels } = useMessages();
-
- if (!id || disabled) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.CLOUD_MODE,
- },
- };
-}
diff --git a/src/pages/settings/websites/index.js b/src/pages/settings/websites/index.js
deleted file mode 100644
index f4551f4a..00000000
--- a/src/pages/settings/websites/index.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import SettingsLayout from 'components/layout/SettingsLayout';
-import WebsitesList from 'components/pages/settings/websites/WebsitesList';
-import useMessages from 'components/hooks/useMessages';
-
-export default function ({ disabled }) {
- const { formatMessage, labels } = useMessages();
- if (disabled) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
-
-export async function getServerSideProps() {
- return {
- props: {
- disabled: !!process.env.CLOUD_MODE,
- },
- };
-}
diff --git a/src/pages/share/[...id].js b/src/pages/share/[...id].js
deleted file mode 100644
index a2c69df8..00000000
--- a/src/pages/share/[...id].js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { useRouter } from 'next/router';
-import ShareLayout from 'components/layout/ShareLayout';
-import WebsiteDetailsPage from 'components/pages/websites/WebsiteDetailsPage';
-import useShareToken from 'components/hooks/useShareToken';
-
-export default function () {
- const router = useRouter();
- const { id } = router.query;
- const shareId = id?.[0];
- const shareToken = useShareToken(shareId);
-
- if (!shareToken) {
- return null;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/websites/[id]/event-data.js b/src/pages/websites/[id]/event-data.js
deleted file mode 100644
index b99d2fc9..00000000
--- a/src/pages/websites/[id]/event-data.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { useRouter } from 'next/router';
-import AppLayout from 'components/layout/AppLayout';
-import WebsiteEventDataPage from 'components/pages/websites/WebsiteEventDataPage';
-import useMessages from 'components/hooks/useMessages';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
- const router = useRouter();
- const { id } = router.query;
-
- if (!id) {
- return null;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/websites/[id]/index.js b/src/pages/websites/[id]/index.js
deleted file mode 100644
index d3ec5f93..00000000
--- a/src/pages/websites/[id]/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { useRouter } from 'next/router';
-import AppLayout from 'components/layout/AppLayout';
-import WebsiteDetailsPage from 'components/pages/websites/WebsiteDetailsPage';
-import useMessages from 'components/hooks/useMessages';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
- const router = useRouter();
- const { id } = router.query;
-
- if (!id) {
- return null;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/websites/[id]/realtime.js b/src/pages/websites/[id]/realtime.js
deleted file mode 100644
index efe486a5..00000000
--- a/src/pages/websites/[id]/realtime.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useRouter } from 'next/router';
-import AppLayout from 'components/layout/AppLayout';
-import RealtimePage from 'components/pages/realtime/RealtimePage';
-
-export default function () {
- const router = useRouter();
- const { id: websiteId } = router.query;
-
- if (!websiteId) {
- return null;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/websites/[id]/reports.js b/src/pages/websites/[id]/reports.js
deleted file mode 100644
index ccd88081..00000000
--- a/src/pages/websites/[id]/reports.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useRouter } from 'next/router';
-import AppLayout from 'components/layout/AppLayout';
-import WebsiteReportsPage from 'components/pages/websites/WebsiteReportsPage';
-
-export default function () {
- const router = useRouter();
- const { id: websiteId } = router.query;
-
- if (!websiteId) {
- return null;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/websites/index.js b/src/pages/websites/index.js
deleted file mode 100644
index 43eed640..00000000
--- a/src/pages/websites/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import AppLayout from 'components/layout/AppLayout';
-import WebsitesPage from 'components/pages/websites/WebsitesPage';
-import useMessages from 'components/hooks/useMessages';
-
-export default function () {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
- );
-}
diff --git a/src/queries/admin/report.ts b/src/queries/admin/report.ts
index 59eb7035..2f987681 100644
--- a/src/queries/admin/report.ts
+++ b/src/queries/admin/report.ts
@@ -1,5 +1,4 @@
import { Prisma, Report } from '@prisma/client';
-import { REPORT_FILTER_TYPES } from 'lib/constants';
import prisma from 'lib/prisma';
import { FilterResult, ReportSearchFilter } from 'lib/types';
@@ -27,27 +26,21 @@ export async function deleteReport(reportId: string): Promise {
}
export async function getReports(
- ReportSearchFilter: ReportSearchFilter,
+ params: ReportSearchFilter,
options?: { include?: Prisma.ReportInclude },
): Promise> {
- const {
- userId,
- websiteId,
- includeTeams,
- filter,
- filterType = REPORT_FILTER_TYPES.all,
- } = ReportSearchFilter;
+ const { query, userId, websiteId, includeTeams } = params;
const mode = prisma.getSearchMode();
const where: Prisma.ReportWhereInput = {
- ...(userId && { userId: userId }),
- ...(websiteId && { websiteId: websiteId }),
+ userId,
+ websiteId,
AND: [
{
OR: [
{
- ...(userId && { userId: userId }),
+ userId,
},
{
...(includeTeams && {
@@ -71,71 +64,53 @@ export async function getReports(
{
OR: [
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES.name) && {
+ name: {
+ contains: query,
+ ...mode,
+ },
+ },
+ {
+ description: {
+ contains: query,
+ ...mode,
+ },
+ },
+ {
+ type: {
+ contains: query,
+ ...mode,
+ },
+ },
+ {
+ user: {
+ username: {
+ contains: query,
+ ...mode,
+ },
+ },
+ },
+ {
+ website: {
name: {
- startsWith: filter,
+ contains: query,
...mode,
},
- }),
+ },
},
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES.description) && {
- description: {
- startsWith: filter,
+ website: {
+ domain: {
+ contains: query,
...mode,
},
- }),
- },
- {
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES.type) && {
- type: {
- startsWith: filter,
- ...mode,
- },
- }),
- },
- {
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES['user:username']) && {
- user: {
- username: {
- startsWith: filter,
- ...mode,
- },
- },
- }),
- },
- {
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES['website:name']) && {
- website: {
- name: {
- startsWith: filter,
- ...mode,
- },
- },
- }),
- },
- {
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES['website:domain']) && {
- website: {
- domain: {
- startsWith: filter,
- ...mode,
- },
- },
- }),
+ },
},
],
},
],
};
- const [pageFilters, getParameters] = prisma.getPageFilters(ReportSearchFilter);
+ const [pageFilters, pageInfo] = prisma.getPageFilters(params);
const reports = await prisma.client.report.findMany({
where,
@@ -150,13 +125,13 @@ export async function getReports(
return {
data: reports,
count,
- ...getParameters,
+ ...pageInfo,
};
}
export async function getReportsByUserId(
userId: string,
- filter: ReportSearchFilter,
+ filter?: ReportSearchFilter,
): Promise> {
return getReports(
{ userId, ...filter },
diff --git a/src/queries/admin/team.ts b/src/queries/admin/team.ts
index cf731ad4..9947b9a3 100644
--- a/src/queries/admin/team.ts
+++ b/src/queries/admin/team.ts
@@ -1,5 +1,5 @@
import { Prisma, Team } from '@prisma/client';
-import { ROLES, TEAM_FILTER_TYPES } from 'lib/constants';
+import { ROLES } from 'lib/constants';
import { uuid } from 'lib/crypto';
import prisma from 'lib/prisma';
import { FilterResult, TeamSearchFilter } from 'lib/types';
@@ -82,10 +82,10 @@ export async function deleteTeam(
}
export async function getTeams(
- TeamSearchFilter: TeamSearchFilter,
+ filters: TeamSearchFilter,
options?: { include?: Prisma.TeamInclude },
): Promise> {
- const { userId, filter, filterType = TEAM_FILTER_TYPES.all } = TeamSearchFilter;
+ const { userId, query } = filters;
const mode = prisma.getSearchMode();
const where: Prisma.TeamWhereInput = {
@@ -94,29 +94,24 @@ export async function getTeams(
some: { userId },
},
}),
- ...(filter && {
+ ...(query && {
AND: {
OR: [
{
- ...((filterType === TEAM_FILTER_TYPES.all || filterType === TEAM_FILTER_TYPES.name) && {
- name: { startsWith: filter, ...mode },
- }),
+ name: { startsWith: query, ...mode },
},
{
- ...((filterType === TEAM_FILTER_TYPES.all ||
- filterType === TEAM_FILTER_TYPES['user:username']) && {
- teamUser: {
- some: {
- role: ROLES.teamOwner,
- user: {
- username: {
- startsWith: filter,
- ...mode,
- },
+ teamUser: {
+ some: {
+ role: ROLES.teamOwner,
+ user: {
+ username: {
+ startsWith: query,
+ ...mode,
},
},
},
- }),
+ },
},
],
},
@@ -125,7 +120,7 @@ export async function getTeams(
const [pageFilters, getParameters] = prisma.getPageFilters({
orderBy: 'name',
- ...TeamSearchFilter,
+ ...filters,
});
const teams = await prisma.client.team.findMany({
diff --git a/src/queries/admin/user.ts b/src/queries/admin/user.ts
index ee6f778b..b7319942 100644
--- a/src/queries/admin/user.ts
+++ b/src/queries/admin/user.ts
@@ -1,6 +1,6 @@
import { Prisma } from '@prisma/client';
import cache from 'lib/cache';
-import { ROLES, USER_FILTER_TYPES } from 'lib/constants';
+import { ROLES } from 'lib/constants';
import prisma from 'lib/prisma';
import { FilterResult, Role, User, UserSearchFilter } from 'lib/types';
import { getRandomChars } from 'next-basics';
@@ -37,10 +37,10 @@ export async function getUserByUsername(username: string, options: GetUserOption
}
export async function getUsers(
- searchFilter: UserSearchFilter,
+ params: UserSearchFilter,
options?: { include?: Prisma.UserInclude },
): Promise> {
- const { teamId, filter, filterType = USER_FILTER_TYPES.all } = searchFilter;
+ const { teamId, query } = params;
const mode = prisma.getSearchMode();
const where: Prisma.UserWhereInput = {
@@ -51,17 +51,14 @@ export async function getUsers(
},
},
}),
- ...(filter && {
+ ...(query && {
AND: {
OR: [
{
- ...((filterType === USER_FILTER_TYPES.all ||
- filterType === USER_FILTER_TYPES.username) && {
- username: {
- startsWith: filter,
- ...mode,
- },
- }),
+ username: {
+ contains: query,
+ ...mode,
+ },
},
],
},
@@ -70,7 +67,7 @@ export async function getUsers(
const [pageFilters, getParameters] = prisma.getPageFilters({
orderBy: 'username',
- ...searchFilter,
+ ...params,
});
const users = await prisma.client.user
diff --git a/src/queries/admin/website.ts b/src/queries/admin/website.ts
index 6417ade6..0e7f5124 100644
--- a/src/queries/admin/website.ts
+++ b/src/queries/admin/website.ts
@@ -1,6 +1,6 @@
import { Prisma, Website } from '@prisma/client';
import cache from 'lib/cache';
-import { ROLES, WEBSITE_FILTER_TYPES } from 'lib/constants';
+import { ROLES } from 'lib/constants';
import prisma from 'lib/prisma';
import { FilterResult, WebsiteSearchFilter } from 'lib/types';
@@ -19,17 +19,10 @@ export async function getWebsiteByShareId(shareId: string) {
}
export async function getWebsites(
- WebsiteSearchFilter: WebsiteSearchFilter,
+ filters: WebsiteSearchFilter,
options?: { include?: Prisma.WebsiteInclude },
): Promise> {
- const {
- userId,
- teamId,
- includeTeams,
- onlyTeams,
- filter,
- filterType = WEBSITE_FILTER_TYPES.all,
- } = WebsiteSearchFilter;
+ const { userId, teamId, includeTeams, onlyTeams, query } = filters;
const mode = prisma.getSearchMode();
const where: Prisma.WebsiteWhereInput = {
@@ -76,27 +69,23 @@ export async function getWebsites(
],
},
{
- OR: [
- {
- ...((filterType === WEBSITE_FILTER_TYPES.all ||
- filterType === WEBSITE_FILTER_TYPES.name) && {
- name: { startsWith: filter, ...mode },
- }),
- },
- {
- ...((filterType === WEBSITE_FILTER_TYPES.all ||
- filterType === WEBSITE_FILTER_TYPES.domain) && {
- domain: { startsWith: filter, ...mode },
- }),
- },
- ],
+ OR: query
+ ? [
+ {
+ name: { contains: query, ...mode },
+ },
+ {
+ domain: { contains: query, ...mode },
+ },
+ ]
+ : [],
},
],
};
const [pageFilters, getParameters] = prisma.getPageFilters({
orderBy: 'name',
- ...WebsiteSearchFilter,
+ ...filters,
});
const websites = await prisma.client.website.findMany({
@@ -115,10 +104,10 @@ export async function getWebsites(
export async function getWebsitesByUserId(
userId: string,
- filter?: WebsiteSearchFilter,
+ filters?: WebsiteSearchFilter,
): Promise> {
return getWebsites(
- { userId, ...filter },
+ { userId, ...filters },
{
include: {
teamWebsite: {
@@ -143,12 +132,12 @@ export async function getWebsitesByUserId(
export async function getWebsitesByTeamId(
teamId: string,
- filter?: WebsiteSearchFilter,
+ filters?: WebsiteSearchFilter,
): Promise> {
return getWebsites(
{
teamId,
- ...filter,
+ ...filters,
includeTeams: true,
},
{
diff --git a/src/queries/analytics/eventData/getEventDataEvents.ts b/src/queries/analytics/eventData/getEventDataEvents.ts
index 2c8cb0e0..29158503 100644
--- a/src/queries/analytics/eventData/getEventDataEvents.ts
+++ b/src/queries/analytics/eventData/getEventDataEvents.ts
@@ -59,7 +59,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters,
+): Promise<{ eventName: string; fieldName: string; dataType: number; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { event } = filters;
const { params } = await parseFilters(websiteId, filters);
@@ -75,14 +78,23 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(*) as total
from event_data
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_name = {event:String}
group by event_key, data_type, string_value, event_name
order by 1 asc, 2 asc, 3 asc, 4 desc
limit 100
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return {
+ eventName: a.eventName,
+ fieldName: a.fieldName,
+ dataType: Number(a.dataType),
+ total: Number(a.total),
+ };
+ });
+ });
}
return rawQuery(
@@ -94,11 +106,20 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(*) as total
from event_data
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
group by event_key, data_type, event_name
order by 1 asc, 2 asc
limit 100
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return {
+ eventName: a.eventName,
+ fieldName: a.fieldName,
+ dataType: Number(a.dataType),
+ total: Number(a.total),
+ };
+ });
+ });
}
diff --git a/src/queries/analytics/eventData/getEventDataFields.ts b/src/queries/analytics/eventData/getEventDataFields.ts
index ac32b188..df5a8874 100644
--- a/src/queries/analytics/eventData/getEventDataFields.ts
+++ b/src/queries/analytics/eventData/getEventDataFields.ts
@@ -37,7 +37,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters & { fiel
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters & { field?: string }) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters & { field?: string },
+): Promise<{ fieldName: string; dataType: number; fieldValue: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, filters, {
columns: { field: 'event_key' },
@@ -52,12 +55,21 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters & { fiel
count(*) as total
from event_data
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery}
group by event_key, data_type, string_value
order by 3 desc, 2 desc, 1 asc
limit 100
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return {
+ fieldName: a.fieldName,
+ dataType: Number(a.dataType),
+ fieldValue: a.fieldValue,
+ total: Number(a.total),
+ };
+ });
+ });
}
diff --git a/src/queries/analytics/eventData/getEventDataStats.ts b/src/queries/analytics/eventData/getEventDataStats.ts
index cf77ff89..b940e9c4 100644
--- a/src/queries/analytics/eventData/getEventDataStats.ts
+++ b/src/queries/analytics/eventData/getEventDataStats.ts
@@ -42,7 +42,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters,
+): Promise<{ events: number; fields: number; records: number }> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, filters);
@@ -59,11 +62,19 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(*) as "total"
from event_data
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery}
group by event_id, event_key
) as t
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return {
+ events: Number(a.events),
+ fields: Number(a.fields),
+ records: Number(a.records),
+ };
+ });
+ });
}
diff --git a/src/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts
index 778cfee1..53638fbd 100644
--- a/src/queries/analytics/events/getEventMetrics.ts
+++ b/src/queries/analytics/events/getEventMetrics.ts
@@ -40,7 +40,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters,
+): Promise<{ x: string; t: string; y: number }[]> {
const { timezone = 'UTC', unit = 'day' } = filters;
const { rawQuery, getDateQuery, parseFilters } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {
@@ -56,12 +59,16 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(*) y
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${filterQuery}
group by x, t
order by t
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return { x: a.x, t: a.t, y: Number(a.y) };
+ });
+ });
}
diff --git a/src/queries/analytics/events/getEvents.ts b/src/queries/analytics/events/getEvents.ts
index 17528d66..fe074ec2 100644
--- a/src/queries/analytics/events/getEvents.ts
+++ b/src/queries/analytics/events/getEvents.ts
@@ -37,7 +37,7 @@ function clickhouseQuery(websiteId: string, startDate: Date, eventType: number)
event_name as eventName
from website_event
where website_id = {websiteId:UUID}
- and created_at >= {startDate:DateTime}
+ and created_at >= {startDate:DateTime64}
and event_type = {eventType:UInt32}
`,
{
diff --git a/src/queries/analytics/getActiveVisitors.ts b/src/queries/analytics/getActiveVisitors.ts
index db3583eb..962bea35 100644
--- a/src/queries/analytics/getActiveVisitors.ts
+++ b/src/queries/analytics/getActiveVisitors.ts
@@ -24,17 +24,23 @@ async function relationalQuery(websiteId: string) {
);
}
-async function clickhouseQuery(websiteId: string) {
+async function clickhouseQuery(websiteId: string): Promise<{ x: number }> {
const { rawQuery } = clickhouse;
- return rawQuery(
+ const result = rawQuery(
`
select
count(distinct session_id) x
from website_event
where website_id = {websiteId:UUID}
- and created_at >= {startAt:DateTime}
+ and created_at >= {startAt:DateTime64}
`,
{ websiteId, startAt: subMinutes(new Date(), 5) },
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return { x: Number(a.x) };
+ });
+ });
+
+ return result[0] ?? null;
}
diff --git a/src/queries/analytics/getValues.ts b/src/queries/analytics/getValues.ts
index 1f088e3b..45250447 100644
--- a/src/queries/analytics/getValues.ts
+++ b/src/queries/analytics/getValues.ts
@@ -2,14 +2,16 @@ import prisma from 'lib/prisma';
import clickhouse from 'lib/clickhouse';
import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db';
-export async function getValues(...args: [websiteId: string, column: string]) {
+export async function getValues(
+ ...args: [websiteId: string, column: string, startDate: Date, endDate: Date]
+) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
});
}
-async function relationalQuery(websiteId: string, column: string) {
+async function relationalQuery(websiteId: string, column: string, startDate: Date, endDate: Date) {
const { rawQuery } = prisma;
return rawQuery(
@@ -19,12 +21,17 @@ async function relationalQuery(websiteId: string, column: string) {
inner join session
on session.session_id = website_event.session_id
where website_event.website_id = {{websiteId::uuid}}
+ and website_event.created_at between {{startDate}} and {{endDate}}
`,
- { websiteId },
+ {
+ websiteId,
+ startDate,
+ endDate,
+ },
);
}
-async function clickhouseQuery(websiteId: string, column: string) {
+async function clickhouseQuery(websiteId: string, column: string, startDate: Date, endDate: Date) {
const { rawQuery } = clickhouse;
return rawQuery(
@@ -32,7 +39,12 @@ async function clickhouseQuery(websiteId: string, column: string) {
select distinct ${column} as value
from website_event
where website_id = {websiteId:UUID}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
`,
- { websiteId },
+ {
+ websiteId,
+ startDate,
+ endDate,
+ },
);
}
diff --git a/src/queries/analytics/getWebsiteDateRange.ts b/src/queries/analytics/getWebsiteDateRange.ts
index 4fb24733..a4daaafc 100644
--- a/src/queries/analytics/getWebsiteDateRange.ts
+++ b/src/queries/analytics/getWebsiteDateRange.ts
@@ -40,7 +40,7 @@ async function clickhouseQuery(websiteId: string) {
max(created_at) as maxdate
from website_event
where website_id = {websiteId:UUID}
- and created_at >= {startDate:DateTime}
+ and created_at >= {startDate:DateTime64}
`,
params,
);
diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts
index 16519511..654a09a9 100644
--- a/src/queries/analytics/getWebsiteStats.ts
+++ b/src/queries/analytics/getWebsiteStats.ts
@@ -46,7 +46,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters,
+): Promise<{ pageviews: number; uniques: number; bounces: number; totaltime: number }[]> {
const { rawQuery, getDateQuery, parseFilters } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {
...filters,
@@ -69,12 +72,21 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
max(created_at) max_time
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${filterQuery}
group by session_id, time_series
) as t;
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return {
+ pageviews: Number(a.pageviews),
+ uniques: Number(a.uniques),
+ bounces: Number(a.bounces),
+ totaltime: Number(a.totaltime),
+ };
+ });
+ });
}
diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts
index 3cf6c122..4e7e9da2 100644
--- a/src/queries/analytics/pageviews/getPageviewMetrics.ts
+++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts
@@ -48,7 +48,11 @@ async function relationalQuery(websiteId: string, column: string, filters: Query
);
}
-async function clickhouseQuery(websiteId: string, column: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ column: string,
+ filters: QueryFilters,
+): Promise<{ x: string; y: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {
...filters,
@@ -65,7 +69,7 @@ async function clickhouseQuery(websiteId: string, column: string, filters: Query
select ${column} x, count(*) y
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${excludeDomain}
${filterQuery}
@@ -74,5 +78,9 @@ async function clickhouseQuery(websiteId: string, column: string, filters: Query
limit 100
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return { x: a.x, y: Number(a.y) };
+ });
+ });
}
diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts
index d6a980ef..b221e010 100644
--- a/src/queries/analytics/pageviews/getPageviewStats.ts
+++ b/src/queries/analytics/pageviews/getPageviewStats.ts
@@ -36,7 +36,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters,
+): Promise<{ x: string; y: number }[]> {
const { timezone = 'UTC', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateStringQuery, getDateQuery } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {
@@ -55,7 +58,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(*) as y
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${filterQuery}
group by t
@@ -63,5 +66,9 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
order by t
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return { x: a.x, y: Number(a.y) };
+ });
+ });
}
diff --git a/src/queries/analytics/reports/getFunnel.ts b/src/queries/analytics/reports/getFunnel.ts
index 1bbbc878..8dbd8d45 100644
--- a/src/queries/analytics/reports/getFunnel.ts
+++ b/src/queries/analytics/reports/getFunnel.ts
@@ -172,7 +172,7 @@ async function clickhouseQuery(
);
}
- return rawQuery<{ level: number; count: number }[]>(
+ return rawQuery(
`
WITH level0 AS (
select distinct session_id, url_path, referrer_path, created_at
@@ -201,7 +201,7 @@ async function clickhouseQuery(
).then(results => {
return urls.map((a, i) => ({
x: a,
- y: results[i]?.count || 0,
+ y: Number(results[i]?.count) || 0,
z: (1 - Number(results[i]?.count) / Number(results[i - 1]?.count)) * 100 || 0, // drop off
}));
});
diff --git a/src/queries/analytics/reports/getInsights.ts b/src/queries/analytics/reports/getInsights.ts
index fa54488b..c5fac48b 100644
--- a/src/queries/analytics/reports/getInsights.ts
+++ b/src/queries/analytics/reports/getInsights.ts
@@ -75,7 +75,7 @@ async function clickhouseQuery(
${parseFields(fields)}
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${filterQuery}
${parseGroupBy(fields)}
diff --git a/src/queries/analytics/reports/getRetention.ts b/src/queries/analytics/reports/getRetention.ts
index 3c384b6e..fce7841d 100644
--- a/src/queries/analytics/reports/getRetention.ts
+++ b/src/queries/analytics/reports/getRetention.ts
@@ -8,7 +8,7 @@ export async function getRetention(
filters: {
startDate: Date;
endDate: Date;
- timezone: string;
+ timezone?: string;
},
]
) {
@@ -23,7 +23,7 @@ async function relationalQuery(
filters: {
startDate: Date;
endDate: Date;
- timezone: string;
+ timezone?: string;
},
): Promise<
{
@@ -103,7 +103,7 @@ async function clickhouseQuery(
filters: {
startDate: Date;
endDate: Date;
- timezone: string;
+ timezone?: string;
},
): Promise<
{
@@ -172,5 +172,15 @@ async function clickhouseQuery(
startDate,
endDate,
},
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return {
+ date: a.date,
+ day: Number(a.day),
+ visitors: Number(a.visitors),
+ returnVisitors: Number(a.returnVisitors),
+ percentage: Number(a.percentage),
+ };
+ });
+ });
}
diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts
index 43d9ef5a..cccde433 100644
--- a/src/queries/analytics/sessions/getSessionMetrics.ts
+++ b/src/queries/analytics/sessions/getSessionMetrics.ts
@@ -47,7 +47,11 @@ async function relationalQuery(websiteId: string, column: string, filters: Query
);
}
-async function clickhouseQuery(websiteId: string, column: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ column: string,
+ filters: QueryFilters,
+): Promise<{ x: string; y: number }[]> {
const { parseFilters, rawQuery } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {
...filters,
@@ -63,7 +67,7 @@ async function clickhouseQuery(websiteId: string, column: string, filters: Query
${includeCountry ? ', country' : ''}
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${filterQuery}
group by x
@@ -72,5 +76,9 @@ async function clickhouseQuery(websiteId: string, column: string, filters: Query
limit 100
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return { x: a.x, y: Number(a.y) };
+ });
+ });
}
diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts
index 9ed01a59..c977187d 100644
--- a/src/queries/analytics/sessions/getSessionStats.ts
+++ b/src/queries/analytics/sessions/getSessionStats.ts
@@ -36,7 +36,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
);
}
-async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
+async function clickhouseQuery(
+ websiteId: string,
+ filters: QueryFilters,
+): Promise<{ x: string; y: number }[]> {
const { timezone = 'UTC', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateStringQuery, getDateQuery } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {
@@ -55,7 +58,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(distinct session_id) as y
from website_event
where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime} and {endDate:DateTime}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = {eventType:UInt32}
${filterQuery}
group by t
@@ -63,5 +66,9 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
order by t
`,
params,
- );
+ ).then(a => {
+ return Object.values(a).map(a => {
+ return { x: a.x, y: Number(a.y) };
+ });
+ });
}
diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts
index 6936f902..d67edd5e 100644
--- a/src/queries/analytics/sessions/getSessions.ts
+++ b/src/queries/analytics/sessions/getSessions.ts
@@ -42,7 +42,7 @@ async function clickhouseQuery(websiteId: string, startDate: Date) {
city
from website_event
where website_id = {websiteId:UUID}
- and created_at >= {startDate:DateTime}
+ and created_at >= {startDate:DateTime64}
`,
{
websiteId,
diff --git a/src/store/cache.js b/src/store/cache.js
new file mode 100644
index 00000000..b0ef4b81
--- /dev/null
+++ b/src/store/cache.js
@@ -0,0 +1,9 @@
+import { create } from 'zustand';
+
+const store = create(() => ({}));
+
+export function setValue(key, value) {
+ store.setState({ [key]: value });
+}
+
+export default store;
diff --git a/src/store/queries.js b/src/store/queries.js
deleted file mode 100644
index 1de2f04b..00000000
--- a/src/store/queries.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { create } from 'zustand';
-
-const store = create(() => ({}));
-
-export function saveQuery(key, data) {
- store.setState({ [key]: data });
-}
-
-export function getQuery(key) {
- return store.getState()[key];
-}
-
-export default store;
diff --git a/src/store/websites.ts b/src/store/websites.ts
index 5d0eeccd..a9f6b44d 100644
--- a/src/store/websites.ts
+++ b/src/store/websites.ts
@@ -4,10 +4,6 @@ import { DateRange } from 'lib/types';
const store = create(() => ({}));
-export function getWebsiteDateRange(websiteId: string) {
- return store.getState()?.[websiteId];
-}
-
export function setWebsiteDateRange(websiteId: string, dateRange: DateRange) {
store.setState(
produce(state => {
diff --git a/src/styles/variables.css b/src/styles/variables.css
index 726195d2..686fac84 100644
--- a/src/styles/variables.css
+++ b/src/styles/variables.css
@@ -1,4 +1,4 @@
-:root {
+html body {
--primary400: var(--blue800);
--primary500: var(--blue900);
--primary600: var(--blue1000);
diff --git a/src/tracker/index.js b/src/tracker/index.js
index 491eef7d..d5278b21 100644
--- a/src/tracker/index.js
+++ b/src/tracker/index.js
@@ -43,10 +43,11 @@
};
const getPath = url => {
- if (url.substring(0, 4) === 'http') {
- return '/' + url.split('/').splice(3).join('/');
+ try {
+ return new URL(url).pathname;
+ } catch (e) {
+ return url;
}
- return url;
};
const getPayload = () => ({
diff --git a/tsconfig.json b/tsconfig.json
index 71094dd7..9b8b6033 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,8 +30,13 @@
"queries/*": ["./queries/*"],
"store/*": ["./store/*"],
"styles/*": ["./styles/*"]
- }
+ },
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
},
- "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"],
+ "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
diff --git a/yarn.lock b/yarn.lock
index db6d800a..b0eeb778 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -35,7 +35,7 @@
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz"
integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==
-"@babel/core@^7.19.6", "@babel/core@^7.21.3", "@babel/core@^7.9.0":
+"@babel/core@^7.21.3", "@babel/core@^7.9.0":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz"
integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==
@@ -777,7 +777,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.18.6"
-"@babel/plugin-transform-react-constant-elements@^7.18.12", "@babel/plugin-transform-react-constant-elements@^7.21.3":
+"@babel/plugin-transform-react-constant-elements@^7.21.3":
version "7.21.3"
resolved "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz"
integrity sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==
@@ -893,7 +893,7 @@
"@babel/helper-create-regexp-features-plugin" "^7.18.6"
"@babel/helper-plugin-utils" "^7.18.6"
-"@babel/preset-env@^7.19.4", "@babel/preset-env@^7.20.2":
+"@babel/preset-env@^7.20.2":
version "7.21.4"
resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz"
integrity sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==
@@ -997,7 +997,7 @@
"@babel/plugin-transform-react-jsx-development" "^7.18.6"
"@babel/plugin-transform-react-pure-annotations" "^7.18.6"
-"@babel/preset-typescript@^7.18.6", "@babel/preset-typescript@^7.21.0":
+"@babel/preset-typescript@^7.21.0":
version "7.21.4"
resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz"
integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==
@@ -1083,6 +1083,18 @@
"@babel/helper-validator-identifier" "^7.22.5"
to-fast-properties "^2.0.0"
+"@clickhouse/client-common@0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-0.2.2.tgz#0690046241140a51ba5b0c0b9298c3cb3cf20974"
+ integrity sha512-jlom9zLfcDzX9E3off93ZD3CPOkClyM213Y7TN1datkuRGKMvVyj1k0KXaMekhbRev+FTe85CqfoD5eq6qOnPg==
+
+"@clickhouse/client@^0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-0.2.2.tgz#a6358aa2342ee3f2850cdb2f47a9e1d6fbde5757"
+ integrity sha512-2faBnDS4x7ZkcOZqi3f6H967kH+nOfJLhBTWWjz0wTSBnEJBXRtePhN/ZY0NJIKc9Ga5w41Pf67mQgm6Dm/1/w==
+ dependencies:
+ "@clickhouse/client-common" "0.2.2"
+
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
@@ -1331,22 +1343,17 @@
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz"
integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==
-"@eslint-community/eslint-utils@^4.2.0":
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies:
eslint-visitor-keys "^3.3.0"
-"@eslint-community/regexpp@^4.4.0":
- version "4.6.2"
- resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8"
- integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==
-
-"@eslint-community/regexpp@^4.6.1":
- version "4.8.1"
- resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c"
- integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==
+"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1":
+ version "4.9.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.9.0.tgz#7ccb5f58703fa61ffdcbf39e2c604a109e781162"
+ integrity sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ==
"@eslint/eslintrc@^2.1.2":
version "2.1.2"
@@ -1363,10 +1370,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
-"@eslint/js@8.49.0":
- version "8.49.0"
- resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333"
- integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==
+"@eslint/js@8.50.0":
+ version "8.50.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.50.0.tgz#9e93b850f0f3fa35f5fa59adfd03adae8488e484"
+ integrity sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==
"@fastify/accept-negotiator@^1.1.0":
version "1.1.0"
@@ -1408,6 +1415,14 @@
"@formatjs/intl-localematcher" "0.2.25"
tslib "^2.1.0"
+"@formatjs/ecma402-abstract@1.17.2":
+ version "1.17.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.2.tgz#d197c6e26b9fd96ff7ba3b3a0cc2f25f1f2dcac3"
+ integrity sha512-k2mTh0m+IV1HRdU0xXM617tSQTi53tVR2muvYOsBeYcUgEAyxV1FOC7Qj279th3fBVQ+Dj6muvNJZcHSPNdbKg==
+ dependencies:
+ "@formatjs/intl-localematcher" "0.4.2"
+ tslib "^2.4.0"
+
"@formatjs/ecma402-abstract@1.4.0":
version "1.4.0"
resolved "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz"
@@ -1422,12 +1437,12 @@
dependencies:
tslib "^2.0.1"
-"@formatjs/fast-memoize@1.2.1":
- version "1.2.1"
- resolved "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz"
- integrity sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==
+"@formatjs/fast-memoize@2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz#33bd616d2e486c3e8ef4e68c99648c196887802b"
+ integrity sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==
dependencies:
- tslib "^2.1.0"
+ tslib "^2.4.0"
"@formatjs/icu-messageformat-parser@2.1.0":
version "2.1.0"
@@ -1438,6 +1453,15 @@
"@formatjs/icu-skeleton-parser" "1.3.6"
tslib "^2.1.0"
+"@formatjs/icu-messageformat-parser@2.6.2":
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.2.tgz#9bbb29099416e4ce2c7df50029c48985d4f901b3"
+ integrity sha512-nF/Iww7sc5h+1MBCDRm68qpHTCG4xvGzYs/x9HFcDETSGScaJ1Fcadk5U/NXjXeCtzD+DhN4BAwKFVclHfKMdA==
+ dependencies:
+ "@formatjs/ecma402-abstract" "1.17.2"
+ "@formatjs/icu-skeleton-parser" "1.6.2"
+ tslib "^2.4.0"
+
"@formatjs/icu-skeleton-parser@1.3.6":
version "1.3.6"
resolved "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz"
@@ -1446,23 +1470,31 @@
"@formatjs/ecma402-abstract" "1.11.4"
tslib "^2.1.0"
-"@formatjs/intl-displaynames@5.4.3":
- version "5.4.3"
- resolved "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz"
- integrity sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==
+"@formatjs/icu-skeleton-parser@1.6.2":
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.2.tgz#00303034dc08583973c8aa67b96534c49c0bad8d"
+ integrity sha512-VtB9Slo4ZL6QgtDFJ8Injvscf0xiDd4bIV93SOJTBjUF4xe2nAWOoSjLEtqIG+hlIs1sNrVKAaFo3nuTI4r5ZA==
dependencies:
- "@formatjs/ecma402-abstract" "1.11.4"
- "@formatjs/intl-localematcher" "0.2.25"
- tslib "^2.1.0"
+ "@formatjs/ecma402-abstract" "1.17.2"
+ tslib "^2.4.0"
-"@formatjs/intl-listformat@6.5.3":
- version "6.5.3"
- resolved "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz"
- integrity sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==
+"@formatjs/intl-displaynames@6.5.2":
+ version "6.5.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.5.2.tgz#b14ffd0962d5b5cfd71457efc389f0bca83a00db"
+ integrity sha512-uC2VBlz+WydGTDDpJwMTQuPH3CUpTricr91WH1QMfz5oEHg2sB7mUERcZONE/lu8MOe1jREIx4vBciZEVTqkmA==
dependencies:
- "@formatjs/ecma402-abstract" "1.11.4"
- "@formatjs/intl-localematcher" "0.2.25"
- tslib "^2.1.0"
+ "@formatjs/ecma402-abstract" "1.17.2"
+ "@formatjs/intl-localematcher" "0.4.2"
+ tslib "^2.4.0"
+
+"@formatjs/intl-listformat@7.4.2":
+ version "7.4.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.4.2.tgz#c8d86d3b15eead41f74748d1c79d6450fd1bad82"
+ integrity sha512-+6bSVudEQkf12Hh7kuKt8Xv/MyFlqdwA4V4NLnTZW8uYdF9RxlOELDD0rPaOc2++TMKIzI5o6XXwHPvpL6VrPA==
+ dependencies:
+ "@formatjs/ecma402-abstract" "1.17.2"
+ "@formatjs/intl-localematcher" "0.4.2"
+ tslib "^2.4.0"
"@formatjs/intl-localematcher@0.2.25":
version "0.2.25"
@@ -1471,6 +1503,13 @@
dependencies:
tslib "^2.1.0"
+"@formatjs/intl-localematcher@0.4.2":
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.4.2.tgz#7e6e596dbaf2f0c5a7c22da5a01d5c55f4c37e9a"
+ integrity sha512-BGdtJFmaNJy5An/Zan4OId/yR9Ih1OojFjcduX/xOvq798OgWSyDtd6Qd5jqJXwJs1ipe4Fxu9+cshic5Ox2tA==
+ dependencies:
+ tslib "^2.4.0"
+
"@formatjs/intl-numberformat@^5.5.2":
version "5.7.6"
resolved "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz"
@@ -1479,18 +1518,18 @@
"@formatjs/ecma402-abstract" "1.4.0"
tslib "^2.0.1"
-"@formatjs/intl@2.2.1":
- version "2.2.1"
- resolved "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz"
- integrity sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==
+"@formatjs/intl@2.9.3":
+ version "2.9.3"
+ resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.9.3.tgz#e570c4b1afb173dfb1f80a42624425dde9841329"
+ integrity sha512-hclPdyCF1zk2XmhgdXfl5Sd30QEdRBnIijH7Vc1AWz2K0/saVRrxuL3UYn+m3xEyfOa4yDbTWVbmXDL0XEzlsQ==
dependencies:
- "@formatjs/ecma402-abstract" "1.11.4"
- "@formatjs/fast-memoize" "1.2.1"
- "@formatjs/icu-messageformat-parser" "2.1.0"
- "@formatjs/intl-displaynames" "5.4.3"
- "@formatjs/intl-listformat" "6.5.3"
- intl-messageformat "9.13.0"
- tslib "^2.1.0"
+ "@formatjs/ecma402-abstract" "1.17.2"
+ "@formatjs/fast-memoize" "2.2.0"
+ "@formatjs/icu-messageformat-parser" "2.6.2"
+ "@formatjs/intl-displaynames" "6.5.2"
+ "@formatjs/intl-listformat" "7.4.2"
+ intl-messageformat "10.5.3"
+ tslib "^2.4.0"
"@formatjs/ts-transformer@3.9.4":
version "3.9.4"
@@ -1793,10 +1832,10 @@
"@netlify/node-cookies" "^0.1.0"
urlpattern-polyfill "8.0.2"
-"@next/env@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.2.tgz#1c09e6cf1df8b1edf3cf0ca9c0e0119a49802a5d"
- integrity sha512-dUseBIQVax+XtdJPzhwww4GetTjlkRSsXeQnisIJWBaHsnxYcN2RGzsPHi58D6qnkATjnhuAtQTJmR1hKYQQPg==
+"@next/env@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.3.tgz#402da9a0af87f93d853519f0c2a602b1ab637c2c"
+ integrity sha512-X4te86vsbjsB7iO4usY9jLPtZ827Mbx+WcwNBGUOIuswuTAKQtzsuoxc/6KLxCMvogKG795MhrR1LDhYgDvasg==
"@next/eslint-plugin-next@12.3.4":
version "12.3.4"
@@ -1805,50 +1844,50 @@
dependencies:
glob "7.1.7"
-"@next/swc-darwin-arm64@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.2.tgz#f099a36fdd06b1949eb4e190aee95a52b97d3885"
- integrity sha512-7eAyunAWq6yFwdSQliWMmGhObPpHTesiKxMw4DWVxhm5yLotBj8FCR4PXGkpRP2tf8QhaWuVba+/fyAYggqfQg==
+"@next/swc-darwin-arm64@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.3.tgz#f72eac8c7b71d33e0768bd3c8baf68b00fea0160"
+ integrity sha512-6hiYNJxJmyYvvKGrVThzo4nTcqvqUTA/JvKim7Auaj33NexDqSNwN5YrrQu+QhZJCIpv2tULSHt+lf+rUflLSw==
-"@next/swc-darwin-x64@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.2.tgz#b8950fbe150db6f82961619e31fc6e9232fce8f4"
- integrity sha512-WxXYWE7zF1ch8rrNh5xbIWzhMVas6Vbw+9BCSyZvu7gZC5EEiyZNJsafsC89qlaSA7BnmsDXVWQmc+s1feSYbQ==
+"@next/swc-darwin-x64@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.3.tgz#96eda3a1247a713579eb241d76d3f503291c8938"
+ integrity sha512-UpBKxu2ob9scbpJyEq/xPgpdrgBgN3aLYlxyGqlYX5/KnwpJpFuIHU2lx8upQQ7L+MEmz+fA1XSgesoK92ppwQ==
-"@next/swc-linux-arm64-gnu@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.2.tgz#8134d31fa9ad6848561b6969d27a8c07ab090974"
- integrity sha512-URSwhRYrbj/4MSBjLlefPTK3/tvg95TTm6mRaiZWBB6Za3hpHKi8vSdnCMw5D2aP6k0sQQIEG6Pzcfwm+C5vrg==
+"@next/swc-linux-arm64-gnu@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.3.tgz#132e155a029310fffcdfd3e3c4255f7ce9fd2714"
+ integrity sha512-5AzM7Yx1Ky+oLY6pHs7tjONTF22JirDPd5Jw/3/NazJ73uGB05NqhGhB4SbeCchg7SlVYVBeRMrMSZwJwq/xoA==
-"@next/swc-linux-arm64-musl@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.2.tgz#56233fe5140ed437c638194f0a01a3f89821ca89"
- integrity sha512-HefiwAdIygFyNmyVsQeiJp+j8vPKpIRYDlmTlF9/tLdcd3qEL/UEBswa1M7cvO8nHcr27ZTKXz5m7dkd56/Esg==
+"@next/swc-linux-arm64-musl@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.3.tgz#981d7d8fdcf040bd0c89588ef4139c28805f5cf1"
+ integrity sha512-A/C1shbyUhj7wRtokmn73eBksjTM7fFQoY2v/0rTM5wehpkjQRLOXI8WJsag2uLhnZ4ii5OzR1rFPwoD9cvOgA==
-"@next/swc-linux-x64-gnu@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.2.tgz#1947a9dc603e6d5d5a8e99db7d42e2240c78e713"
- integrity sha512-htGVVroW0tdHgMYwKWkxWvVoG2RlAdDXRO1RQxYDvOBQsaV0nZsgKkw0EJJJ3urTYnwKskn/MXm305cOgRxD2w==
+"@next/swc-linux-x64-gnu@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.3.tgz#b8263663acda7b84bc2c4ffa39ca4b0172a78060"
+ integrity sha512-FubPuw/Boz8tKkk+5eOuDHOpk36F80rbgxlx4+xty/U71e3wZZxVYHfZXmf0IRToBn1Crb8WvLM9OYj/Ur815g==
-"@next/swc-linux-x64-musl@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.2.tgz#83eea3985eed84fbbbb1004a555d2f093d4ed245"
- integrity sha512-UBD333GxbHVGi7VDJPPDD1bKnx30gn2clifNJbla7vo5nmBV+x5adyARg05RiT9amIpda6yzAEEUu+s774ldkw==
+"@next/swc-linux-x64-musl@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.3.tgz#cd0bed8ee92032c25090bed9d95602ac698d925f"
+ integrity sha512-DPw8nFuM1uEpbX47tM3wiXIR0Qa+atSzs9Q3peY1urkhofx44o7E1svnq+a5Q0r8lAcssLrwiM+OyJJgV/oj7g==
-"@next/swc-win32-arm64-msvc@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.2.tgz#c3734235e85458b76ec170dd0d6c13c2fdfac5ed"
- integrity sha512-Em9ApaSFIQnWXRT3K6iFnr9uBXymixLc65Xw4eNt7glgH0eiXpg+QhjmgI2BFyc7k4ZIjglfukt9saNpEyolWA==
+"@next/swc-win32-arm64-msvc@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.3.tgz#7f556674ca97e6936220d10c58252cc36522d80a"
+ integrity sha512-zBPSP8cHL51Gub/YV8UUePW7AVGukp2D8JU93IHbVDu2qmhFAn9LWXiOOLKplZQKxnIPUkJTQAJDCWBWU4UWUA==
-"@next/swc-win32-ia32-msvc@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.2.tgz#cf16184af9be8b8f7750833a441c116b7a76b273"
- integrity sha512-TBACBvvNYU+87X0yklSuAseqdpua8m/P79P0SG1fWUvWDDA14jASIg7kr86AuY5qix47nZLEJ5WWS0L20jAUNw==
+"@next/swc-win32-ia32-msvc@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.3.tgz#4912721fb8695f11daec4cde42e73dc57bcc479f"
+ integrity sha512-ONcL/lYyGUj4W37D4I2I450SZtSenmFAvapkJQNIJhrPMhzDU/AdfLkW98NvH1D2+7FXwe7yclf3+B7v28uzBQ==
-"@next/swc-win32-x64-msvc@13.5.2":
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.2.tgz#cf8db00763d9219567655b90853b7d484f3fcad6"
- integrity sha512-LfTHt+hTL8w7F9hnB3H4nRasCzLD/fP+h4/GUVBTxrkMJOnh/7OZ0XbYDKO/uuWwryJS9kZjhxcruBiYwc5UDw==
+"@next/swc-win32-x64-msvc@13.5.3":
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.3.tgz#97340a709febb60ff73003566b99d127d4e5b881"
+ integrity sha512-2Vz2tYWaLqJvLcWbbTlJ5k9AN6JD7a5CN2pAeIzpbecK8ZF/yobA39cXtv6e+Z8c5UJuVOmaTldEAIxvsIux/Q==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -1988,7 +2027,7 @@
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
-"@react-spring/core@~9.7.1", "@react-spring/core@~9.7.3":
+"@react-spring/core@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.7.3.tgz#60056bcb397f2c4f371c6c9a5f882db77ae90095"
integrity sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==
@@ -1997,26 +2036,6 @@
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
-"@react-spring/konva@~9.7.1", "@react-spring/konva@~9.7.3":
- version "9.7.3"
- resolved "https://registry.yarnpkg.com/@react-spring/konva/-/konva-9.7.3.tgz#16bd29dd4860a99e960a72987c8bcfc828b22119"
- integrity sha512-R9sY6SiPGYqz1383P5qppg5z57YfChVknOC1UxxaGxpw+WiZa8fZ4zmZobslrw+os3/+HAXZv8O+EvU/nQpf7g==
- dependencies:
- "@react-spring/animated" "~9.7.3"
- "@react-spring/core" "~9.7.3"
- "@react-spring/shared" "~9.7.3"
- "@react-spring/types" "~9.7.3"
-
-"@react-spring/native@~9.7.1", "@react-spring/native@~9.7.3":
- version "9.7.3"
- resolved "https://registry.yarnpkg.com/@react-spring/native/-/native-9.7.3.tgz#ee38d7c23482cfb4916c9b3c021de2995a4f553a"
- integrity sha512-4mpxX3FuEBCUT6ae2fjhxcJW6bhr2FBwFf274eXB7n+U30Gdg8Wo2qYwcUnmiAA0S3dvP8vLTazx3+CYWFShnA==
- dependencies:
- "@react-spring/animated" "~9.7.3"
- "@react-spring/core" "~9.7.3"
- "@react-spring/shared" "~9.7.3"
- "@react-spring/types" "~9.7.3"
-
"@react-spring/shared@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.7.3.tgz#4cf29797847c689912aec4e62e34c99a4d5d9e53"
@@ -2024,22 +2043,12 @@
dependencies:
"@react-spring/types" "~9.7.3"
-"@react-spring/three@~9.7.1", "@react-spring/three@~9.7.3":
- version "9.7.3"
- resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.7.3.tgz#4358a0c4640efe2972c4f7d0f7cd4efe927471c1"
- integrity sha512-Q1p512CqUlmMK8UMBF/Rj79qndhOWq4XUTayxMP9S892jiXzWQuj+xC3Xvm59DP/D4JXusXpxxqfgoH+hmOktA==
- dependencies:
- "@react-spring/animated" "~9.7.3"
- "@react-spring/core" "~9.7.3"
- "@react-spring/shared" "~9.7.3"
- "@react-spring/types" "~9.7.3"
-
"@react-spring/types@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.3.tgz#ea78fd447cbc2612c1f5d55852e3c331e8172a0b"
integrity sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==
-"@react-spring/web@~9.7.1", "@react-spring/web@~9.7.3":
+"@react-spring/web@^9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.3.tgz#d9f4e17fec259f1d65495a19502ada4f5b57fa3d"
integrity sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==
@@ -2049,16 +2058,6 @@
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
-"@react-spring/zdog@~9.7.1", "@react-spring/zdog@~9.7.3":
- version "9.7.3"
- resolved "https://registry.yarnpkg.com/@react-spring/zdog/-/zdog-9.7.3.tgz#8ccc7316f6d3460ed244d9e3f60de9b4c4a848ac"
- integrity sha512-L+yK/1PvNi9n8cldiJ309k4LdxcPkeWE0W18l1zrP1IBIyd5NB5EPA8DMsGr9gtNnnIujtEzZk+4JIOjT8u/tw==
- dependencies:
- "@react-spring/animated" "~9.7.3"
- "@react-spring/core" "~9.7.3"
- "@react-spring/shared" "~9.7.3"
- "@react-spring/types" "~9.7.3"
-
"@redis/bloom@1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz"
@@ -2176,26 +2175,11 @@
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-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-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"
@@ -2206,51 +2190,26 @@
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-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-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-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-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-preset@8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece"
@@ -2265,20 +2224,6 @@
"@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"
- resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz"
- integrity sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==
- dependencies:
- "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1"
- "@svgr/babel-plugin-remove-jsx-attribute" "*"
- "@svgr/babel-plugin-remove-jsx-empty-expression" "*"
- "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1"
- "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1"
- "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1"
- "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1"
- "@svgr/babel-plugin-transform-svg-component" "^6.5.1"
-
"@svgr/core@8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88"
@@ -2290,17 +2235,6 @@
cosmiconfig "^8.1.3"
snake-case "^3.0.4"
-"@svgr/core@^6.5.1":
- version "6.5.1"
- resolved "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz"
- integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==
- dependencies:
- "@babel/core" "^7.19.6"
- "@svgr/babel-preset" "^6.5.1"
- "@svgr/plugin-jsx" "^6.5.1"
- camelcase "^6.2.0"
- cosmiconfig "^7.0.1"
-
"@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"
@@ -2309,14 +2243,6 @@
"@babel/types" "^7.21.3"
entities "^4.4.0"
-"@svgr/hast-util-to-babel-ast@^6.5.1":
- version "6.5.1"
- resolved "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz"
- integrity sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==
- dependencies:
- "@babel/types" "^7.20.0"
- entities "^4.4.0"
-
"@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"
@@ -2327,16 +2253,6 @@
"@svgr/hast-util-to-babel-ast" "8.0.0"
svg-parser "^2.0.4"
-"@svgr/plugin-jsx@^6.5.1":
- version "6.5.1"
- resolved "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz"
- integrity sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==
- dependencies:
- "@babel/core" "^7.19.6"
- "@svgr/babel-preset" "^6.5.1"
- "@svgr/hast-util-to-babel-ast" "^6.5.1"
- svg-parser "^2.0.4"
-
"@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"
@@ -2346,15 +2262,6 @@
deepmerge "^4.3.1"
svgo "^3.0.2"
-"@svgr/plugin-svgo@^6.5.1":
- version "6.5.1"
- resolved "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz"
- integrity sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==
- dependencies:
- cosmiconfig "^7.0.1"
- deepmerge "^4.2.2"
- svgo "^2.8.0"
-
"@svgr/rollup@^8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/rollup/-/rollup-8.1.0.tgz#2c8e09655336cda4b7843799a5d2a5860300b030"
@@ -2370,19 +2277,19 @@
"@svgr/plugin-jsx" "8.1.0"
"@svgr/plugin-svgo" "8.1.0"
-"@svgr/webpack@^6.2.1":
- version "6.5.1"
- resolved "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz"
- integrity sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==
+"@svgr/webpack@^8.1.0":
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.1.0.tgz#16f1b5346f102f89fda6ec7338b96a701d8be0c2"
+ integrity sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==
dependencies:
- "@babel/core" "^7.19.6"
- "@babel/plugin-transform-react-constant-elements" "^7.18.12"
- "@babel/preset-env" "^7.19.4"
+ "@babel/core" "^7.21.3"
+ "@babel/plugin-transform-react-constant-elements" "^7.21.3"
+ "@babel/preset-env" "^7.20.2"
"@babel/preset-react" "^7.18.6"
- "@babel/preset-typescript" "^7.18.6"
- "@svgr/core" "^6.5.1"
- "@svgr/plugin-jsx" "^6.5.1"
- "@svgr/plugin-svgo" "^6.5.1"
+ "@babel/preset-typescript" "^7.21.0"
+ "@svgr/core" "8.1.0"
+ "@svgr/plugin-jsx" "8.1.0"
+ "@svgr/plugin-svgo" "8.1.0"
"@swc/helpers@0.5.2":
version "0.5.2"
@@ -2509,6 +2416,11 @@
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
+"@types/json-schema@^7.0.12":
+ version "7.0.13"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85"
+ integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==
+
"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.9":
version "7.0.12"
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz"
@@ -2555,29 +2467,24 @@
integrity sha512-5jY9RhV7c0Z4Jy09G+NIDTsCZ5G0L5n+Z+p+Y7t5VJHM30bgwzSjVtlcBxqAj+6L/swIlvtOSzr8rBk/aNyV2g==
"@types/node@^18.11.9":
- version "18.17.18"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.18.tgz#acae19ad9011a2ab3d792232501c95085ba1838f"
- integrity sha512-/4QOuy3ZpV7Ya1GTRz5CYSz3DgkKpyUptXuQ5PPce7uuyJAOR7r9FhkmxJfvcNUXyklbC63a+YvB3jxy7s9ngw==
+ version "18.18.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.0.tgz#bd19d5133a6e5e2d0152ec079ac27c120e7f1763"
+ integrity sha512-3xA4X31gHT1F1l38ATDIL9GpRLdwVhnEFC8Uikv5ZLlXATwrCYyPq7ZWHxzxc3J/30SUiwiYT+bQe0/XvKlWbw==
"@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"
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
-"@types/parse-json@^4.0.0":
- version "4.0.0"
- resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
- integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
-
"@types/prop-types@*":
- version "15.7.6"
- resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.6.tgz#bbf819813d6be21011b8f5801058498bec555572"
- integrity sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==
+ version "15.7.7"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.7.tgz#f9361f7b87fd5d8188b2c998db0a1f47e9fb391a"
+ integrity sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog==
"@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==
+ version "18.2.8"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.8.tgz#338f1b0a646c9f10e0a97208c1d26b9f473dffd6"
+ integrity sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw==
dependencies:
"@types/react" "*"
@@ -2591,19 +2498,19 @@
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
-"@types/react@*", "@types/react@16 || 17 || 18":
- version "18.0.25"
- resolved "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz"
- integrity sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==
+"@types/react@*", "@types/react@^18.0.25":
+ version "18.2.23"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.23.tgz#60ad6cf4895e93bed858db0e03bcc4ff97d0410e"
+ integrity sha512-qHLW6n1q2+7KyBEYnrZpcsAmU/iiCh9WGCKgXvMxx89+TYdJWRjZohVIo9XTcoLhfX3+/hP0Pbulu3bCZQ9PSA==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
-"@types/react@^18.0.25":
- version "18.2.22"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.22.tgz#abe778a1c95a07fa70df40a52d7300a40b949ccb"
- integrity sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==
+"@types/react@16 || 17 || 18":
+ version "18.0.25"
+ resolved "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz"
+ integrity sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@@ -2615,9 +2522,9 @@
integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==
"@types/scheduler@*":
- version "0.16.3"
- resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
- integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==
+ version "0.16.4"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.4.tgz#fedc3e5b15c26dc18faae96bf1317487cb3658cf"
+ integrity sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==
"@types/schema-utils@^2.4.0":
version "2.4.0"
@@ -2626,26 +2533,27 @@
dependencies:
schema-utils "*"
-"@types/semver@^7.3.12":
- version "7.5.0"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a"
- integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==
+"@types/semver@^7.5.0":
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04"
+ integrity sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==
-"@typescript-eslint/eslint-plugin@^5.50.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
- integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==
+"@typescript-eslint/eslint-plugin@^6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz#d98046e9f7102d49a93d944d413c6055c47fafd7"
+ integrity sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==
dependencies:
- "@eslint-community/regexpp" "^4.4.0"
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/type-utils" "5.62.0"
- "@typescript-eslint/utils" "5.62.0"
+ "@eslint-community/regexpp" "^4.5.1"
+ "@typescript-eslint/scope-manager" "6.7.3"
+ "@typescript-eslint/type-utils" "6.7.3"
+ "@typescript-eslint/utils" "6.7.3"
+ "@typescript-eslint/visitor-keys" "6.7.3"
debug "^4.3.4"
graphemer "^1.4.0"
- ignore "^5.2.0"
- natural-compare-lite "^1.4.0"
- semver "^7.3.7"
- tsutils "^3.21.0"
+ ignore "^5.2.4"
+ natural-compare "^1.4.0"
+ semver "^7.5.4"
+ ts-api-utils "^1.0.1"
"@typescript-eslint/parser@^5.21.0":
version "5.59.11"
@@ -2657,14 +2565,15 @@
"@typescript-eslint/typescript-estree" "5.59.11"
debug "^4.3.4"
-"@typescript-eslint/parser@^5.50.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
- integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
+"@typescript-eslint/parser@^6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.3.tgz#aaf40092a32877439e5957e18f2d6a91c82cc2fd"
+ integrity sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==
dependencies:
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/typescript-estree" "5.62.0"
+ "@typescript-eslint/scope-manager" "6.7.3"
+ "@typescript-eslint/types" "6.7.3"
+ "@typescript-eslint/typescript-estree" "6.7.3"
+ "@typescript-eslint/visitor-keys" "6.7.3"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.59.11":
@@ -2675,33 +2584,33 @@
"@typescript-eslint/types" "5.59.11"
"@typescript-eslint/visitor-keys" "5.59.11"
-"@typescript-eslint/scope-manager@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
- integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
+"@typescript-eslint/scope-manager@6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz#07e5709c9bdae3eaf216947433ef97b3b8b7d755"
+ integrity sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==
dependencies:
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/visitor-keys" "5.62.0"
+ "@typescript-eslint/types" "6.7.3"
+ "@typescript-eslint/visitor-keys" "6.7.3"
-"@typescript-eslint/type-utils@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
- integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==
+"@typescript-eslint/type-utils@6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz#c2c165c135dda68a5e70074ade183f5ad68f3400"
+ integrity sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==
dependencies:
- "@typescript-eslint/typescript-estree" "5.62.0"
- "@typescript-eslint/utils" "5.62.0"
+ "@typescript-eslint/typescript-estree" "6.7.3"
+ "@typescript-eslint/utils" "6.7.3"
debug "^4.3.4"
- tsutils "^3.21.0"
+ ts-api-utils "^1.0.1"
"@typescript-eslint/types@5.59.11":
version "5.59.11"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.11.tgz#1a9018fe3c565ba6969561f2a49f330cf1fe8db1"
integrity sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==
-"@typescript-eslint/types@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
- integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
+"@typescript-eslint/types@6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.3.tgz#0402b5628a63f24f2dc9d4a678e9a92cc50ea3e9"
+ integrity sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==
"@typescript-eslint/typescript-estree@5.59.11":
version "5.59.11"
@@ -2716,32 +2625,31 @@
semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/typescript-estree@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
- integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
+"@typescript-eslint/typescript-estree@6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz#ec5bb7ab4d3566818abaf0e4a8fa1958561b7279"
+ integrity sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==
dependencies:
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/visitor-keys" "5.62.0"
+ "@typescript-eslint/types" "6.7.3"
+ "@typescript-eslint/visitor-keys" "6.7.3"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
- semver "^7.3.7"
- tsutils "^3.21.0"
+ semver "^7.5.4"
+ ts-api-utils "^1.0.1"
-"@typescript-eslint/utils@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
- integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
+"@typescript-eslint/utils@6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.3.tgz#96c655816c373135b07282d67407cb577f62e143"
+ integrity sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==
dependencies:
- "@eslint-community/eslint-utils" "^4.2.0"
- "@types/json-schema" "^7.0.9"
- "@types/semver" "^7.3.12"
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/typescript-estree" "5.62.0"
- eslint-scope "^5.1.1"
- semver "^7.3.7"
+ "@eslint-community/eslint-utils" "^4.4.0"
+ "@types/json-schema" "^7.0.12"
+ "@types/semver" "^7.5.0"
+ "@typescript-eslint/scope-manager" "6.7.3"
+ "@typescript-eslint/types" "6.7.3"
+ "@typescript-eslint/typescript-estree" "6.7.3"
+ semver "^7.5.4"
"@typescript-eslint/visitor-keys@5.59.11":
version "5.59.11"
@@ -2751,19 +2659,20 @@
"@typescript-eslint/types" "5.59.11"
eslint-visitor-keys "^3.3.0"
-"@typescript-eslint/visitor-keys@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
- integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==
+"@typescript-eslint/visitor-keys@6.7.3":
+ version "6.7.3"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz#83809631ca12909bd2083558d2f93f5747deebb2"
+ integrity sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==
dependencies:
- "@typescript-eslint/types" "5.62.0"
- eslint-visitor-keys "^3.3.0"
+ "@typescript-eslint/types" "6.7.3"
+ eslint-visitor-keys "^3.4.1"
-"@umami/prisma-client@^0.2.0":
- version "0.2.0"
- resolved "https://registry.npmjs.org/@umami/prisma-client/-/prisma-client-0.2.0.tgz"
- integrity sha512-+27dd4DLl8SvbbIYG1mdm6pIZd+UzQI7eZGNjQ9ONeWO0jr+/wiVnPIXUzd8w4R/OoM4ChpI3mBZPqcWa5MAOw==
+"@umami/prisma-client@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@umami/prisma-client/-/prisma-client-0.3.0.tgz#fea35a44c76af0e4ce58288107cda3ee76fc80ba"
+ integrity sha512-88y/WJX2TEZaUfP+PTretGUL6YdwZCBbhaoeC87eTF3l1aG0Lv3TsmW0lJy5rbKpVqrFJ8zrtvCMP/vt7WeIjg==
dependencies:
+ chalk "^4.1.2"
debug "^4.3.4"
"@umami/redis-client@^0.15.0":
@@ -2869,14 +2778,6 @@
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.2.36.tgz"
integrity sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ==
-JSONStream@1.3.4:
- version "1.3.4"
- resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz"
- integrity sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==
- dependencies:
- jsonparse "^1.2.0"
- through ">=2.2.7 <3"
-
acorn-dynamic-import@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz"
@@ -2939,7 +2840,7 @@ ajv-keywords@^5.0.0:
dependencies:
fast-deep-equal "^3.1.3"
-ajv@^6.12.3, ajv@^6.12.4:
+ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2969,23 +2870,23 @@ ajv@^8.0.1:
require-from-string "^2.0.2"
uri-js "^4.2.2"
-ansi-colors@^4.1.1:
- version "4.1.1"
- resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz"
- integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
-
-ansi-escapes@^4.3.0:
- version "4.3.2"
- resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz"
- integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+ansi-escapes@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6"
+ integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==
dependencies:
- type-fest "^0.21.3"
+ type-fest "^1.0.2"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+ansi-regex@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
+ integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
+
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -3000,6 +2901,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
+ansi-styles@^6.0.0, ansi-styles@^6.1.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+ integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
anymatch@^3.1.3, anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
@@ -3138,18 +3044,6 @@ arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==
-asn1@~0.2.3:
- version "0.2.6"
- resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz"
- integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
- dependencies:
- safer-buffer "~2.1.0"
-
-assert-plus@1.0.0, assert-plus@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
- integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
-
ast-types-flow@^0.0.7:
version "0.0.7"
resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz"
@@ -3160,11 +3054,6 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
-asynckit@^0.4.0:
- version "0.4.0"
- resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
- integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
-
at-least-node@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz"
@@ -3187,16 +3076,6 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
-aws-sign2@~0.7.0:
- version "0.7.0"
- resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz"
- integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==
-
-aws4@^1.8.0:
- version "1.11.0"
- resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz"
- integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
-
axe-core@^4.4.3:
version "4.5.2"
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz"
@@ -3267,13 +3146,6 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
-bcrypt-pbkdf@^1.0.0:
- version "1.0.2"
- resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz"
- integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==
- dependencies:
- tweetnacl "^0.14.3"
-
bcryptjs@^2.4.3:
version "2.4.3"
resolved "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz"
@@ -3430,10 +3302,10 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001426, can
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz"
integrity sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==
-caseless@~0.12.0:
- version "0.12.0"
- resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz"
- integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
+chalk@5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
+ integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
@@ -3511,34 +3383,20 @@ clean-stack@^2.0.0:
resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-cli-cursor@^3.1.0:
+cli-cursor@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea"
+ integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==
+ dependencies:
+ restore-cursor "^4.0.0"
+
+cli-truncate@^3.1.0:
version "3.1.0"
- resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz"
- integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389"
+ integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==
dependencies:
- restore-cursor "^3.1.0"
-
-cli-truncate@2.1.0, cli-truncate@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz"
- integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
- dependencies:
- slice-ansi "^3.0.0"
- string-width "^4.2.0"
-
-clickhouse@^2.5.0:
- version "2.6.0"
- resolved "https://registry.npmjs.org/clickhouse/-/clickhouse-2.6.0.tgz"
- integrity sha512-HC5OV99GJOup4qZsTuWWPpXlj+847Z0OeygDU2x22rNYost0V/vWapzFWYZdV/5iRbGMrhFQPOyQEzmGvoaWRQ==
- dependencies:
- JSONStream "1.3.4"
- lodash "4.17.21"
- querystring "0.2.0"
- request "2.88.0"
- stream2asynciter "1.0.3"
- through "2.3.8"
- tsv "0.2.0"
- uuid "3.4.0"
+ slice-ansi "^5.0.0"
+ string-width "^5.0.0"
client-only@0.0.1:
version "0.0.1"
@@ -3609,22 +3467,20 @@ 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.1.0, colorette@^1.4.0:
+colorette@^1.1.0:
version "1.4.0"
resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz"
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
-colorette@^2.0.16:
+colorette@^2.0.20:
version "2.0.20"
- resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
-combined-stream@^1.0.6, combined-stream@~1.0.6:
- version "1.0.8"
- resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
- integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
- dependencies:
- delayed-stream "~1.0.0"
+commander@11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67"
+ integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==
commander@2, commander@^2.20.0, commander@^2.20.3:
version "2.20.3"
@@ -3636,7 +3492,7 @@ commander@2.20.0:
resolved "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
-commander@8, commander@^8.2.0:
+commander@8:
version "8.3.0"
resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
@@ -3690,11 +3546,6 @@ core-js-pure@^3.25.1:
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz"
integrity sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==
-core-util-is@1.0.2:
- version "1.0.2"
- resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
- integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==
-
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz"
@@ -3703,17 +3554,6 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"
-cosmiconfig@^7.0.1:
- version "7.1.0"
- resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz"
- integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
- dependencies:
- "@types/parse-json" "^4.0.0"
- import-fresh "^3.2.1"
- parse-json "^5.0.0"
- path-type "^4.0.0"
- yaml "^1.10.0"
-
cosmiconfig@^8.1.3:
version "8.1.3"
resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz"
@@ -4029,13 +3869,6 @@ damerau-levenshtein@^1.0.8:
resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
-dashdash@^1.12.0:
- version "1.14.1"
- resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz"
- integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==
- dependencies:
- assert-plus "^1.0.0"
-
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
@@ -4063,6 +3896,13 @@ debounce@^1.2.1:
resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz"
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
+debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -4070,13 +3910,6 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
-debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
- version "4.3.4"
- resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
- integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
- dependencies:
- ms "2.1.2"
-
decamelize-keys@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8"
@@ -4168,11 +4001,6 @@ del@^6.0.0:
rimraf "^3.0.2"
slash "^3.0.0"
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
- integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
-
denque@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
@@ -4302,13 +4130,10 @@ dotenv@^10.0.0:
resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
-ecc-jsbn@~0.1.1:
- version "0.1.2"
- resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz"
- integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==
- dependencies:
- jsbn "~0.1.0"
- safer-buffer "^2.1.0"
+eastasianwidth@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+ integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
@@ -4339,13 +4164,6 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
-enquirer@^2.3.6:
- version "2.3.6"
- resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
- integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
- dependencies:
- ansi-colors "^4.1.1"
-
entities@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
@@ -4661,14 +4479,6 @@ eslint-plugin-react@^7.31.7:
semver "^6.3.0"
string.prototype.matchall "^4.0.8"
-eslint-scope@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^4.1.1"
-
eslint-scope@^7.2.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
@@ -4683,14 +4493,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint@^8.33.0:
- version "8.49.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42"
- integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==
+ version "8.50.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.50.0.tgz#2ae6015fee0240fcd3f83e1e25df0287f487d6b2"
+ integrity sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1"
"@eslint/eslintrc" "^2.1.2"
- "@eslint/js" "8.49.0"
+ "@eslint/js" "8.50.0"
"@humanwhocodes/config-array" "^0.11.11"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
@@ -4753,11 +4563,6 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
-estraverse@^4.1.1:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
- integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-
estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
@@ -4788,6 +4593,26 @@ eventemitter3@^4.0.4:
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+eventemitter3@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+ integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
+execa@7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9"
+ integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.1"
+ human-signals "^4.3.0"
+ is-stream "^3.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^5.1.0"
+ onetime "^6.0.0"
+ signal-exit "^3.0.7"
+ strip-final-newline "^3.0.0"
+
execa@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@@ -4808,11 +4633,6 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
-extend@~3.0.2:
- version "3.0.2"
- resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
- integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-
extract-react-intl-messages@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/extract-react-intl-messages/-/extract-react-intl-messages-4.1.1.tgz"
@@ -4834,16 +4654,6 @@ extract-react-intl-messages@^4.1.1:
sort-keys "^4.0.0"
write-json-file "^4.3.0"
-extsprintf@1.3.0:
- version "1.3.0"
- resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz"
- integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==
-
-extsprintf@^1.2.0:
- version "1.4.1"
- resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz"
- integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
-
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -4977,20 +4787,6 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
-forever-agent@~0.6.1:
- version "0.6.1"
- resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
- integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==
-
-form-data@~2.3.2:
- version "2.3.3"
- resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz"
- integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.6"
- mime-types "^2.1.12"
-
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
@@ -5109,17 +4905,12 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@
has-proto "^1.0.1"
has-symbols "^1.0.3"
-get-own-enumerable-property-symbols@^3.0.0:
- version "3.0.2"
- resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz"
- integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
-
get-port-please@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/get-port-please/-/get-port-please-3.1.1.tgz#2556623cddb4801d823c0a6a15eec038abb483be"
integrity sha512-3UBAyM3u4ZBVYDsxOQfJDxEa6XTbpBDrOjp4mf7ExFRt5BKs/QywQQiJsh2B+hxcZLSapWqCRvElUe8DnKcFHA==
-get-stream@^6.0.0:
+get-stream@^6.0.0, get-stream@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -5132,13 +4923,6 @@ get-symbol-description@^1.0.0:
call-bind "^1.0.2"
get-intrinsic "^1.1.1"
-getpass@^0.1.1:
- version "0.1.7"
- resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz"
- integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==
- dependencies:
- assert-plus "^1.0.0"
-
github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
@@ -5319,19 +5103,6 @@ h3@^1.7.1, h3@^1.8.1:
uncrypto "^0.1.3"
unenv "^1.7.4"
-har-schema@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz"
- integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==
-
-har-validator@~5.1.0:
- version "5.1.5"
- resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz"
- integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
- dependencies:
- ajv "^6.12.3"
- har-schema "^2.0.0"
-
hard-rejection@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
@@ -5412,24 +5183,20 @@ http-shutdown@^1.2.2:
resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f"
integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==
-http-signature@~1.2.0:
- version "1.2.0"
- resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz"
- integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==
- dependencies:
- assert-plus "^1.0.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
-husky@^7.0.0:
- version "7.0.4"
- resolved "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz"
- integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
+human-signals@^4.3.0:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2"
+ integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==
+
+husky@^8.0.3:
+ version "8.0.3"
+ resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
+ integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
icss-replace-symbols@^1.1.0:
version "1.1.0"
@@ -5559,15 +5326,15 @@ intl-messageformat-parser@^5.3.7:
dependencies:
"@formatjs/intl-numberformat" "^5.5.2"
-intl-messageformat@9.13.0:
- version "9.13.0"
- resolved "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz"
- integrity sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==
+intl-messageformat@10.5.3:
+ version "10.5.3"
+ resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.3.tgz#db0779d4a1988faa2977d76574489b7a25f0d5d0"
+ integrity sha512-TzKn1uhJBMyuKTO4zUX47SU+d66fu1W9tVzIiZrQ6hBqQQeYscBMIzKL/qEXnFbJrH9uU5VV3+T5fWib4SIcKA==
dependencies:
- "@formatjs/ecma402-abstract" "1.11.4"
- "@formatjs/fast-memoize" "1.2.1"
- "@formatjs/icu-messageformat-parser" "2.1.0"
- tslib "^2.1.0"
+ "@formatjs/ecma402-abstract" "1.17.2"
+ "@formatjs/fast-memoize" "2.2.0"
+ "@formatjs/icu-messageformat-parser" "2.6.2"
+ tslib "^2.4.0"
ioredis@^5.3.2:
version "5.3.2"
@@ -5713,6 +5480,11 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+is-fullwidth-code-point@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88"
+ integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
+
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
@@ -5747,11 +5519,6 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-is-obj@^1.0.1:
- version "1.0.1"
- resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz"
- integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
-
is-path-cwd@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz"
@@ -5802,11 +5569,6 @@ is-regex@^1.1.4:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
-is-regexp@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz"
- integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
-
is-shared-array-buffer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
@@ -5819,6 +5581,11 @@ is-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+is-stream@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
+ integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
+
is-string@^1.0.5, is-string@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
@@ -5840,7 +5607,7 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9:
dependencies:
which-typed-array "^1.1.11"
-is-typedarray@^1.0.0, is-typedarray@~1.0.0:
+is-typedarray@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
@@ -5874,11 +5641,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
-isstream@~0.1.2:
- version "0.1.2"
- resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz"
- integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
-
jest-worker@^26.2.1:
version "26.6.2"
resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz"
@@ -5918,11 +5680,6 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
-jsbn@~0.1.0:
- version "0.1.1"
- resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz"
- integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
-
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
@@ -5958,11 +5715,6 @@ json-schema-traverse@^1.0.0:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
-json-schema@0.4.0:
- version "0.4.0"
- resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz"
- integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
-
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -5975,11 +5727,6 @@ json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
-json-stringify-safe@~5.0.1:
- version "5.0.1"
- resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
- integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
-
json5@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
@@ -6023,11 +5770,6 @@ jsonify@~0.0.0:
resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
integrity sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA==
-jsonparse@^1.2.0:
- version "1.3.1"
- resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz"
- integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
-
jsonwebtoken@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz"
@@ -6038,16 +5780,6 @@ jsonwebtoken@^9.0.0:
ms "^2.1.1"
semver "^7.3.8"
-jsprim@^1.2.2:
- version "1.4.2"
- resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz"
- integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==
- dependencies:
- assert-plus "1.0.0"
- extsprintf "1.3.0"
- json-schema "0.4.0"
- verror "1.10.0"
-
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
version "3.3.3"
resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz"
@@ -6120,7 +5852,7 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
-lilconfig@^2.0.3, lilconfig@^2.0.5:
+lilconfig@2.1.0, lilconfig@^2.0.3, lilconfig@^2.0.5:
version "2.1.0"
resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz"
integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
@@ -6130,25 +5862,21 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-lint-staged@^11.0.0:
- version "11.2.6"
- resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-11.2.6.tgz"
- integrity sha512-Vti55pUnpvPE0J9936lKl0ngVeTdSZpEdTNhASbkaWX7J5R9OEifo1INBGQuGW4zmy6OG+TcWPJ3m5yuy5Q8Tg==
+lint-staged@^14.0.1:
+ version "14.0.1"
+ resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-14.0.1.tgz#57dfa3013a3d60762d9af5d9c83bdb51291a6232"
+ integrity sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==
dependencies:
- cli-truncate "2.1.0"
- colorette "^1.4.0"
- commander "^8.2.0"
- cosmiconfig "^7.0.1"
- debug "^4.3.2"
- enquirer "^2.3.6"
- execa "^5.1.1"
- listr2 "^3.12.2"
- micromatch "^4.0.4"
- normalize-path "^3.0.0"
- please-upgrade-node "^3.2.0"
- string-argv "0.3.1"
- stringify-object "3.3.0"
- supports-color "8.1.1"
+ chalk "5.3.0"
+ commander "11.0.0"
+ debug "4.3.4"
+ execa "7.2.0"
+ lilconfig "2.1.0"
+ listr2 "6.6.1"
+ micromatch "4.0.5"
+ pidtree "0.6.0"
+ string-argv "0.3.2"
+ yaml "2.3.1"
listhen@^1.2.2, listhen@^1.4.4:
version "1.5.5"
@@ -6173,19 +5901,17 @@ listhen@^1.2.2, listhen@^1.4.4:
untun "^0.1.2"
uqr "^0.1.2"
-listr2@^3.12.2:
- version "3.14.0"
- resolved "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz"
- integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==
+listr2@6.6.1:
+ version "6.6.1"
+ resolved "https://registry.yarnpkg.com/listr2/-/listr2-6.6.1.tgz#08b2329e7e8ba6298481464937099f4a2cd7f95d"
+ integrity sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==
dependencies:
- cli-truncate "^2.1.0"
- colorette "^2.0.16"
- log-update "^4.0.0"
- p-map "^4.0.0"
+ cli-truncate "^3.1.0"
+ colorette "^2.0.20"
+ eventemitter3 "^5.0.1"
+ log-update "^5.0.1"
rfdc "^1.3.0"
- rxjs "^7.5.1"
- through "^2.3.8"
- wrap-ansi "^7.0.0"
+ wrap-ansi "^8.1.0"
load-json-file@^4.0.0:
version "4.0.0"
@@ -6281,20 +6007,21 @@ lodash.uniq@^4.5.0:
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
-lodash@4.17.21, lodash@^4.17.21:
+lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-log-update@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz"
- integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+log-update@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/log-update/-/log-update-5.0.1.tgz#9e928bf70cb183c1f0c9e91d9e6b7115d597ce09"
+ integrity sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==
dependencies:
- ansi-escapes "^4.3.0"
- cli-cursor "^3.1.0"
- slice-ansi "^4.0.0"
- wrap-ansi "^6.2.0"
+ ansi-escapes "^5.0.0"
+ cli-cursor "^4.0.0"
+ slice-ansi "^5.0.0"
+ strip-ansi "^7.0.1"
+ wrap-ansi "^8.0.1"
loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
@@ -6386,12 +6113,12 @@ mathml-tag-names@^2.1.3:
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
maxmind@^4.3.6:
- version "4.3.11"
- resolved "https://registry.npmjs.org/maxmind/-/maxmind-4.3.11.tgz"
- integrity sha512-tJDrKbUzN6PSA88tWgg0L2R4Ln00XwecYQJPFI+RvlF2k1sx6VQYtuQ1SVxm8+bw5tF7GWV4xyb+3/KyzEpPUw==
+ version "4.3.13"
+ resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.3.13.tgz#41e0339bc299b760e6503909ddab018621278953"
+ integrity sha512-9y2WM2UR8kZqfMJjaXeg+PdKFABLwx3ntUlvDlmIQyKJ1GPVfXAMyGLOoc0aNoUB39yqzvBn0xiXCgVMhJDyMQ==
dependencies:
mmdb-lib "2.0.2"
- tiny-lru "11.0.1"
+ tiny-lru "11.1.2"
mdn-data@2.0.14:
version "2.0.14"
@@ -6468,7 +6195,7 @@ micro-memoize@^4.1.2:
resolved "https://registry.yarnpkg.com/micro-memoize/-/micro-memoize-4.1.2.tgz#ce719c1ba1e41592f1cd91c64c5f41dcbf135f36"
integrity sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g==
-micromatch@^4.0.4, micromatch@^4.0.5:
+micromatch@4.0.5, micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@@ -6476,18 +6203,6 @@ micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
-mime-db@1.52.0:
- version "1.52.0"
- resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
- integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-
-mime-types@^2.1.12, mime-types@~2.1.19:
- version "2.1.35"
- resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
- integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
- dependencies:
- mime-db "1.52.0"
-
mime@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7"
@@ -6498,6 +6213,11 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+mimic-fn@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
+ integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
+
mimic-response@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
@@ -6583,7 +6303,7 @@ mlly@^1.2.0, mlly@^1.4.2:
mmdb-lib@2.0.2:
version "2.0.2"
- resolved "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-2.0.2.tgz"
+ resolved "https://registry.yarnpkg.com/mmdb-lib/-/mmdb-lib-2.0.2.tgz#fe60404142c0456c19607c72caa15821731ae957"
integrity sha512-shi1I+fCPQonhTi7qyb6hr7hi87R7YS69FlfJiMFuJ12+grx0JyL56gLNzGTYXPU7EhAPkMLliGeyHer0K+AVA==
moize@^6.1.0:
@@ -6653,11 +6373,6 @@ napi-wasm@^1.1.0:
resolved "https://registry.yarnpkg.com/napi-wasm/-/napi-wasm-1.1.0.tgz#bbe617823765ae9c1bc12ff5942370eae7b2ba4e"
integrity sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==
-natural-compare-lite@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
- integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
-
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -6672,12 +6387,12 @@ next-basics@^0.36.0:
jsonwebtoken "^9.0.0"
pure-rand "^6.0.2"
-next@13.5.2:
- version "13.5.2"
- resolved "https://registry.yarnpkg.com/next/-/next-13.5.2.tgz#809dd84e481049e298fe79d28b1d66b587483fca"
- integrity sha512-vog4UhUaMYAzeqfiAAmgB/QWLW7p01/sg+2vn6bqc/CxHFYizMzLv6gjxKzl31EVFkfl/F+GbxlKizlkTE9RdA==
+next@13.5.3:
+ version "13.5.3"
+ resolved "https://registry.yarnpkg.com/next/-/next-13.5.3.tgz#631efcbcc9d756c610855d9b94f3d8c4e73ee131"
+ integrity sha512-4Nt4HRLYDW/yRpJ/QR2t1v63UOMS55A38dnWv3UDOWGezuY0ZyFO1ABNbD7mulVzs9qVhgy2+ppjdsANpKP1mg==
dependencies:
- "@next/env" "13.5.2"
+ "@next/env" "13.5.3"
"@swc/helpers" "0.5.2"
busboy "1.6.0"
caniuse-lite "^1.0.30001406"
@@ -6686,15 +6401,15 @@ next@13.5.2:
watchpack "2.4.0"
zod "3.21.4"
optionalDependencies:
- "@next/swc-darwin-arm64" "13.5.2"
- "@next/swc-darwin-x64" "13.5.2"
- "@next/swc-linux-arm64-gnu" "13.5.2"
- "@next/swc-linux-arm64-musl" "13.5.2"
- "@next/swc-linux-x64-gnu" "13.5.2"
- "@next/swc-linux-x64-musl" "13.5.2"
- "@next/swc-win32-arm64-msvc" "13.5.2"
- "@next/swc-win32-ia32-msvc" "13.5.2"
- "@next/swc-win32-x64-msvc" "13.5.2"
+ "@next/swc-darwin-arm64" "13.5.3"
+ "@next/swc-darwin-x64" "13.5.3"
+ "@next/swc-linux-arm64-gnu" "13.5.3"
+ "@next/swc-linux-arm64-musl" "13.5.3"
+ "@next/swc-linux-x64-gnu" "13.5.3"
+ "@next/swc-linux-x64-musl" "13.5.3"
+ "@next/swc-win32-arm64-msvc" "13.5.3"
+ "@next/swc-win32-ia32-msvc" "13.5.3"
+ "@next/swc-win32-x64-msvc" "13.5.3"
nice-try@^1.0.4:
version "1.0.5"
@@ -6824,6 +6539,13 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
+npm-run-path@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00"
+ integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==
+ dependencies:
+ path-key "^4.0.0"
+
nth-check@^2.0.1:
version "2.1.1"
resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz"
@@ -6831,11 +6553,6 @@ nth-check@^2.0.1:
dependencies:
boolbase "^1.0.0"
-oauth-sign@~0.9.0:
- version "0.9.0"
- resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz"
- integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
@@ -6929,6 +6646,13 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
+onetime@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
+ integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
+ dependencies:
+ mimic-fn "^4.0.0"
+
optionator@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
@@ -7058,6 +6782,11 @@ path-key@^3.0.0, path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+path-key@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
+ integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
+
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
@@ -7085,11 +6814,6 @@ pathe@^1.1.0, pathe@^1.1.1:
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
-performance-now@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
- integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
-
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@@ -7100,6 +6824,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+pidtree@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
+ integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
+
pidtree@^0.3.0:
version "0.3.1"
resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz"
@@ -7129,13 +6858,6 @@ pkg-types@^1.0.3:
mlly "^1.2.0"
pathe "^1.1.0"
-please-upgrade-node@^3.2.0:
- version "3.2.0"
- resolved "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz"
- integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==
- dependencies:
- semver-compare "^1.0.0"
-
postcss-attribute-case-insensitive@^5.0.2:
version "5.0.2"
resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz"
@@ -7770,11 +7492,6 @@ property-expr@^2.0.4:
resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz"
integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==
-psl@^1.1.24:
- version "1.9.0"
- resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"
- integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
-
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
@@ -7783,11 +7500,6 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
-punycode@^1.4.1:
- version "1.4.1"
- resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz"
- integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
-
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
@@ -7798,16 +7510,6 @@ pure-rand@^6.0.2:
resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz"
integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==
-qs@~6.5.2:
- version "6.5.3"
- resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz"
- integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
-
-querystring@0.2.0:
- version "0.2.0"
- resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz"
- integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==
-
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -7855,15 +7557,15 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-basics@^0.100.0:
- version "0.100.0"
- resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.100.0.tgz#14a36769af89f3e01641997f897e4073f16f5035"
- integrity sha512-ET6DX/FYAcjGRauBE4jwqwVpd/hKmA2Nu/fi1dakwsv17hkyV5FEAhdWhQAxJX3VnaCH//QysN8+ae12KuNA9g==
+react-basics@^0.102.0:
+ version "0.102.0"
+ resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.102.0.tgz#61dbc837963facb2d409044046eb55f7c689411e"
+ integrity sha512-MhLHgjVnOwdm1YX1Lwkyg37kPCTOk+b3NXWGORlh4dJThq/qq/s0lF09yqEq09Gye5yyTDbeNDPXlkXIPtadog==
dependencies:
+ "@react-spring/web" "^9.7.3"
classnames "^2.3.1"
date-fns "^2.29.3"
react-hook-form "^7.34.2"
- react-spring "^9.5.5"
react-window "^1.8.6"
react-beautiful-dnd@^13.1.0:
@@ -7899,21 +7601,21 @@ react-hook-form@^7.34.2:
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.45.0.tgz#df2bbc8cee598855a63ba446e0bb06f7c8120ccf"
integrity sha512-AbHeZ4ad+0dEIknSW9dBgIwcvRDfZ1O97sgj75WaMdOX0eg8TBiUf9wxzVkIjZbk76BBIE9lmFOzyD4PN80ZQg==
-react-intl@^5.24.7:
- version "5.25.1"
- resolved "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz"
- integrity sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==
+react-intl@^6.4.7:
+ version "6.4.7"
+ resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.4.7.tgz#28ec40350ff791a6a773f5e76b9e12835ae17e19"
+ integrity sha512-0hnOHAZhxTFqD1hGTxrF40qNyZJPPYiGhWIIxIz0Udz+3e3c7sdN80qlxArR+AbJ+jb5ALXZkJYH20+GPFCM0Q==
dependencies:
- "@formatjs/ecma402-abstract" "1.11.4"
- "@formatjs/icu-messageformat-parser" "2.1.0"
- "@formatjs/intl" "2.2.1"
- "@formatjs/intl-displaynames" "5.4.3"
- "@formatjs/intl-listformat" "6.5.3"
+ "@formatjs/ecma402-abstract" "1.17.2"
+ "@formatjs/icu-messageformat-parser" "2.6.2"
+ "@formatjs/intl" "2.9.3"
+ "@formatjs/intl-displaynames" "6.5.2"
+ "@formatjs/intl-listformat" "7.4.2"
"@types/hoist-non-react-statics" "^3.3.1"
"@types/react" "16 || 17 || 18"
hoist-non-react-statics "^3.3.2"
- intl-messageformat "9.13.0"
- tslib "^2.1.0"
+ intl-messageformat "10.5.3"
+ tslib "^2.4.0"
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
@@ -7947,30 +7649,6 @@ react-simple-maps@^2.3.0:
d3-zoom "^2.0.0"
topojson-client "^3.1.0"
-react-spring@^9.4.4:
- version "9.7.2"
- resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-9.7.2.tgz#218360d0ca53d04d8faac984f0d8683819b967b4"
- integrity sha512-cckALtj79yiaJiAOUNAhtZbdqjvv1bdn/FpobgkckIChc8l6vu0E53WQ+zWru60gINI3JT+oRJSIn2hUVlOvlQ==
- dependencies:
- "@react-spring/core" "~9.7.3"
- "@react-spring/konva" "~9.7.3"
- "@react-spring/native" "~9.7.3"
- "@react-spring/three" "~9.7.3"
- "@react-spring/web" "~9.7.3"
- "@react-spring/zdog" "~9.7.3"
-
-react-spring@^9.5.5:
- version "9.7.1"
- resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-9.7.1.tgz#8acfed700823490a4d9d4cf131c5fea12d1aaa93"
- integrity sha512-o2+r2DNQDVEuefiz33ZF76DPd/gLq3kbdObJmllGF2IUfv2W6x+ZP0gR97QYCSR4QLbmOl1mPKUBbI+FJdys2Q==
- dependencies:
- "@react-spring/core" "~9.7.1"
- "@react-spring/konva" "~9.7.1"
- "@react-spring/native" "~9.7.1"
- "@react-spring/three" "~9.7.1"
- "@react-spring/web" "~9.7.1"
- "@react-spring/zdog" "~9.7.1"
-
react-use-measure@^2.0.4:
version "2.1.1"
resolved "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz"
@@ -8242,32 +7920,6 @@ request-ip@^3.3.0:
resolved "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz"
integrity sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==
-request@2.88.0:
- version "2.88.0"
- resolved "https://registry.npmjs.org/request/-/request-2.88.0.tgz"
- integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
- dependencies:
- aws-sign2 "~0.7.0"
- aws4 "^1.8.0"
- caseless "~0.12.0"
- combined-stream "~1.0.6"
- extend "~3.0.2"
- forever-agent "~0.6.1"
- form-data "~2.3.2"
- har-validator "~5.1.0"
- http-signature "~1.2.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.19"
- oauth-sign "~0.9.0"
- performance-now "^2.1.0"
- qs "~6.5.2"
- safe-buffer "^5.1.2"
- tough-cookie "~2.4.3"
- tunnel-agent "^0.6.0"
- uuid "^3.3.2"
-
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -8310,10 +7962,10 @@ resolve@^2.0.0-next.3:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
-restore-cursor@^3.1.0:
- version "3.1.0"
- resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz"
- integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+restore-cursor@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9"
+ integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==
dependencies:
onetime "^5.1.0"
signal-exit "^3.0.2"
@@ -8415,9 +8067,9 @@ rollup-pluginutils@^2.8.2:
estree-walker "^0.6.1"
rollup@^3.28.0:
- version "3.29.2"
- resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.2.tgz#cbc76cd5b03b9f9e93be991d23a1dff9c6d5b740"
- integrity sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A==
+ version "3.29.3"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.3.tgz#97769774ccaa6a3059083d4680fcabd8ead01289"
+ integrity sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==
optionalDependencies:
fsevents "~2.3.2"
@@ -8438,13 +8090,6 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
-rxjs@^7.5.1:
- version "7.5.5"
- resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz"
- integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
- dependencies:
- tslib "^2.1.0"
-
safe-array-concat@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c"
@@ -8455,7 +8100,7 @@ safe-array-concat@^1.0.1:
has-symbols "^1.0.3"
isarray "^2.0.5"
-safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -8474,11 +8119,6 @@ safe-regex-test@^1.0.0:
get-intrinsic "^1.1.3"
is-regex "^1.1.4"
-safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
- version "2.1.2"
- resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz"
@@ -8505,11 +8145,6 @@ schema-utils@^2.6.6:
ajv "^6.12.4"
ajv-keywords "^3.5.2"
-semver-compare@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz"
- integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
-
"semver@2 || 3 || 4 || 5", semver@^5.5.0:
version "5.7.1"
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
@@ -8607,7 +8242,7 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-signal-exit@^3.0.2, signal-exit@^3.0.3:
+signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
@@ -8653,15 +8288,6 @@ slash@^4.0.0:
resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
-slice-ansi@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz"
- integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
- dependencies:
- ansi-styles "^4.0.0"
- astral-regex "^2.0.0"
- is-fullwidth-code-point "^3.0.0"
-
slice-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
@@ -8671,6 +8297,14 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
+slice-ansi@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a"
+ integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==
+ dependencies:
+ ansi-styles "^6.0.0"
+ is-fullwidth-code-point "^4.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"
@@ -8740,21 +8374,6 @@ sprintf-js@~1.0.2:
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-sshpk@^1.7.0:
- version "1.17.0"
- resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz"
- integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==
- dependencies:
- asn1 "~0.2.3"
- assert-plus "^1.0.0"
- bcrypt-pbkdf "^1.0.0"
- dashdash "^1.12.0"
- ecc-jsbn "~0.1.1"
- getpass "^0.1.1"
- jsbn "~0.1.0"
- safer-buffer "^2.0.2"
- tweetnacl "~0.14.0"
-
stable@^0.1.8:
version "0.1.8"
resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz"
@@ -8770,11 +8389,6 @@ std-env@^3.4.3:
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.4.3.tgz#326f11db518db751c83fd58574f449b7c3060910"
integrity sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==
-stream2asynciter@1.0.3:
- version "1.0.3"
- resolved "https://registry.npmjs.org/stream2asynciter/-/stream2asynciter-1.0.3.tgz"
- integrity sha512-9/dEZW+LQjuW6ub5hmWi4n9Pn8W8qA8k7NAE1isecesA164e73xTdy1CJ3S9o9YS+O21HuiK7T+4uS7FgKDy4w==
-
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
@@ -8788,25 +8402,34 @@ streamx@^2.15.0:
fast-fifo "^1.1.0"
queue-tick "^1.0.1"
-string-argv@0.3.1:
- version "0.3.1"
- resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz"
- integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
+string-argv@0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
+ integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
string-hash@^1.1.1:
version "1.1.3"
resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz"
integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==
-string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+string-width@^4.2.3:
version "4.2.3"
- resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
+string-width@^5.0.0, string-width@^5.0.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+ integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+ dependencies:
+ eastasianwidth "^0.2.0"
+ emoji-regex "^9.2.2"
+ strip-ansi "^7.0.1"
+
string.prototype.matchall@^4.0.8:
version "4.0.8"
resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz"
@@ -8864,22 +8487,20 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
-stringify-object@3.3.0:
- version "3.3.0"
- resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz"
- integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==
- dependencies:
- get-own-enumerable-property-symbols "^3.0.0"
- is-obj "^1.0.1"
- is-regexp "^1.0.0"
-
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
+strip-ansi@^7.0.1:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+ integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+ dependencies:
+ ansi-regex "^6.0.1"
+
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -8895,6 +8516,11 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+strip-final-newline@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
+ integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz"
@@ -9017,13 +8643,6 @@ stylelint@^15.10.1:
table "^6.8.1"
write-file-atomic "^5.0.1"
-supports-color@8.1.1:
- version "8.1.1"
- resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz"
- integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
- dependencies:
- has-flag "^4.0.0"
-
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -9061,7 +8680,7 @@ svg-tags@^1.0.0:
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==
-svgo@^2.7.0, svgo@^2.8.0:
+svgo@^2.7.0:
version "2.8.0"
resolved "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz"
integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
@@ -9168,11 +8787,6 @@ thenby@^1.3.4:
resolved "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz"
integrity sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==
-through@2.3.8, "through@>=2.2.7 <3", through@^2.3.8:
- version "2.3.8"
- resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
- integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
-
timezone-support@^2.0.2:
version "2.2.0"
resolved "https://registry.npmjs.org/timezone-support/-/timezone-support-2.2.0.tgz"
@@ -9194,10 +8808,10 @@ tiny-invariant@^1.0.6:
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz"
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
-tiny-lru@11.0.1:
- version "11.0.1"
- resolved "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.0.1.tgz"
- integrity sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==
+tiny-lru@11.1.2:
+ version "11.1.2"
+ resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-11.1.2.tgz#ef8d5ac74a78fa26a9b841335348b4d71df46ddc"
+ integrity sha512-EGJRgd/nY4qMlVWCYLecHdzsN6GJRM073iqlQNk/Xq5KVUGl5zkgWYNnv5pgVClDFvVEAI+7R5wpNBOu/foI2w==
to-fast-properties@^2.0.0:
version "2.0.0"
@@ -9223,14 +8837,6 @@ toposort@^2.0.2:
resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz"
integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==
-tough-cookie@~2.4.3:
- version "2.4.3"
- resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz"
- integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
- dependencies:
- psl "^1.1.24"
- punycode "^1.4.1"
-
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@@ -9246,6 +8852,11 @@ trim-newlines@^4.0.2:
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.1.1.tgz#28c88deb50ed10c7ba6dc2474421904a00139125"
integrity sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==
+ts-api-utils@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331"
+ integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==
+
ts-node@^10.9.1:
version "10.9.1"
resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz"
@@ -9302,11 +8913,6 @@ tsutils@^3.21.0:
dependencies:
tslib "^1.8.1"
-tsv@0.2.0:
- version "0.2.0"
- resolved "https://registry.npmjs.org/tsv/-/tsv-0.2.0.tgz"
- integrity sha512-GG6xbOP85giXXom0dS6z9uyDsxktznjpa1AuDlPrIXDqDnbhjr9Vk6Us8iz6U1nENL4CPS2jZDvIjEdaZsmc4Q==
-
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -9314,11 +8920,6 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
-tweetnacl@^0.14.3, tweetnacl@~0.14.0:
- version "0.14.5"
- resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz"
- integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==
-
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -9336,11 +8937,6 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-type-fest@^0.21.3:
- version "0.21.3"
- resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
- integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
-
type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz"
@@ -9351,7 +8947,7 @@ type-fest@^0.8.1:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
-type-fest@^1.0.1, type-fest@^1.2.1, type-fest@^1.2.2:
+type-fest@^1.0.1, type-fest@^1.0.2, type-fest@^1.2.1, type-fest@^1.2.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
@@ -9570,11 +9166,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
-uuid@3.4.0, uuid@^3.3.2:
- version "3.4.0"
- resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
uuid@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
@@ -9598,15 +9189,6 @@ vary@^1:
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
-verror@1.10.0:
- version "1.10.0"
- resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz"
- integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==
- dependencies:
- assert-plus "^1.0.0"
- core-util-is "1.0.2"
- extsprintf "^1.2.0"
-
vue@^3.2.23:
version "3.2.36"
resolved "https://registry.npmjs.org/vue/-/vue-3.2.36.tgz"
@@ -9680,23 +9262,14 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
-wrap-ansi@^6.2.0:
- version "6.2.0"
- resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz"
- integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+ integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
+ ansi-styles "^6.1.0"
+ string-width "^5.0.1"
+ strip-ansi "^7.0.1"
wrappy@1:
version "1.0.2"
@@ -9751,7 +9324,12 @@ yallist@^3.0.2:
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
-yaml@^1.10.0, yaml@^1.10.2:
+yaml@2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
+ integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
+
+yaml@^1.10.2:
version "1.10.2"
resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==