diff --git a/next-env.d.ts b/next-env.d.ts
index 4f11a03dc6..fd36f9494e 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 cc3cde7c68..cf7dce7fb1 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 6fc413d44b..5b2ef173f9 100644
--- a/package.json
+++ b/package.json
@@ -64,8 +64,9 @@
"@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",
@@ -91,7 +92,7 @@
"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",
@@ -100,9 +101,8 @@
"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.21",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^15.1.0",
diff --git a/src/app/(app)/NavBar.js b/src/app/(app)/NavBar.js
new file mode 100644
index 0000000000..211adf5fb0
--- /dev/null
+++ b/src/app/(app)/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/(app)/NavBar.module.css
similarity index 75%
rename from src/components/layout/NavBar.module.css
rename to src/app/(app)/NavBar.module.css
index dd5085a03a..fd022ecab1 100644
--- a/src/components/layout/NavBar.module.css
+++ b/src/app/(app)/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/(app)/Shell.tsx b/src/app/(app)/Shell.tsx
new file mode 100644
index 0000000000..980abb6217
--- /dev/null
+++ b/src/app/(app)/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/(app)/console/TestConsole.js
similarity index 97%
rename from src/components/pages/console/TestConsole.js
rename to src/app/(app)/console/TestConsole.js
index 71eb27b489..ca7ffc4aca 100644
--- a/src/components/pages/console/TestConsole.js
+++ b/src/app/(app)/console/TestConsole.js
@@ -1,12 +1,13 @@
+'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 '../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 styles from './TestConsole.module.css';
diff --git a/src/components/pages/console/TestConsole.module.css b/src/app/(app)/console/TestConsole.module.css
similarity index 100%
rename from src/components/pages/console/TestConsole.module.css
rename to src/app/(app)/console/TestConsole.module.css
diff --git a/src/app/(app)/console/[[...id]]/page.tsx b/src/app/(app)/console/[[...id]]/page.tsx
new file mode 100644
index 0000000000..d020ddf9f7
--- /dev/null
+++ b/src/app/(app)/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/(app)/dashboard/Dashboard.js
similarity index 79%
rename from src/components/pages/dashboard/Dashboard.js
rename to src/app/(app)/dashboard/Dashboard.js
index 2294b8be8e..6520323e4e 100644
--- a/src/components/pages/dashboard/Dashboard.js
+++ b/src/app/(app)/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 '../websites/[id]/WebsiteChartList';
+import DashboardSettingsButton from 'app/(app)/dashboard/DashboardSettingsButton';
+import DashboardEdit from 'app/(app)/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/(app)/dashboard/DashboardEdit.js
similarity index 93%
rename from src/components/pages/dashboard/DashboardEdit.js
rename to src/app/(app)/dashboard/DashboardEdit.js
index f628599fed..3af3386705 100644
--- a/src/components/pages/dashboard/DashboardEdit.js
+++ b/src/app/(app)/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/(app)/dashboard/DashboardEdit.module.css
similarity index 100%
rename from src/components/pages/dashboard/DashboardEdit.module.css
rename to src/app/(app)/dashboard/DashboardEdit.module.css
diff --git a/src/components/pages/dashboard/DashboardSettingsButton.js b/src/app/(app)/dashboard/DashboardSettingsButton.js
similarity index 100%
rename from src/components/pages/dashboard/DashboardSettingsButton.js
rename to src/app/(app)/dashboard/DashboardSettingsButton.js
diff --git a/src/components/pages/dashboard/DashboardSettingsButton.module.css b/src/app/(app)/dashboard/DashboardSettingsButton.module.css
similarity index 100%
rename from src/components/pages/dashboard/DashboardSettingsButton.module.css
rename to src/app/(app)/dashboard/DashboardSettingsButton.module.css
diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx
new file mode 100644
index 0000000000..7e7c095c5b
--- /dev/null
+++ b/src/app/(app)/dashboard/page.tsx
@@ -0,0 +1,10 @@
+import Dashboard from 'app/(app)/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/(app)/layout.module.css
similarity index 91%
rename from src/components/layout/AppLayout.module.css
rename to src/app/(app)/layout.module.css
index bcce963f0d..0afd11f9fe 100644
--- a/src/components/layout/AppLayout.module.css
+++ b/src/app/(app)/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/(app)/layout.tsx b/src/app/(app)/layout.tsx
new file mode 100644
index 0000000000..1c9cc27792
--- /dev/null
+++ b/src/app/(app)/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/components/pages/reports/BaseParameters.js b/src/app/(app)/reports/BaseParameters.js
similarity index 100%
rename from src/components/pages/reports/BaseParameters.js
rename to src/app/(app)/reports/BaseParameters.js
diff --git a/src/components/pages/reports/FieldAddForm.js b/src/app/(app)/reports/FieldAddForm.js
similarity index 100%
rename from src/components/pages/reports/FieldAddForm.js
rename to src/app/(app)/reports/FieldAddForm.js
diff --git a/src/components/pages/reports/FieldAddForm.module.css b/src/app/(app)/reports/FieldAddForm.module.css
similarity index 100%
rename from src/components/pages/reports/FieldAddForm.module.css
rename to src/app/(app)/reports/FieldAddForm.module.css
diff --git a/src/components/pages/reports/FieldAggregateForm.js b/src/app/(app)/reports/FieldAggregateForm.js
similarity index 100%
rename from src/components/pages/reports/FieldAggregateForm.js
rename to src/app/(app)/reports/FieldAggregateForm.js
diff --git a/src/components/pages/reports/FieldFilterForm.js b/src/app/(app)/reports/FieldFilterForm.js
similarity index 100%
rename from src/components/pages/reports/FieldFilterForm.js
rename to src/app/(app)/reports/FieldFilterForm.js
diff --git a/src/components/pages/reports/FieldFilterForm.module.css b/src/app/(app)/reports/FieldFilterForm.module.css
similarity index 100%
rename from src/components/pages/reports/FieldFilterForm.module.css
rename to src/app/(app)/reports/FieldFilterForm.module.css
diff --git a/src/components/pages/reports/FieldSelectForm.js b/src/app/(app)/reports/FieldSelectForm.js
similarity index 100%
rename from src/components/pages/reports/FieldSelectForm.js
rename to src/app/(app)/reports/FieldSelectForm.js
diff --git a/src/components/pages/reports/FieldSelectForm.module.css b/src/app/(app)/reports/FieldSelectForm.module.css
similarity index 100%
rename from src/components/pages/reports/FieldSelectForm.module.css
rename to src/app/(app)/reports/FieldSelectForm.module.css
diff --git a/src/components/pages/reports/FilterSelectForm.js b/src/app/(app)/reports/FilterSelectForm.js
similarity index 100%
rename from src/components/pages/reports/FilterSelectForm.js
rename to src/app/(app)/reports/FilterSelectForm.js
diff --git a/src/components/pages/reports/ParameterList.js b/src/app/(app)/reports/ParameterList.js
similarity index 100%
rename from src/components/pages/reports/ParameterList.js
rename to src/app/(app)/reports/ParameterList.js
diff --git a/src/components/pages/reports/ParameterList.module.css b/src/app/(app)/reports/ParameterList.module.css
similarity index 100%
rename from src/components/pages/reports/ParameterList.module.css
rename to src/app/(app)/reports/ParameterList.module.css
diff --git a/src/components/pages/reports/PopupForm.js b/src/app/(app)/reports/PopupForm.js
similarity index 100%
rename from src/components/pages/reports/PopupForm.js
rename to src/app/(app)/reports/PopupForm.js
diff --git a/src/components/pages/reports/PopupForm.module.css b/src/app/(app)/reports/PopupForm.module.css
similarity index 100%
rename from src/components/pages/reports/PopupForm.module.css
rename to src/app/(app)/reports/PopupForm.module.css
diff --git a/src/components/pages/reports/Report.js b/src/app/(app)/reports/Report.js
similarity index 73%
rename from src/components/pages/reports/Report.js
rename to src/app/(app)/reports/Report.js
index 538a444da3..0bea18eddb 100644
--- a/src/components/pages/reports/Report.js
+++ b/src/app/(app)/reports/Report.js
@@ -1,7 +1,7 @@
+'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);
@@ -10,9 +10,9 @@ export function Report({ reportId, defaultParameters, children, ...props }) {
return (
-
+
{children}
-
+
);
}
diff --git a/src/components/pages/reports/reports.module.css b/src/app/(app)/reports/Report.module.css
similarity index 100%
rename from src/components/pages/reports/reports.module.css
rename to src/app/(app)/reports/Report.module.css
diff --git a/src/components/pages/reports/ReportBody.js b/src/app/(app)/reports/ReportBody.js
similarity index 75%
rename from src/components/pages/reports/ReportBody.js
rename to src/app/(app)/reports/ReportBody.js
index 2310c8af82..b34b138dc4 100644
--- a/src/components/pages/reports/ReportBody.js
+++ b/src/app/(app)/reports/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/components/pages/reports/ReportHeader.js b/src/app/(app)/reports/ReportHeader.js
similarity index 96%
rename from src/components/pages/reports/ReportHeader.js
rename to src/app/(app)/reports/ReportHeader.js
index 930f745b9b..a182142f98 100644
--- a/src/components/pages/reports/ReportHeader.js
+++ b/src/app/(app)/reports/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/(app)/reports/ReportHeader.module.css
similarity index 100%
rename from src/components/pages/reports/ReportHeader.module.css
rename to src/app/(app)/reports/ReportHeader.module.css
diff --git a/src/components/pages/reports/ReportMenu.js b/src/app/(app)/reports/ReportMenu.js
similarity index 75%
rename from src/components/pages/reports/ReportMenu.js
rename to src/app/(app)/reports/ReportMenu.js
index abfea6fe20..91c6fc16c2 100644
--- a/src/components/pages/reports/ReportMenu.js
+++ b/src/app/(app)/reports/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/(app)/reports/ReportsHeader.js b/src/app/(app)/reports/ReportsHeader.js
new file mode 100644
index 0000000000..14d8bae278
--- /dev/null
+++ b/src/app/(app)/reports/ReportsHeader.js
@@ -0,0 +1,24 @@
+'use client';
+import PageHeader from 'components/layout/PageHeader';
+import Link from 'next/link';
+import { Button, Icon, Icons, Text } from 'react-basics';
+import { useMessages } from 'components/hooks';
+
+export function ReportsHeader() {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+
+
+
+
+
+ {formatMessage(labels.createReport)}
+
+
+
+ );
+}
+
+export default ReportsHeader;
diff --git a/src/app/(app)/reports/ReportsList.js b/src/app/(app)/reports/ReportsList.js
new file mode 100644
index 0000000000..dc2b9f2be9
--- /dev/null
+++ b/src/app/(app)/reports/ReportsList.js
@@ -0,0 +1,37 @@
+'use client';
+import { useApi } from 'components/hooks';
+import ReportsTable from './ReportsTable';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import DataTable from 'components/common/DataTable';
+
+function useReports() {
+ const { get, del, useMutation } = useApi();
+ const { mutate } = useMutation(reportId => del(`/reports/${reportId}`));
+ const reports = useFilterQuery(['reports'], params => get(`/reports`, params));
+
+ const deleteReport = id => {
+ mutate(id, {
+ onSuccess: () => {
+ reports.refetch();
+ },
+ });
+ };
+
+ return { reports, deleteReport };
+}
+
+export default function ReportsList() {
+ const { reports, deleteReport } = useReports();
+
+ const handleDelete = async (id, callback) => {
+ await deleteReport(id);
+ await reports.refetch();
+ callback?.();
+ };
+
+ return (
+
+ {({ data }) => }
+
+ );
+}
diff --git a/src/app/(app)/reports/ReportsTable.js b/src/app/(app)/reports/ReportsTable.js
new file mode 100644
index 0000000000..4c81faa701
--- /dev/null
+++ b/src/app/(app)/reports/ReportsTable.js
@@ -0,0 +1,74 @@
+import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm';
+import LinkButton from 'components/common/LinkButton';
+import { useMessages } from 'components/hooks';
+import useUser from 'components/hooks/useUser';
+import {
+ Button,
+ GridColumn,
+ GridTable,
+ Icon,
+ Icons,
+ Modal,
+ ModalTrigger,
+ Text,
+} from 'react-basics';
+import { REPORT_TYPES } from 'lib/constants';
+
+export function ReportsTable({ data = [], onDelete, showDomain }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ const handleConfirm = (id, callback) => {
+ onDelete?.(id, callback);
+ };
+
+ 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 (
+ <>
+ {formatMessage(labels.view)}
+ {(user.id === userId || user.id === website?.userId) && (
+
+
+
+
+
+ {formatMessage(labels.delete)}
+
+
+ {close => (
+
+ )}
+
+
+ )}
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default ReportsTable;
diff --git a/src/app/(app)/reports/[id]/ReportDetails.js b/src/app/(app)/reports/[id]/ReportDetails.js
new file mode 100644
index 0000000000..8605ffb33c
--- /dev/null
+++ b/src/app/(app)/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/app/(app)/reports/[id]/page.tsx b/src/app/(app)/reports/[id]/page.tsx
new file mode 100644
index 0000000000..9ba87f41f9
--- /dev/null
+++ b/src/app/(app)/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/(app)/reports/create/ReportTemplates.js
similarity index 96%
rename from src/components/pages/reports/ReportTemplates.js
rename to src/app/(app)/reports/create/ReportTemplates.js
index 59cc8b310c..003cb3fc4d 100644
--- a/src/components/pages/reports/ReportTemplates.js
+++ b/src/app/(app)/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/(app)/reports/create/ReportTemplates.module.css
similarity index 100%
rename from src/components/pages/reports/ReportTemplates.module.css
rename to src/app/(app)/reports/create/ReportTemplates.module.css
diff --git a/src/app/(app)/reports/create/page.tsx b/src/app/(app)/reports/create/page.tsx
new file mode 100644
index 0000000000..a1a761bcd3
--- /dev/null
+++ b/src/app/(app)/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/(app)/reports/event-data/EventDataParameters.js
similarity index 98%
rename from src/components/pages/reports/event-data/EventDataParameters.js
rename to src/app/(app)/reports/event-data/EventDataParameters.js
index e0fadb8b8b..00397ffb85 100644
--- a/src/components/pages/reports/event-data/EventDataParameters.js
+++ b/src/app/(app)/reports/event-data/EventDataParameters.js
@@ -1,7 +1,7 @@
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 { ReportContext } from '../Report';
import Empty from 'components/common/Empty';
import { DATA_TYPES, REPORT_PARAMETERS } from 'lib/constants';
import Icons from 'components/icons';
diff --git a/src/components/pages/reports/event-data/EventDataParameters.module.css b/src/app/(app)/reports/event-data/EventDataParameters.module.css
similarity index 100%
rename from src/components/pages/reports/event-data/EventDataParameters.module.css
rename to src/app/(app)/reports/event-data/EventDataParameters.module.css
diff --git a/src/components/pages/reports/event-data/EventDataReport.js b/src/app/(app)/reports/event-data/EventDataReport.js
similarity index 100%
rename from src/components/pages/reports/event-data/EventDataReport.js
rename to src/app/(app)/reports/event-data/EventDataReport.js
diff --git a/src/components/pages/reports/event-data/EventDataTable.js b/src/app/(app)/reports/event-data/EventDataTable.js
similarity index 100%
rename from src/components/pages/reports/event-data/EventDataTable.js
rename to src/app/(app)/reports/event-data/EventDataTable.js
diff --git a/src/components/pages/reports/funnel/FunnelChart.js b/src/app/(app)/reports/funnel/FunnelChart.js
similarity index 100%
rename from src/components/pages/reports/funnel/FunnelChart.js
rename to src/app/(app)/reports/funnel/FunnelChart.js
diff --git a/src/components/pages/reports/funnel/FunnelChart.module.css b/src/app/(app)/reports/funnel/FunnelChart.module.css
similarity index 100%
rename from src/components/pages/reports/funnel/FunnelChart.module.css
rename to src/app/(app)/reports/funnel/FunnelChart.module.css
diff --git a/src/components/pages/reports/funnel/FunnelParameters.js b/src/app/(app)/reports/funnel/FunnelParameters.js
similarity index 97%
rename from src/components/pages/reports/funnel/FunnelParameters.js
rename to src/app/(app)/reports/funnel/FunnelParameters.js
index a3fbe66343..c8e510d818 100644
--- a/src/components/pages/reports/funnel/FunnelParameters.js
+++ b/src/app/(app)/reports/funnel/FunnelParameters.js
@@ -13,7 +13,7 @@ import {
} from 'react-basics';
import Icons from 'components/icons';
import UrlAddForm from './UrlAddForm';
-import { ReportContext } from 'components/pages/reports/Report';
+import { ReportContext } from '../Report';
import BaseParameters from '../BaseParameters';
import ParameterList from '../ParameterList';
import PopupForm from '../PopupForm';
diff --git a/src/components/pages/reports/funnel/FunnelReport.js b/src/app/(app)/reports/funnel/FunnelReport.js
similarity index 98%
rename from src/components/pages/reports/funnel/FunnelReport.js
rename to src/app/(app)/reports/funnel/FunnelReport.js
index d2971fa301..9b50d08bf8 100644
--- a/src/components/pages/reports/funnel/FunnelReport.js
+++ b/src/app/(app)/reports/funnel/FunnelReport.js
@@ -1,3 +1,4 @@
+'use client';
import FunnelChart from './FunnelChart';
import FunnelTable from './FunnelTable';
import FunnelParameters from './FunnelParameters';
diff --git a/src/components/pages/reports/funnel/FunnelReport.module.css b/src/app/(app)/reports/funnel/FunnelReport.module.css
similarity index 100%
rename from src/components/pages/reports/funnel/FunnelReport.module.css
rename to src/app/(app)/reports/funnel/FunnelReport.module.css
diff --git a/src/components/pages/reports/funnel/FunnelTable.js b/src/app/(app)/reports/funnel/FunnelTable.js
similarity index 100%
rename from src/components/pages/reports/funnel/FunnelTable.js
rename to src/app/(app)/reports/funnel/FunnelTable.js
diff --git a/src/components/pages/reports/funnel/UrlAddForm.js b/src/app/(app)/reports/funnel/UrlAddForm.js
similarity index 100%
rename from src/components/pages/reports/funnel/UrlAddForm.js
rename to src/app/(app)/reports/funnel/UrlAddForm.js
diff --git a/src/components/pages/reports/funnel/UrlAddForm.module.css b/src/app/(app)/reports/funnel/UrlAddForm.module.css
similarity index 100%
rename from src/components/pages/reports/funnel/UrlAddForm.module.css
rename to src/app/(app)/reports/funnel/UrlAddForm.module.css
diff --git a/src/app/(app)/reports/funnel/page.tsx b/src/app/(app)/reports/funnel/page.tsx
new file mode 100644
index 0000000000..1ce70c7547
--- /dev/null
+++ b/src/app/(app)/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/(app)/reports/insights/InsightsParameters.js
similarity index 98%
rename from src/components/pages/reports/insights/InsightsParameters.js
rename to src/app/(app)/reports/insights/InsightsParameters.js
index 3ddc0367d7..a012bd07f8 100644
--- a/src/components/pages/reports/insights/InsightsParameters.js
+++ b/src/app/(app)/reports/insights/InsightsParameters.js
@@ -10,7 +10,7 @@ import {
Popup,
TooltipPopup,
} from 'react-basics';
-import { ReportContext } from 'components/pages/reports/Report';
+import { ReportContext } from '../Report';
import Icons from 'components/icons';
import BaseParameters from '../BaseParameters';
import ParameterList from '../ParameterList';
diff --git a/src/components/pages/reports/insights/InsightsParameters.module.css b/src/app/(app)/reports/insights/InsightsParameters.module.css
similarity index 100%
rename from src/components/pages/reports/insights/InsightsParameters.module.css
rename to src/app/(app)/reports/insights/InsightsParameters.module.css
diff --git a/src/components/pages/reports/insights/InsightsReport.js b/src/app/(app)/reports/insights/InsightsReport.js
similarity index 98%
rename from src/components/pages/reports/insights/InsightsReport.js
rename to src/app/(app)/reports/insights/InsightsReport.js
index 3d855d9e86..48d610bde0 100644
--- a/src/components/pages/reports/insights/InsightsReport.js
+++ b/src/app/(app)/reports/insights/InsightsReport.js
@@ -1,3 +1,4 @@
+'use client';
import Report from '../Report';
import ReportHeader from '../ReportHeader';
import ReportMenu from '../ReportMenu';
diff --git a/src/components/pages/reports/insights/InsightsTable.js b/src/app/(app)/reports/insights/InsightsTable.js
similarity index 100%
rename from src/components/pages/reports/insights/InsightsTable.js
rename to src/app/(app)/reports/insights/InsightsTable.js
diff --git a/src/app/(app)/reports/insights/page.tsx b/src/app/(app)/reports/insights/page.tsx
new file mode 100644
index 0000000000..1f4db5c667
--- /dev/null
+++ b/src/app/(app)/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/(app)/reports/page.tsx b/src/app/(app)/reports/page.tsx
new file mode 100644
index 0000000000..101844608f
--- /dev/null
+++ b/src/app/(app)/reports/page.tsx
@@ -0,0 +1,14 @@
+import ReportsHeader from './ReportsHeader';
+import ReportsList from './ReportsList';
+
+export default function ReportsPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
+export const metadata = {
+ title: 'Reports | umami',
+};
diff --git a/src/components/pages/reports/retention/RetentionParameters.js b/src/app/(app)/reports/retention/RetentionParameters.js
similarity index 95%
rename from src/components/pages/reports/retention/RetentionParameters.js
rename to src/app/(app)/reports/retention/RetentionParameters.js
index e87108d137..6abae5ac15 100644
--- a/src/components/pages/reports/retention/RetentionParameters.js
+++ b/src/app/(app)/reports/retention/RetentionParameters.js
@@ -1,7 +1,7 @@
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 '../Report';
import { MonthSelect } from 'components/input/MonthSelect';
import BaseParameters from '../BaseParameters';
import { parseDateRange } from 'lib/date';
diff --git a/src/components/pages/reports/retention/RetentionReport.js b/src/app/(app)/reports/retention/RetentionReport.js
similarity index 98%
rename from src/components/pages/reports/retention/RetentionReport.js
rename to src/app/(app)/reports/retention/RetentionReport.js
index a9aaeb3e7a..e6a32ece5d 100644
--- a/src/components/pages/reports/retention/RetentionReport.js
+++ b/src/app/(app)/reports/retention/RetentionReport.js
@@ -1,3 +1,4 @@
+'use client';
import RetentionTable from './RetentionTable';
import RetentionParameters from './RetentionParameters';
import Report from '../Report';
diff --git a/src/components/pages/reports/retention/RetentionReport.module.css b/src/app/(app)/reports/retention/RetentionReport.module.css
similarity index 100%
rename from src/components/pages/reports/retention/RetentionReport.module.css
rename to src/app/(app)/reports/retention/RetentionReport.module.css
diff --git a/src/components/pages/reports/retention/RetentionTable.js b/src/app/(app)/reports/retention/RetentionTable.js
similarity index 100%
rename from src/components/pages/reports/retention/RetentionTable.js
rename to src/app/(app)/reports/retention/RetentionTable.js
diff --git a/src/components/pages/reports/retention/RetentionTable.module.css b/src/app/(app)/reports/retention/RetentionTable.module.css
similarity index 100%
rename from src/components/pages/reports/retention/RetentionTable.module.css
rename to src/app/(app)/reports/retention/RetentionTable.module.css
diff --git a/src/app/(app)/reports/retention/page.js b/src/app/(app)/reports/retention/page.js
new file mode 100644
index 0000000000..7c60cee8e7
--- /dev/null
+++ b/src/app/(app)/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/components/layout/SideNav.js b/src/app/(app)/settings/SideNav.js
similarity index 85%
rename from src/components/layout/SideNav.js
rename to src/app/(app)/settings/SideNav.js
index ccb6f360cd..1f1ab14f8f 100644
--- a/src/components/layout/SideNav.js
+++ b/src/app/(app)/settings/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/layout/SideNav.module.css b/src/app/(app)/settings/SideNav.module.css
similarity index 100%
rename from src/components/layout/SideNav.module.css
rename to src/app/(app)/settings/SideNav.module.css
diff --git a/src/components/layout/SettingsLayout.module.css b/src/app/(app)/settings/layout.module.css
similarity index 71%
rename from src/components/layout/SettingsLayout.module.css
rename to src/app/(app)/settings/layout.module.css
index 08ff02aa65..b87f75d004 100644
--- a/src/components/layout/SettingsLayout.module.css
+++ b/src/app/(app)/settings/layout.module.css
@@ -1,11 +1,18 @@
+.layout {
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ gap: 20px;
+}
+
.menu {
- display: flex;
- flex-direction: column;
+ width: 240px;
padding-top: 40px;
padding-right: 20px;
}
.content {
+ display: flex;
+ flex-direction: column;
min-height: 50vh;
}
diff --git a/src/components/layout/SettingsLayout.js b/src/app/(app)/settings/layout.tsx
similarity index 64%
rename from src/components/layout/SettingsLayout.js
rename to src/app/(app)/settings/layout.tsx
index 0f4aa5d96f..23323b86dd 100644
--- a/src/components/layout/SettingsLayout.js
+++ b/src/app/(app)/settings/layout.tsx
@@ -1,13 +1,13 @@
-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 './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);
@@ -21,17 +21,13 @@ export function SettingsLayout({ children }) {
const getKey = () => items.find(({ url }) => pathname === url)?.key;
return (
-
+
{!cloudMode && (
-
+
-
+
)}
-
- {children}
-
-
+ {children}
+
);
}
-
-export default SettingsLayout;
diff --git a/src/components/pages/settings/profile/DateRangeSetting.js b/src/app/(app)/settings/profile/DateRangeSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/DateRangeSetting.js
rename to src/app/(app)/settings/profile/DateRangeSetting.js
diff --git a/src/components/pages/settings/profile/LanguageSetting.js b/src/app/(app)/settings/profile/LanguageSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/LanguageSetting.js
rename to src/app/(app)/settings/profile/LanguageSetting.js
diff --git a/src/components/pages/settings/profile/PasswordChangeButton.js b/src/app/(app)/settings/profile/PasswordChangeButton.js
similarity index 91%
rename from src/components/pages/settings/profile/PasswordChangeButton.js
rename to src/app/(app)/settings/profile/PasswordChangeButton.js
index 81324eaab6..692f6d6e5f 100644
--- a/src/components/pages/settings/profile/PasswordChangeButton.js
+++ b/src/app/(app)/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/(app)/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/(app)/settings/profile/PasswordEditForm.js
similarity index 100%
rename from src/components/pages/settings/profile/PasswordEditForm.js
rename to src/app/(app)/settings/profile/PasswordEditForm.js
diff --git a/src/app/(app)/settings/profile/ProfileHeader.js b/src/app/(app)/settings/profile/ProfileHeader.js
new file mode 100644
index 0000000000..35aeb0e659
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/profile/ProfileSettings.js
similarity index 79%
rename from src/components/pages/settings/profile/ProfileDetails.js
rename to src/app/(app)/settings/profile/ProfileSettings.js
index d4a3a7d581..8510efbbdd 100644
--- a/src/components/pages/settings/profile/ProfileDetails.js
+++ b/src/app/(app)/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/(app)/settings/profile/TimezoneSetting';
+import DateRangeSetting from 'app/(app)/settings/profile/DateRangeSetting';
+import LanguageSetting from 'app/(app)/settings/profile/LanguageSetting';
+import ThemeSetting from 'app/(app)/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/(app)/settings/profile/ThemeSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/ThemeSetting.js
rename to src/app/(app)/settings/profile/ThemeSetting.js
diff --git a/src/components/pages/settings/profile/ThemeSetting.module.css b/src/app/(app)/settings/profile/ThemeSetting.module.css
similarity index 100%
rename from src/components/pages/settings/profile/ThemeSetting.module.css
rename to src/app/(app)/settings/profile/ThemeSetting.module.css
diff --git a/src/components/pages/settings/profile/TimezoneSetting.js b/src/app/(app)/settings/profile/TimezoneSetting.js
similarity index 100%
rename from src/components/pages/settings/profile/TimezoneSetting.js
rename to src/app/(app)/settings/profile/TimezoneSetting.js
diff --git a/src/app/(app)/settings/profile/page.js b/src/app/(app)/settings/profile/page.js
new file mode 100644
index 0000000000..c1ea676bca
--- /dev/null
+++ b/src/app/(app)/settings/profile/page.js
@@ -0,0 +1,11 @@
+import ProfileHeader from './ProfileHeader';
+import ProfileSettings from './ProfileSettings';
+
+export default function () {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/components/pages/settings/teams/TeamAddForm.js b/src/app/(app)/settings/teams/TeamAddForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamAddForm.js
rename to src/app/(app)/settings/teams/TeamAddForm.js
diff --git a/src/app/(app)/settings/teams/TeamDeleteButton.js b/src/app/(app)/settings/teams/TeamDeleteButton.js
new file mode 100644
index 0000000000..5e4a41ea0f
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/teams/TeamDeleteForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamDeleteForm.js
rename to src/app/(app)/settings/teams/TeamDeleteForm.js
diff --git a/src/components/pages/settings/teams/TeamJoinForm.js b/src/app/(app)/settings/teams/TeamJoinForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamJoinForm.js
rename to src/app/(app)/settings/teams/TeamJoinForm.js
diff --git a/src/app/(app)/settings/teams/TeamLeaveButton.js b/src/app/(app)/settings/teams/TeamLeaveButton.js
new file mode 100644
index 0000000000..8cc8548772
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/teams/TeamLeaveForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamLeaveForm.js
rename to src/app/(app)/settings/teams/TeamLeaveForm.js
diff --git a/src/components/pages/settings/teams/TeamWebsiteRemoveButton.js b/src/app/(app)/settings/teams/TeamWebsiteRemoveButton.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamWebsiteRemoveButton.js
rename to src/app/(app)/settings/teams/TeamWebsiteRemoveButton.js
diff --git a/src/app/(app)/settings/teams/TeamsAddButton.js b/src/app/(app)/settings/teams/TeamsAddButton.js
new file mode 100644
index 0000000000..b7850812b4
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/teams/TeamsHeader.js b/src/app/(app)/settings/teams/TeamsHeader.js
new file mode 100644
index 0000000000..444f87030b
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/teams/TeamsJoinButton.js b/src/app/(app)/settings/teams/TeamsJoinButton.js
new file mode 100644
index 0000000000..f8d2fa2385
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/teams/TeamsList.js b/src/app/(app)/settings/teams/TeamsList.js
new file mode 100644
index 0000000000..78e0c29927
--- /dev/null
+++ b/src/app/(app)/settings/teams/TeamsList.js
@@ -0,0 +1,19 @@
+'use client';
+import DataTable from 'components/common/DataTable';
+import TeamsTable from 'app/(app)/settings/teams/TeamsTable';
+import useApi from 'components/hooks/useApi';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+
+export function TeamsList() {
+ const { get } = useApi();
+ const filterQuery = useFilterQuery(['teams'], params => {
+ return get(`/teams`, {
+ ...params,
+ });
+ });
+ const { getProps } = filterQuery;
+
+ return {({ data }) => } ;
+}
+
+export default TeamsList;
diff --git a/src/app/(app)/settings/teams/TeamsTable.js b/src/app/(app)/settings/teams/TeamsTable.js
new file mode 100644
index 0000000000..ca5a7fa743
--- /dev/null
+++ b/src/app/(app)/settings/teams/TeamsTable.js
@@ -0,0 +1,46 @@
+'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 } from 'react-basics';
+import TeamDeleteButton from './TeamDeleteButton';
+import TeamLeaveButton from './TeamLeaveButton';
+
+export function TeamsTable({ data = [] }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ 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 showDelete = user.id === owner?.userId;
+
+ return (
+ <>
+
+
+
+
+
+ {formatMessage(labels.view)}
+
+
+ {showDelete && }
+ {!showDelete && }
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default TeamsTable;
diff --git a/src/components/pages/settings/teams/WebsiteTags.js b/src/app/(app)/settings/teams/WebsiteTags.js
similarity index 100%
rename from src/components/pages/settings/teams/WebsiteTags.js
rename to src/app/(app)/settings/teams/WebsiteTags.js
diff --git a/src/components/pages/settings/teams/WebsiteTags.module.css b/src/app/(app)/settings/teams/WebsiteTags.module.css
similarity index 100%
rename from src/components/pages/settings/teams/WebsiteTags.module.css
rename to src/app/(app)/settings/teams/WebsiteTags.module.css
diff --git a/src/components/pages/settings/teams/TeamAddWebsiteForm.js b/src/app/(app)/settings/teams/[id]/TeamAddWebsiteForm.js
similarity index 98%
rename from src/components/pages/settings/teams/TeamAddWebsiteForm.js
rename to src/app/(app)/settings/teams/[id]/TeamAddWebsiteForm.js
index 23e3e13602..503ad1110c 100644
--- a/src/components/pages/settings/teams/TeamAddWebsiteForm.js
+++ b/src/app/(app)/settings/teams/[id]/TeamAddWebsiteForm.js
@@ -1,7 +1,7 @@
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 WebsiteTags from '../WebsiteTags';
import useMessages from 'components/hooks/useMessages';
export function TeamAddWebsiteForm({ teamId, onSave, onClose }) {
diff --git a/src/components/pages/settings/teams/TeamEditForm.js b/src/app/(app)/settings/teams/[id]/TeamEditForm.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamEditForm.js
rename to src/app/(app)/settings/teams/[id]/TeamEditForm.js
diff --git a/src/components/pages/settings/teams/TeamMemberRemoveButton.js b/src/app/(app)/settings/teams/[id]/TeamMemberRemoveButton.js
similarity index 100%
rename from src/components/pages/settings/teams/TeamMemberRemoveButton.js
rename to src/app/(app)/settings/teams/[id]/TeamMemberRemoveButton.js
diff --git a/src/app/(app)/settings/teams/[id]/TeamMembers.js b/src/app/(app)/settings/teams/[id]/TeamMembers.js
new file mode 100644
index 0000000000..f061d22dc0
--- /dev/null
+++ b/src/app/(app)/settings/teams/[id]/TeamMembers.js
@@ -0,0 +1,27 @@
+import useApi from 'components/hooks/useApi';
+import TeamMembersTable from './TeamMembersTable';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import DataTable from 'components/common/DataTable';
+
+export function TeamMembers({ teamId, readOnly }) {
+ const { get } = useApi();
+ const { getProps } = useFilterQuery(
+ ['team:users', teamId],
+ params => {
+ return get(`/teams/${teamId}/users`, {
+ ...params,
+ });
+ },
+ { enabled: !!teamId },
+ );
+
+ return (
+ <>
+
+ {({ data }) => }
+
+ >
+ );
+}
+
+export default TeamMembers;
diff --git a/src/app/(app)/settings/teams/[id]/TeamMembersTable.js b/src/app/(app)/settings/teams/[id]/TeamMembersTable.js
new file mode 100644
index 0000000000..0c52280efc
--- /dev/null
+++ b/src/app/(app)/settings/teams/[id]/TeamMembersTable.js
@@ -0,0 +1,37 @@
+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, onChange }) {
+ 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/(app)/settings/teams/[id]/TeamSettings.js
similarity index 90%
rename from src/components/pages/settings/teams/TeamSettings.js
rename to src/app/(app)/settings/teams/[id]/TeamSettings.js
index 8c4fe8f496..8ec0ad8569 100644
--- a/src/components/pages/settings/teams/TeamSettings.js
+++ b/src/app/(app)/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/(app)/settings/teams/[id]/TeamWebsites.js b/src/app/(app)/settings/teams/[id]/TeamWebsites.js
new file mode 100644
index 0000000000..5f13027e4a
--- /dev/null
+++ b/src/app/(app)/settings/teams/[id]/TeamWebsites.js
@@ -0,0 +1,50 @@
+import { ActionForm, Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
+import TeamWebsitesTable from './TeamWebsitesTable';
+import TeamAddWebsiteForm from './TeamAddWebsiteForm';
+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';
+
+export function TeamWebsites({ teamId }) {
+ const { formatMessage, labels, messages } = useMessages();
+ const { user } = useUser();
+ const { get } = useApi();
+ const { getProps, refetch } = useFilterQuery(
+ ['team:websites', teamId],
+ params => {
+ return get(`/teams/${teamId}/websites`, {
+ ...params,
+ });
+ },
+ { enabled: !!user },
+ );
+
+ const handleWebsiteAdd = () => {
+ refetch();
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ {formatMessage(labels.addWebsite)}
+
+
+ {close => (
+
+ )}
+
+
+
+ {({ data }) => }
+ >
+ );
+}
+
+export default TeamWebsites;
diff --git a/src/app/(app)/settings/teams/[id]/TeamWebsitesTable.js b/src/app/(app)/settings/teams/[id]/TeamWebsitesTable.js
new file mode 100644
index 0000000000..8a6c6ebe5d
--- /dev/null
+++ b/src/app/(app)/settings/teams/[id]/TeamWebsitesTable.js
@@ -0,0 +1,42 @@
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+import Link from 'next/link';
+import { Button, GridColumn, GridTable, Icon, Icons, Text } from 'react-basics';
+import TeamWebsiteRemoveButton from '../TeamWebsiteRemoveButton';
+
+export function TeamWebsitesTable({ data = [], onSave }) {
+ 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 (
+ <>
+
+
+
+
+
+ {formatMessage(labels.view)}
+
+
+ {canRemove && (
+
+ )}
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default TeamWebsitesTable;
diff --git a/src/app/(app)/settings/teams/[id]/page.js b/src/app/(app)/settings/teams/[id]/page.js
new file mode 100644
index 0000000000..652e65c14a
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/teams/page.js b/src/app/(app)/settings/teams/page.js
new file mode 100644
index 0000000000..606c457288
--- /dev/null
+++ b/src/app/(app)/settings/teams/page.js
@@ -0,0 +1,15 @@
+import TeamsList from 'app/(app)/settings/teams/TeamsList';
+import TeamsHeader from './TeamsHeader';
+
+export default function () {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/components/pages/settings/users/UserAddButton.js b/src/app/(app)/settings/users/UserAddButton.js
similarity index 100%
rename from src/components/pages/settings/users/UserAddButton.js
rename to src/app/(app)/settings/users/UserAddButton.js
diff --git a/src/components/pages/settings/users/UserAddForm.js b/src/app/(app)/settings/users/UserAddForm.js
similarity index 100%
rename from src/components/pages/settings/users/UserAddForm.js
rename to src/app/(app)/settings/users/UserAddForm.js
diff --git a/src/app/(app)/settings/users/UserDeleteButton.js b/src/app/(app)/settings/users/UserDeleteButton.js
new file mode 100644
index 0000000000..22d9317132
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/users/UserDeleteForm.js
similarity index 100%
rename from src/components/pages/settings/users/UserDeleteForm.js
rename to src/app/(app)/settings/users/UserDeleteForm.js
diff --git a/src/components/pages/settings/users/UserEditForm.js b/src/app/(app)/settings/users/UserEditForm.js
similarity index 100%
rename from src/components/pages/settings/users/UserEditForm.js
rename to src/app/(app)/settings/users/UserEditForm.js
diff --git a/src/components/pages/settings/users/UserWebsites.js b/src/app/(app)/settings/users/UserWebsites.js
similarity index 92%
rename from src/components/pages/settings/users/UserWebsites.js
rename to src/app/(app)/settings/users/UserWebsites.js
index 14127275f4..f88ec78a3a 100644
--- a/src/components/pages/settings/users/UserWebsites.js
+++ b/src/app/(app)/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/(app)/settings/websites/WebsitesTable';
import useApiFilter from 'components/hooks/useApiFilter';
export function UserWebsites({ userId }) {
diff --git a/src/app/(app)/settings/users/UsersHeader.js b/src/app/(app)/settings/users/UsersHeader.js
new file mode 100644
index 0000000000..caf1f913fd
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/users/UsersList.js b/src/app/(app)/settings/users/UsersList.js
new file mode 100644
index 0000000000..0bb9089637
--- /dev/null
+++ b/src/app/(app)/settings/users/UsersList.js
@@ -0,0 +1,25 @@
+'use client';
+import useApi from 'components/hooks/useApi';
+import useFilterQuery from 'components/hooks/useFilterQuery';
+import DataTable from 'components/common/DataTable';
+import UsersTable from './UsersTable';
+import UsersHeader from './UsersHeader';
+
+export function UsersList() {
+ const { get } = useApi();
+ const filterQuery = useFilterQuery(['users'], params => {
+ return get(`/users`, {
+ ...params,
+ });
+ });
+ const { getProps } = filterQuery;
+
+ return (
+ <>
+
+ {({ data }) => }
+ >
+ );
+}
+
+export default UsersList;
diff --git a/src/app/(app)/settings/users/UsersTable.js b/src/app/(app)/settings/users/UsersTable.js
new file mode 100644
index 0000000000..6712e4a503
--- /dev/null
+++ b/src/app/(app)/settings/users/UsersTable.js
@@ -0,0 +1,57 @@
+import { Button, Text, Icon, Icons, GridTable, GridColumn } 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();
+
+ 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/(app)/settings/users/[id]/UserSettings.js
similarity index 84%
rename from src/components/pages/settings/users/UserSettings.js
rename to src/app/(app)/settings/users/[id]/UserSettings.js
index 5fadf1a17c..ea340ab799 100644
--- a/src/components/pages/settings/users/UserSettings.js
+++ b/src/app/(app)/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/(app)/settings/users/[id]/page.js b/src/app/(app)/settings/users/[id]/page.js
new file mode 100644
index 0000000000..7a6378aa42
--- /dev/null
+++ b/src/app/(app)/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/(app)/settings/users/page.tsx b/src/app/(app)/settings/users/page.tsx
new file mode 100644
index 0000000000..25545083c5
--- /dev/null
+++ b/src/app/(app)/settings/users/page.tsx
@@ -0,0 +1,13 @@
+import UsersList from 'app/(app)/settings/users/UsersList';
+import { Metadata } from 'next';
+
+export default function () {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return ;
+}
+export const metadata: Metadata = {
+ title: 'Users | umami',
+};
diff --git a/src/app/(app)/settings/websites/WebsiteAddButton.js b/src/app/(app)/settings/websites/WebsiteAddButton.js
new file mode 100644
index 0000000000..1e9dff1371
--- /dev/null
+++ b/src/app/(app)/settings/websites/WebsiteAddButton.js
@@ -0,0 +1,29 @@
+import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
+import WebsiteAddForm from './WebsiteAddForm';
+import useMessages from 'components/hooks/useMessages';
+
+export function WebsiteAddButton({ onSave }) {
+ const { formatMessage, labels, messages } = useMessages();
+ const { showToast } = useToasts();
+
+ const handleSave = async () => {
+ showToast({ message: formatMessage(messages.saved), variant: 'success' });
+ onSave?.();
+ };
+
+ return (
+
+
+
+
+
+ {formatMessage(labels.addWebsite)}
+
+
+ {close => }
+
+
+ );
+}
+
+export default WebsiteAddButton;
diff --git a/src/components/pages/settings/websites/WebsiteAddForm.js b/src/app/(app)/settings/websites/WebsiteAddForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteAddForm.js
rename to src/app/(app)/settings/websites/WebsiteAddForm.js
diff --git a/src/components/pages/settings/websites/WebsiteSettings.js b/src/app/(app)/settings/websites/WebsiteSettings.js
similarity index 80%
rename from src/components/pages/settings/websites/WebsiteSettings.js
rename to src/app/(app)/settings/websites/WebsiteSettings.js
index 63e8981486..71d5fe2334 100644
--- a/src/components/pages/settings/websites/WebsiteSettings.js
+++ b/src/app/(app)/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/app/(app)/settings/websites/Websites.js b/src/app/(app)/settings/websites/Websites.js
new file mode 100644
index 0000000000..5680506233
--- /dev/null
+++ b/src/app/(app)/settings/websites/Websites.js
@@ -0,0 +1,43 @@
+'use client';
+import WebsitesTable from 'app/(app)/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 WebsitesHeader from './WebsitesHeader';
+
+export function Websites({
+ showHeader = true,
+ showEditButton = true,
+ showTeam,
+ includeTeams,
+ onlyTeams,
+}) {
+ const { user } = useUser();
+ const { get } = useApi();
+ const filterQuery = useFilterQuery(
+ ['websites', { includeTeams, onlyTeams }],
+ params => {
+ return get(`/users/${user?.id}/websites`, {
+ includeTeams,
+ onlyTeams,
+ ...params,
+ });
+ },
+ { enabled: !!user },
+ );
+ const { getProps } = filterQuery;
+
+ return (
+ <>
+ {showHeader && }
+
+ {({ data }) => (
+
+ )}
+
+ >
+ );
+}
+
+export default Websites;
diff --git a/src/components/pages/websites/WebsiteList.module.css b/src/app/(app)/settings/websites/Websites.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteList.module.css
rename to src/app/(app)/settings/websites/Websites.module.css
diff --git a/src/app/(app)/settings/websites/WebsitesHeader.js b/src/app/(app)/settings/websites/WebsitesHeader.js
new file mode 100644
index 0000000000..6ce58d1efa
--- /dev/null
+++ b/src/app/(app)/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() {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+ {!process.env.cloudMode && }
+
+ );
+}
+
+export default WebsitesHeader;
diff --git a/src/app/(app)/settings/websites/WebsitesTable.js b/src/app/(app)/settings/websites/WebsitesTable.js
new file mode 100644
index 0000000000..6ac977abc8
--- /dev/null
+++ b/src/app/(app)/settings/websites/WebsitesTable.js
@@ -0,0 +1,59 @@
+import Link from 'next/link';
+import { Button, Text, Icon, Icons, GridTable, GridColumn } from 'react-basics';
+import useMessages from 'components/hooks/useMessages';
+import useUser from 'components/hooks/useUser';
+
+export function WebsitesTable({ data = [], showTeam, showEditButton }) {
+ const { formatMessage, labels } = useMessages();
+ const { user } = useUser();
+
+ return (
+
+
+
+ {showTeam && (
+
+ {row => row.teamWebsite[0]?.team.name}
+
+ )}
+ {showTeam && (
+
+ {row => row.user.username}
+
+ )}
+
+ {row => {
+ const {
+ id,
+ user: { id: ownerId },
+ } = row;
+
+ return (
+ <>
+ {showEditButton && (!showTeam || ownerId === user.id) && (
+
+
+
+
+
+ {formatMessage(labels.edit)}
+
+
+ )}
+
+
+
+
+
+ {formatMessage(labels.view)}
+
+
+ >
+ );
+ }}
+
+
+ );
+}
+
+export default WebsitesTable;
diff --git a/src/components/pages/settings/websites/WebsitesTable.module.css b/src/app/(app)/settings/websites/WebsitesTable.module.css
similarity index 100%
rename from src/components/pages/settings/websites/WebsitesTable.module.css
rename to src/app/(app)/settings/websites/WebsitesTable.module.css
diff --git a/src/components/pages/settings/websites/ShareUrl.js b/src/app/(app)/settings/websites/[id]/ShareUrl.js
similarity index 91%
rename from src/components/pages/settings/websites/ShareUrl.js
rename to src/app/(app)/settings/websites/[id]/ShareUrl.js
index f4569ca38c..72ba217cc3 100644
--- a/src/components/pages/settings/websites/ShareUrl.js
+++ b/src/app/(app)/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/(app)/settings/websites/[id]/TrackingCode.js
similarity index 82%
rename from src/components/pages/settings/websites/TrackingCode.js
rename to src/app/(app)/settings/websites/[id]/TrackingCode.js
index 298cd17aa9..368368d76a 100644
--- a/src/components/pages/settings/websites/TrackingCode.js
+++ b/src/app/(app)/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/(app)/settings/websites/[id]/WebsiteData.js
similarity index 89%
rename from src/components/pages/settings/websites/WebsiteData.js
rename to src/app/(app)/settings/websites/[id]/WebsiteData.js
index 08d6702e1f..07dc925750 100644
--- a/src/components/pages/settings/websites/WebsiteData.js
+++ b/src/app/(app)/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/(app)/settings/websites/[id]/WebsiteDeleteForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteDeleteForm.js
rename to src/app/(app)/settings/websites/[id]/WebsiteDeleteForm.js
diff --git a/src/components/pages/settings/websites/WebsiteEditForm.js b/src/app/(app)/settings/websites/[id]/WebsiteEditForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteEditForm.js
rename to src/app/(app)/settings/websites/[id]/WebsiteEditForm.js
diff --git a/src/components/pages/settings/websites/WebsiteResetForm.js b/src/app/(app)/settings/websites/[id]/WebsiteResetForm.js
similarity index 100%
rename from src/components/pages/settings/websites/WebsiteResetForm.js
rename to src/app/(app)/settings/websites/[id]/WebsiteResetForm.js
diff --git a/src/app/(app)/settings/websites/[id]/page.js b/src/app/(app)/settings/websites/[id]/page.js
new file mode 100644
index 0000000000..bdf3b076fe
--- /dev/null
+++ b/src/app/(app)/settings/websites/[id]/page.js
@@ -0,0 +1,15 @@
+import WebsiteSettings from '../WebsiteSettings';
+
+async function getDisabled() {
+ return !!process.env.CLOUD_MODE;
+}
+
+export default async function WebsiteSettingsPage({ params }) {
+ const disabled = await getDisabled();
+
+ if (!params.id || disabled) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/app/(app)/settings/websites/page.js b/src/app/(app)/settings/websites/page.js
new file mode 100644
index 0000000000..3cd5afa756
--- /dev/null
+++ b/src/app/(app)/settings/websites/page.js
@@ -0,0 +1,9 @@
+import Websites from './Websites';
+
+export default function () {
+ if (process.env.cloudMode) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/components/pages/websites/WebsiteTableView.js b/src/app/(app)/websites/WebsiteTableView.js
similarity index 100%
rename from src/components/pages/websites/WebsiteTableView.js
rename to src/app/(app)/websites/WebsiteTableView.js
diff --git a/src/components/pages/websites/WebsiteTableView.module.css b/src/app/(app)/websites/WebsiteTableView.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteTableView.module.css
rename to src/app/(app)/websites/WebsiteTableView.module.css
diff --git a/src/app/(app)/websites/WebsitesBrowse.js b/src/app/(app)/websites/WebsitesBrowse.js
new file mode 100644
index 0000000000..96a25743d9
--- /dev/null
+++ b/src/app/(app)/websites/WebsitesBrowse.js
@@ -0,0 +1,30 @@
+'use client';
+import WebsiteList from '../settings/websites/Websites';
+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);
+
+ 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/(app)/websites/[id]/WebsiteChart.js
similarity index 100%
rename from src/components/pages/websites/WebsiteChart.js
rename to src/app/(app)/websites/[id]/WebsiteChart.js
diff --git a/src/components/pages/websites/WebsiteChart.module.css b/src/app/(app)/websites/[id]/WebsiteChart.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteChart.module.css
rename to src/app/(app)/websites/[id]/WebsiteChart.module.css
diff --git a/src/components/pages/websites/WebsiteChartList.js b/src/app/(app)/websites/[id]/WebsiteChartList.js
similarity index 90%
rename from src/components/pages/websites/WebsiteChartList.js
rename to src/app/(app)/websites/[id]/WebsiteChartList.js
index 56cbe157b4..23764dbb85 100644
--- a/src/components/pages/websites/WebsiteChartList.js
+++ b/src/app/(app)/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/(app)/websites/[id]/WebsiteDetails.js
similarity index 74%
rename from src/components/pages/websites/WebsiteDetailsPage.js
rename to src/app/(app)/websites/[id]/WebsiteDetails.js
index 222d94d9e5..4e7ed19d90 100644
--- a/src/components/pages/websites/WebsiteDetailsPage.js
+++ b/src/app/(app)/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 { 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();
+ if (isLoading || error) {
+ return ;
+ }
+
return (
-
+ <>
}
>
)}
-
+ >
);
}
diff --git a/src/components/pages/websites/WebsiteHeader.js b/src/app/(app)/websites/[id]/WebsiteHeader.js
similarity index 95%
rename from src/components/pages/websites/WebsiteHeader.js
rename to src/app/(app)/websites/[id]/WebsiteHeader.js
index fb4e098638..daedfd435a 100644
--- a/src/components/pages/websites/WebsiteHeader.js
+++ b/src/app/(app)/websites/[id]/WebsiteHeader.js
@@ -1,7 +1,8 @@
+'use client';
import classNames from 'classnames';
import { Row, Column, 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 || {};
diff --git a/src/components/pages/websites/WebsiteHeader.module.css b/src/app/(app)/websites/[id]/WebsiteHeader.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteHeader.module.css
rename to src/app/(app)/websites/[id]/WebsiteHeader.module.css
diff --git a/src/components/pages/websites/WebsiteMenuView.js b/src/app/(app)/websites/[id]/WebsiteMenuView.js
similarity index 98%
rename from src/components/pages/websites/WebsiteMenuView.js
rename to src/app/(app)/websites/[id]/WebsiteMenuView.js
index 8c74d61515..55c617fc6c 100644
--- a/src/components/pages/websites/WebsiteMenuView.js
+++ b/src/app/(app)/websites/[id]/WebsiteMenuView.js
@@ -14,7 +14,7 @@ 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 SideNav from '../../settings/SideNav';
import usePageQuery from 'components/hooks/usePageQuery';
import useMessages from 'components/hooks/useMessages';
import styles from './WebsiteMenuView.module.css';
diff --git a/src/components/pages/websites/WebsiteMenuView.module.css b/src/app/(app)/websites/[id]/WebsiteMenuView.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteMenuView.module.css
rename to src/app/(app)/websites/[id]/WebsiteMenuView.module.css
diff --git a/src/components/pages/websites/WebsiteMetricsBar.js b/src/app/(app)/websites/[id]/WebsiteMetricsBar.js
similarity index 97%
rename from src/components/pages/websites/WebsiteMetricsBar.js
rename to src/app/(app)/websites/[id]/WebsiteMetricsBar.js
index 7ba4a8012b..44165fb3ab 100644
--- a/src/components/pages/websites/WebsiteMetricsBar.js
+++ b/src/app/(app)/websites/[id]/WebsiteMetricsBar.js
@@ -3,8 +3,8 @@ import { useApi, useDateRange, useMessages, usePageQuery, useSticky } from 'comp
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 '../../reports/FilterSelectForm';
+import PopupForm from '../../reports/PopupForm';
import { formatShortTime } from 'lib/format';
import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row } from 'react-basics';
import styles from './WebsiteMetricsBar.module.css';
diff --git a/src/components/pages/websites/WebsiteMetricsBar.module.css b/src/app/(app)/websites/[id]/WebsiteMetricsBar.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteMetricsBar.module.css
rename to src/app/(app)/websites/[id]/WebsiteMetricsBar.module.css
diff --git a/src/components/pages/event-data/EventDataMetricsBar.js b/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.js
similarity index 100%
rename from src/components/pages/event-data/EventDataMetricsBar.js
rename to src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.js
diff --git a/src/components/pages/event-data/EventDataMetricsBar.module.css b/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.module.css
similarity index 100%
rename from src/components/pages/event-data/EventDataMetricsBar.module.css
rename to src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.module.css
diff --git a/src/components/pages/event-data/EventDataTable.js b/src/app/(app)/websites/[id]/event-data/EventDataTable.js
similarity index 100%
rename from src/components/pages/event-data/EventDataTable.js
rename to src/app/(app)/websites/[id]/event-data/EventDataTable.js
diff --git a/src/components/pages/event-data/EventDataValueTable.js b/src/app/(app)/websites/[id]/event-data/EventDataValueTable.js
similarity index 100%
rename from src/components/pages/event-data/EventDataValueTable.js
rename to src/app/(app)/websites/[id]/event-data/EventDataValueTable.js
diff --git a/src/components/pages/websites/WebsiteEventData.js b/src/app/(app)/websites/[id]/event-data/WebsiteEventData.js
similarity index 83%
rename from src/components/pages/websites/WebsiteEventData.js
rename to src/app/(app)/websites/[id]/event-data/WebsiteEventData.js
index d38ca1adbf..dc5a0b9ce2 100644
--- a/src/components/pages/websites/WebsiteEventData.js
+++ b/src/app/(app)/websites/[id]/event-data/WebsiteEventData.js
@@ -1,7 +1,8 @@
+'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 EventDataTable from './EventDataTable';
+import EventDataValueTable from './EventDataValueTable';
+import { EventDataMetricsBar } from './EventDataMetricsBar';
import { useDateRange, useApi, usePageQuery } from 'components/hooks';
import styles from './WebsiteEventData.module.css';
diff --git a/src/components/pages/websites/WebsiteEventData.module.css b/src/app/(app)/websites/[id]/event-data/WebsiteEventData.module.css
similarity index 100%
rename from src/components/pages/websites/WebsiteEventData.module.css
rename to src/app/(app)/websites/[id]/event-data/WebsiteEventData.module.css
diff --git a/src/app/(app)/websites/[id]/event-data/page.js b/src/app/(app)/websites/[id]/event-data/page.js
new file mode 100644
index 0000000000..14e878c628
--- /dev/null
+++ b/src/app/(app)/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/(app)/websites/[id]/page.tsx b/src/app/(app)/websites/[id]/page.tsx
new file mode 100644
index 0000000000..4b7d6ff351
--- /dev/null
+++ b/src/app/(app)/websites/[id]/page.tsx
@@ -0,0 +1,5 @@
+import WebsiteDetails from './WebsiteDetails';
+
+export default function WebsiteReportsPage({ params: { id } }) {
+ return ;
+}
diff --git a/src/app/(app)/websites/[id]/realtime/Realtime.js b/src/app/(app)/websites/[id]/realtime/Realtime.js
new file mode 100644
index 0000000000..0ec92f0543
--- /dev/null
+++ b/src/app/(app)/websites/[id]/realtime/Realtime.js
@@ -0,0 +1,122 @@
+'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 Page from 'components/layout/Page';
+import RealtimeChart from 'components/metrics/RealtimeChart';
+import WorldMap from 'components/common/WorldMap';
+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 { useWebsite } from 'components/hooks';
+import styles from './Realtime.module.css';
+
+function mergeData(state = [], data = [], time) {
+ const ids = state.map(({ __id }) => __id);
+ return state
+ .concat(data.filter(({ __id }) => !ids.includes(__id)))
+ .filter(({ timestamp }) => timestamp >= time);
+}
+
+export function Realtime({ websiteId }) {
+ const [currentData, setCurrentData] = useState();
+ const { get, useQuery } = useApi();
+ const { data: website } = useWebsite(websiteId);
+ const { data, isLoading, error } = useQuery(
+ ['realtime', websiteId],
+ () => get(`/realtime/${websiteId}`, { startAt: currentData?.timestamp || 0 }),
+ {
+ enabled: !!(websiteId && website),
+ refetchInterval: REALTIME_INTERVAL,
+ cache: false,
+ },
+ );
+
+ useEffect(() => {
+ if (data) {
+ const date = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
+ const time = date.getTime();
+
+ setCurrentData(state => ({
+ pageviews: mergeData(state?.pageviews, data.pageviews, time),
+ sessions: mergeData(state?.sessions, data.sessions, time),
+ events: mergeData(state?.events, data.events, time),
+ timestamp: data.timestamp,
+ }));
+ }
+ }, [data]);
+
+ const realtimeData = useMemo(() => {
+ if (!currentData) {
+ return { pageviews: [], sessions: [], events: [], countries: [], visitors: [] };
+ }
+
+ currentData.countries = percentFilter(
+ currentData.sessions
+ .reduce((arr, data) => {
+ if (!arr.find(({ id }) => id === data.id)) {
+ return arr.concat(data);
+ }
+ return arr;
+ }, [])
+ .reduce((arr, { country }) => {
+ if (country) {
+ const row = arr.find(({ x }) => x === country);
+
+ if (!row) {
+ arr.push({ x: country, y: 1 });
+ } else {
+ row.y += 1;
+ }
+ }
+ return arr;
+ }, [])
+ .sort(firstBy('y', -1)),
+ );
+
+ currentData.visitors = currentData.sessions.reduce((arr, val) => {
+ if (!arr.find(({ id }) => id === val.id)) {
+ return arr.concat(val);
+ }
+ return arr;
+ }, []);
+
+ return currentData;
+ }, [currentData]);
+
+ if (isLoading || error) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default Realtime;
diff --git a/src/components/pages/realtime/RealtimePage.module.css b/src/app/(app)/websites/[id]/realtime/Realtime.module.css
similarity index 100%
rename from src/components/pages/realtime/RealtimePage.module.css
rename to src/app/(app)/websites/[id]/realtime/Realtime.module.css
diff --git a/src/components/pages/realtime/RealtimeCountries.js b/src/app/(app)/websites/[id]/realtime/RealtimeCountries.js
similarity index 81%
rename from src/components/pages/realtime/RealtimeCountries.js
rename to src/app/(app)/websites/[id]/realtime/RealtimeCountries.js
index 7a61651a20..6f484b0916 100644
--- a/src/components/pages/realtime/RealtimeCountries.js
+++ b/src/app/(app)/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/(app)/websites/[id]/realtime/RealtimeCountries.module.css
similarity index 100%
rename from src/components/pages/realtime/RealtimeCountries.module.css
rename to src/app/(app)/websites/[id]/realtime/RealtimeCountries.module.css
diff --git a/src/components/pages/realtime/RealtimeHeader.js b/src/app/(app)/websites/[id]/realtime/RealtimeHeader.js
similarity index 100%
rename from src/components/pages/realtime/RealtimeHeader.js
rename to src/app/(app)/websites/[id]/realtime/RealtimeHeader.js
diff --git a/src/components/pages/realtime/RealtimeHeader.module.css b/src/app/(app)/websites/[id]/realtime/RealtimeHeader.module.css
similarity index 100%
rename from src/components/pages/realtime/RealtimeHeader.module.css
rename to src/app/(app)/websites/[id]/realtime/RealtimeHeader.module.css
diff --git a/src/components/pages/realtime/RealtimeHome.js b/src/app/(app)/websites/[id]/realtime/RealtimeHome.js
similarity index 95%
rename from src/components/pages/realtime/RealtimeHome.js
rename to src/app/(app)/websites/[id]/realtime/RealtimeHome.js
index 4f2f627948..dbaeb541ad 100644
--- a/src/components/pages/realtime/RealtimeHome.js
+++ b/src/app/(app)/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/(app)/websites/[id]/realtime/RealtimeLog.js
similarity index 100%
rename from src/components/pages/realtime/RealtimeLog.js
rename to src/app/(app)/websites/[id]/realtime/RealtimeLog.js
diff --git a/src/components/pages/realtime/RealtimeLog.module.css b/src/app/(app)/websites/[id]/realtime/RealtimeLog.module.css
similarity index 98%
rename from src/components/pages/realtime/RealtimeLog.module.css
rename to src/app/(app)/websites/[id]/realtime/RealtimeLog.module.css
index dc78f818d0..f400cc1bc2 100644
--- a/src/components/pages/realtime/RealtimeLog.module.css
+++ b/src/app/(app)/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/RealtimePage.js b/src/app/(app)/websites/[id]/realtime/RealtimePage.js
similarity index 90%
rename from src/components/pages/realtime/RealtimePage.js
rename to src/app/(app)/websites/[id]/realtime/RealtimePage.js
index f26a286957..9363d0e78f 100644
--- a/src/components/pages/realtime/RealtimePage.js
+++ b/src/app/(app)/websites/[id]/realtime/RealtimePage.js
@@ -5,16 +5,16 @@ import { GridRow, GridColumn } 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);
diff --git a/src/components/pages/realtime/RealtimeUrls.js b/src/app/(app)/websites/[id]/realtime/RealtimeUrls.js
similarity index 100%
rename from src/components/pages/realtime/RealtimeUrls.js
rename to src/app/(app)/websites/[id]/realtime/RealtimeUrls.js
diff --git a/src/app/(app)/websites/[id]/realtime/page.tsx b/src/app/(app)/websites/[id]/realtime/page.tsx
new file mode 100644
index 0000000000..b2957fa03c
--- /dev/null
+++ b/src/app/(app)/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/(app)/websites/[id]/reports/WebsiteReports.js
similarity index 79%
rename from src/components/pages/websites/WebsiteReportsPage.js
rename to src/app/(app)/websites/[id]/reports/WebsiteReports.js
index 40631839eb..f5d9aca942 100644
--- a/src/components/pages/websites/WebsiteReportsPage.js
+++ b/src/app/(app)/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 'app/(app)/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 (
-
+ <>
-
+
@@ -49,8 +54,8 @@ export function WebsiteReportsPage({ websiteId }) {
/>
)}
{!hasData && }
-
+ >
);
}
-export default WebsiteReportsPage;
+export default WebsiteReports;
diff --git a/src/app/(app)/websites/[id]/reports/page.tsx b/src/app/(app)/websites/[id]/reports/page.tsx
new file mode 100644
index 0000000000..bf564025d6
--- /dev/null
+++ b/src/app/(app)/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/(app)/websites/page.js b/src/app/(app)/websites/page.js
new file mode 100644
index 0000000000..a30bc5834d
--- /dev/null
+++ b/src/app/(app)/websites/page.js
@@ -0,0 +1,12 @@
+'use client';
+import WebsitesHeader from '../settings/websites/WebsitesHeader';
+import WebsitesBrowse from './WebsitesBrowse';
+
+export default function WebsitesPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/app/Providers.tsx b/src/app/Providers.tsx
new file mode 100644
index 0000000000..c3d6269939
--- /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 0000000000..e2478a9580
--- /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 797eea14d6..59d145bf35 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 d12306ea89..45115d5b6b 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 0000000000..2ac3f72440
--- /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 ef89080c39..e9da0373e9 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 0000000000..bce247364e
--- /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 0000000000..b8b4560561
--- /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 Custom404() {
+ 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 0000000000..6a146801d5
--- /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 100%
rename from src/components/layout/Footer.js
rename to src/app/share/[...id]/Footer.js
diff --git a/src/components/layout/Footer.module.css b/src/app/share/[...id]/Footer.module.css
similarity index 100%
rename from src/components/layout/Footer.module.css
rename to src/app/share/[...id]/Footer.module.css
diff --git a/src/app/share/[...id]/Header.js b/src/app/share/[...id]/Header.js
new file mode 100644
index 0000000000..ab9078ecf0
--- /dev/null
+++ b/src/app/share/[...id]/Header.js
@@ -0,0 +1,29 @@
+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 100%
rename from src/components/layout/Header.module.css
rename to src/app/share/[...id]/Header.module.css
diff --git a/src/app/share/[...id]/Share.js b/src/app/share/[...id]/Share.js
new file mode 100644
index 0000000000..69d76178a3
--- /dev/null
+++ b/src/app/share/[...id]/Share.js
@@ -0,0 +1,13 @@
+'use client';
+import WebsiteDetails from 'app/(app)/websites/[id]/WebsiteDetails';
+import useShareToken from 'components/hooks/useShareToken';
+
+export default function ({ shareId }) {
+ const shareToken = useShareToken(shareId);
+
+ if (!shareToken) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/src/app/share/[...id]/page.tsx b/src/app/share/[...id]/page.tsx
new file mode 100644
index 0000000000..b674d56a44
--- /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 6e63520614..75ea945d4b 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 0000000000..4ca52b744d
--- /dev/null
+++ b/src/components/common/DataTable.module.css
@@ -0,0 +1,41 @@
+.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;
+ position: relative;
+}
+
+.body td {
+ display: flex;
+ gap: 10px;
+ min-height: 70px;
+ align-items: center;
+}
+
+.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 0000000000..489df47590
--- /dev/null
+++ b/src/components/common/DataTable.tsx
@@ -0,0 +1,95 @@
+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 {
+ result: {
+ page: number;
+ pageSize: number;
+ count: number;
+ data: any[];
+ };
+ params: {
+ query: string;
+ page: number;
+ };
+ setParams: Dispatch>;
+ isLoading: boolean;
+ error: unknown;
+ searchDelay?: number;
+ showSearch?: boolean;
+ showPaging?: boolean;
+ children: ReactNode | ((data: any) => ReactNode);
+}
+
+export function DataTable({
+ result,
+ params,
+ setParams,
+ isLoading,
+ error,
+ searchDelay,
+ showSearch = true,
+ showPaging = true,
+ children,
+}: DataTableProps) {
+ const { formatMessage, labels, messages } = useMessages();
+ const { pageSize, count } = result || {};
+ const { query, page } = params || {};
+ const hasData = Boolean(!isLoading && result?.data?.length);
+ const noResults = Boolean(!isLoading && query && !hasData);
+
+ const handleSearch = query => {
+ setParams({ ...params, query });
+ };
+
+ const handlePageChange = page => {
+ setParams({ ...params, page });
+ };
+
+ if (error) {
+ return {formatMessage(messages.error)} ;
+ }
+
+ return (
+ <>
+ {(hasData || query || isLoading) && showSearch && (
+
+ )}
+
+ {hasData ? (typeof children === 'function' ? children(result) : children) : null}
+ {isLoading && }
+ {!isLoading && !hasData && !query && (
+
+ )}
+ {noResults && }
+
+ {showPaging && (
+
+ )}
+ >
+ );
+}
+
+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 c0be761a03..2c7fcd4a2f 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/MobileMenu.js b/src/components/common/MobileMenu.js
index de1e9ffa8d..83a05dff37 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 7a5e7ed5ff..f35c2ab0c2 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 99eb70ce0a..0ed5e1f459 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 701dbe13b6..0000000000
--- 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 fd6cddfad4..0000000000
--- 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 23907948cf..95eb350f10 100644
--- a/src/components/common/UpdateNotice.js
+++ b/src/components/common/UpdateNotice.js
@@ -1,3 +1,4 @@
+'use client';
import { useEffect, useCallback, useState } from 'react';
import { createPortal } from 'react-dom';
import { Button, Row, Column } from 'react-basics';
@@ -6,12 +7,12 @@ 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 &&
diff --git a/src/components/common/WorldMap.js b/src/components/common/WorldMap.js
index 6ae84677e1..ff34d5f2b0 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/useApi.ts b/src/components/hooks/useApi.ts
index f41547a9e9..75a928d535 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 51cabf34cf..40611865c6 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 0000000000..987d3c443f
--- /dev/null
+++ b/src/components/hooks/useFilterQuery.ts
@@ -0,0 +1,26 @@
+import { useCallback, 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 result = useQuery<{
+ page: number;
+ pageSize: number;
+ count: number;
+ data: any[];
+ }>([...key, params], fn.bind(null, params), options);
+
+ const getProps = useCallback(() => {
+ const { data, isLoading, error } = result;
+ return { result: data, isLoading, error, params, setParams };
+ }, [result, params, setParams]);
+
+ return { ...result, getProps };
+}
+
+export default useFilterQuery;
diff --git a/src/components/hooks/useLanguageNames.js b/src/components/hooks/useLanguageNames.js
index ff59e93dcf..3823a26bd8 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 1374af81f5..71574d86b5 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/usePageQuery.js b/src/components/hooks/usePageQuery.js
index b275d5807c..de87e247e8 100644
--- a/src/components/hooks/usePageQuery.js
+++ b/src/components/hooks/usePageQuery.js
@@ -1,30 +1,24 @@
import { useMemo } from 'react';
-import { useRouter } from 'next/router';
+import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { buildUrl } from 'next-basics';
export function usePageQuery() {
const router = useRouter();
- const { pathname, search } = location;
- const { asPath } = router;
+ const pathname = usePathname();
+ const params = useSearchParams();
const query = useMemo(() => {
- if (!search) {
- return {};
- }
-
- const params = search.substring(1).split('&');
-
- return params.reduce((obj, item) => {
- const [key, value] = item.split('=');
+ const obj = {};
+ for (const [key, value] of params.entries()) {
obj[key] = decodeURIComponent(value);
+ }
- return obj;
- }, {});
- }, [search]);
+ return obj;
+ }, [params]);
function resolveUrl(params, reset) {
- return buildUrl(asPath.split('?')[0], { ...(reset ? {} : query), ...params });
+ return buildUrl(pathname, { ...(reset ? {} : query) });
}
return { pathname, query, resolveUrl, router };
diff --git a/src/components/hooks/useRequireLogin.ts b/src/components/hooks/useRequireLogin.ts
index d2f540d45a..b0d8029d8c 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/input/LogoutButton.js b/src/components/input/LogoutButton.js
index 2b04a78a27..6ca358a121 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 35b0eb454e..2c3f86294d 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 a6d72a2b09..8c15c615bf 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/(app)/settings/profile/TimezoneSetting';
+import DateRangeSetting from 'app/(app)/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/layout/AppLayout.js b/src/components/layout/AppLayout.js
deleted file mode 100644
index 41e2ec0d71..0000000000
--- 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/Header.js b/src/components/layout/Header.js
deleted file mode 100644
index 21cdd25139..0000000000
--- 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 07627e2a2b..0000000000
--- 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 94f9d8e663..361dffb5a1 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 c546971b6e..018aecd4ad 100644
--- a/src/components/layout/Page.module.css
+++ b/src/components/layout/Page.module.css
@@ -4,4 +4,8 @@
flex-direction: column;
background: var(--base50);
position: relative;
+ height: 100%;
+ max-width: 1320px;
+ 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 4f42aa55ae..2f7020128d 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 f13631409f..c92a89a033 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 374da26352..0000000000
--- 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 6922665fa2..0000000000
--- 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/ShareLayout.js b/src/components/layout/ShareLayout.js
deleted file mode 100644
index c634e1b6d5..0000000000
--- 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/messages.js b/src/components/messages.js
index 7f432eb3ed..04a29a4c98 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/BrowsersTable.js b/src/components/metrics/BrowsersTable.js
index e68b159f30..afc5366374 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 ec7ab73d5c..6f3b75b044 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 7b59155253..606b020ab0 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/ListTable.js b/src/components/metrics/ListTable.js
index 33ca59eb11..b9ec015c8b 100644
--- a/src/components/metrics/ListTable.js
+++ b/src/components/metrics/ListTable.js
@@ -6,7 +6,7 @@ import classNames from 'classnames';
import Empty from 'components/common/Empty';
import { formatNumber, 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 = [],
diff --git a/src/components/metrics/DataTable.module.css b/src/components/metrics/ListTable.module.css
similarity index 100%
rename from src/components/metrics/DataTable.module.css
rename to src/components/metrics/ListTable.module.css
diff --git a/src/components/metrics/MetricsTable.js b/src/components/metrics/MetricsTable.js
index 6521c4155e..1bf8419b78 100644
--- a/src/components/metrics/MetricsTable.js
+++ b/src/components/metrics/MetricsTable.js
@@ -29,7 +29,6 @@ export function MetricsTable({
const [{ startDate, endDate, modified }] = useDateRange(websiteId);
const {
resolveUrl,
- router,
query: { url, referrer, title, os, browser, device, country, region, city },
} = usePageQuery();
const { formatMessage, labels } = useMessages();
@@ -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 90135124ca..15570d8589 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 (
{
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/login/LoginLayout.js b/src/components/pages/login/LoginLayout.js
deleted file mode 100644
index d1bf47bbf3..0000000000
--- 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 df1589af78..0000000000
--- 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 bbb15a3659..0000000000
--- 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 52488c11e1..0000000000
--- 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 a217e52cad..0000000000
--- 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/TeamMembers.js b/src/components/pages/settings/teams/TeamMembers.js
deleted file mode 100644
index 207ad72d40..0000000000
--- 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 0755c19329..0000000000
--- 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 27fe2f8523..0000000000
--- 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 5ce08f355f..0000000000
--- 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 76a87b0cca..0000000000
--- 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 e17107830a..0000000000
--- 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 0bc8612e53..0000000000
--- 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 1a93710d0e..0000000000
--- 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 0cec3832bb..0000000000
--- 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 7fa507168f..0000000000
--- 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 08acafb587..0000000000
--- 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/WebsitesPage.js b/src/components/pages/websites/WebsitesPage.js
deleted file mode 100644
index 61c29448a4..0000000000
--- 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 f2ef13cab1..aeaf318af8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,7 +12,6 @@ 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';
@@ -89,29 +88,29 @@ 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 'app/(app)/settings/teams/TeamAddForm';
+export * from 'app/(app)/settings/teams/[id]/TeamAddWebsiteForm';
+export * from 'app/(app)/settings/teams/TeamDeleteForm';
+export * from 'app/(app)/settings/teams/[id]/TeamEditForm';
+export * from 'app/(app)/settings/teams/TeamJoinForm';
+export * from 'app/(app)/settings/teams/TeamLeaveForm';
+export * from 'app/(app)/settings/teams/[id]/TeamMemberRemoveButton';
+export * from 'app/(app)/settings/teams/[id]/TeamMembers';
+export * from 'app/(app)/settings/teams/[id]/TeamMembersTable';
+export * from 'app/(app)/settings/teams/[id]/TeamSettings';
+export * from 'app/(app)/settings/teams/TeamsList';
+export * from 'app/(app)/settings/teams/TeamsTable';
+export * from 'app/(app)/settings/teams/TeamWebsiteRemoveButton';
+export * from 'app/(app)/settings/teams/[id]/TeamWebsites';
+export * from 'app/(app)/settings/teams/[id]/TeamWebsitesTable';
+export * from 'app/(app)/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/(app)/settings/websites/[id]/ShareUrl';
+export * from 'app/(app)/settings/websites/[id]/TrackingCode';
+export * from 'app/(app)/settings/websites/WebsiteAddForm';
+export * from 'app/(app)/settings/websites/[id]/WebsiteDeleteForm';
+export * from 'app/(app)/settings/websites/[id]/WebsiteEditForm';
+export * from 'app/(app)/settings/websites/[id]/WebsiteResetForm';
+export * from 'app/(app)/settings/websites/WebsiteSettings';
+export * from './app/(app)/settings/websites/Websites';
+export * from 'app/(app)/settings/websites/WebsitesTable';
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 888c14843a..9d7a3da84d 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 = [
diff --git a/src/lib/middleware.ts b/src/lib/middleware.ts
index 4be958b6b8..e1e2a38b95 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.method === 'GET' ? { ...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 59638dbd66..4b910f003f 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 0000000000..739128b377
--- /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 3685753e92..98fbc29bac 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 a9d2102859..2407f7ad6b 100644
--- a/src/lib/yup.ts
+++ b/src/lib/yup.ts
@@ -1,19 +1,15 @@
+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().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 8fa13a9c7b..0000000000
--- 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 7022772c7d..0000000000
--- 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 74661e33e6..f3d3d4fdd7 100644
--- a/src/pages/api/auth/login.ts
+++ b/src/pages/api/auth/login.ts
@@ -43,8 +43,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;
diff --git a/src/pages/api/event-data/events.ts b/src/pages/api/event-data/events.ts
index 1d1d37877a..19f0c8f164 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 1cd24fe659..b2af751114 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 7f694bc62f..a12ad92f8f 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 a601f5d645..5b7a8e05e3 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 d394ef07db..b0803277db 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 d4a803a0db..e5a9ad3286 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 5b1e1e057d..212d4a0f29 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 eb4199bcd4..02eb7363cf 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 a51817bf49..9071b96222 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 b36c5638d6..2d59c9d6de 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 4d17c9220f..4344422af4 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 4006ab1289..6ff7bbe1c3 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 954d50586d..6a249de0cf 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 00d7210463..e8d3e38647 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 cc782246ea..876fbef520 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 a5527580d5..a4f80abaef 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 adb635d526..3b16ac0586 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 d0efba25f3..c3181e6a28 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 ada1efdc02..865ce0d2f5 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 d4b70b7f47..5ff8c66599 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 7ef6f533ee..5ad7321eb1 100644
--- a/src/pages/api/teams/index.ts
+++ b/src/pages/api/teams/index.ts
@@ -2,23 +2,23 @@ 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 {}
+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 +30,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 06feda8a62..0f9d01fa85 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 3ac560ede3..7926d34a52 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 72b99b8692..bfa0d31acf 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 b0fc2055b2..7cb40d610e 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 ab7d88ef63..2617047231 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,10 @@ 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, query = '', includeTeams, onlyTeams } = req.query;
if (req.method === 'GET') {
if (!user.isAdmin && user.id !== userId) {
@@ -41,8 +39,7 @@ export default async (
const websites = await getWebsitesByUserId(userId, {
page,
- filter,
- pageSize: +pageSize || undefined,
+ query,
includeTeams,
onlyTeams,
});
diff --git a/src/pages/api/users/index.ts b/src/pages/api/users/index.ts
index 991986e876..5eb26ea4a2 100644
--- a/src/pages/api/users/index.ts
+++ b/src/pages/api/users/index.ts
@@ -2,13 +2,13 @@ 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';
-export interface UsersRequestQuery extends SearchFilter {}
+export interface UsersRequestQuery extends SearchFilter {}
export interface UsersRequestBody {
username: string;
password: string;
@@ -19,7 +19,7 @@ export interface UsersRequestBody {
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 abc23dd781..ef631a0e8a 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 bfa5338e4f..5bf76a91a0 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 427cb40ea2..05a651ab6f 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 { parseDateRangeQuery } from 'lib/query';
+import { NextApiRequestQueryBody, WebsiteMetric } from 'lib/types';
+import { TimezoneTest, UnitTypeTest } from 'lib/yup';
import { NextApiResponse } from 'next';
-import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getEventMetrics } from 'queries';
-import { parseDateRangeQuery } from 'lib/query';
-
-const unitTypes = ['year', 'month', 'hour', 'day'];
+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 0e5aacceb6..084d0fea49 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().required(),
+ domain: yup.string().required(),
+ shareId: yup.string().matches(SHARE_ID_REGEX, { excludeEmptyString: true }),
+ }),
};
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 b8c37339d3..56b0b0661b 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 9985ca8925..7356c50424 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 2c7707e8d1..da6ef7f10d 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 cfd5e76798..6475741114 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 caf5491036..4e8d2a88bd 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 1f479aebed..85d029d094 100644
--- a/src/pages/api/websites/[id]/values.ts
+++ b/src/pages/api/websites/[id]/values.ts
@@ -20,9 +20,7 @@ const schema = {
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;
diff --git a/src/pages/api/websites/index.ts b/src/pages/api/websites/index.ts
index d6009caf69..329d155c4d 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,7 +19,7 @@ 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(),
@@ -34,8 +34,8 @@ 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 d13d6f6871..0000000000
--- 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 c1a3c09e73..0000000000
--- 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 bd4c74be79..0000000000
--- 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 a43f8c1f2f..0000000000
--- 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 101881a136..0000000000
--- 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 08f97f280a..0000000000
--- 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 78174f7b3a..0000000000
--- 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 a1a13a682a..0000000000
--- 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 c52207219c..0000000000
--- 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 7f5d4cf269..0000000000
--- 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 d340c1930b..0000000000
--- 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 775a6a08cf..0000000000
--- 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 7e56a7d731..0000000000
--- 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 fdd708fdcf..0000000000
--- 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 90026d87c8..0000000000
--- 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 506da107ae..0000000000
--- 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 f4551f4a89..0000000000
--- 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 a2c69df8d7..0000000000
--- 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 b99d2fc96b..0000000000
--- 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 d3ec5f9389..0000000000
--- 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 efe486a541..0000000000
--- 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 ccd88081e6..0000000000
--- 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 43eed640db..0000000000
--- 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 59eb70356d..2f987681fc 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: {
- startsWith: filter,
- ...mode,
- },
- }),
+ name: {
+ contains: query,
+ ...mode,
+ },
},
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES.description) && {
- description: {
- startsWith: filter,
- ...mode,
- },
- }),
+ description: {
+ contains: query,
+ ...mode,
+ },
},
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES.type) && {
- type: {
- startsWith: filter,
- ...mode,
- },
- }),
+ type: {
+ contains: query,
+ ...mode,
+ },
},
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES['user:username']) && {
- user: {
- username: {
- startsWith: filter,
- ...mode,
- },
+ user: {
+ username: {
+ contains: query,
+ ...mode,
},
- }),
+ },
},
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES['website:name']) && {
- website: {
- name: {
- startsWith: filter,
- ...mode,
- },
+ website: {
+ name: {
+ contains: query,
+ ...mode,
},
- }),
+ },
},
{
- ...((filterType === REPORT_FILTER_TYPES.all ||
- filterType === REPORT_FILTER_TYPES['website:domain']) && {
- website: {
- domain: {
- startsWith: filter,
- ...mode,
- },
+ website: {
+ domain: {
+ contains: query,
+ ...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 cf731ad421..9947b9a3b1 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 ee6f778b0b..b731994274 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 6417ade6f5..0e7f5124f8 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/reports/getRetention.ts b/src/queries/analytics/reports/getRetention.ts
index 1ec65fcb0a..fce7841d6f 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<
{
diff --git a/src/styles/variables.css b/src/styles/variables.css
index 726195d2ad..686fac8490 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/tsconfig.json b/tsconfig.json
index 71094dd79a..9b8b6033fc 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 d054fefd95..c90a72a1ab 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==
@@ -1343,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"
@@ -1375,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"
@@ -1420,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"
@@ -1434,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"
@@ -1450,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"
@@ -1458,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"
@@ -1483,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"
@@ -1491,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==
- 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/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.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"
@@ -1805,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"
@@ -1817,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-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-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-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-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-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-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-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-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-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.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.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.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.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.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.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.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.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"
@@ -2009,7 +2036,7 @@
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
-"@react-spring/konva@~9.7.1", "@react-spring/konva@~9.7.3":
+"@react-spring/konva@~9.7.1":
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==
@@ -2019,7 +2046,7 @@
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
-"@react-spring/native@~9.7.1", "@react-spring/native@~9.7.3":
+"@react-spring/native@~9.7.1":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/native/-/native-9.7.3.tgz#ee38d7c23482cfb4916c9b3c021de2995a4f553a"
integrity sha512-4mpxX3FuEBCUT6ae2fjhxcJW6bhr2FBwFf274eXB7n+U30Gdg8Wo2qYwcUnmiAA0S3dvP8vLTazx3+CYWFShnA==
@@ -2036,7 +2063,7 @@
dependencies:
"@react-spring/types" "~9.7.3"
-"@react-spring/three@~9.7.1", "@react-spring/three@~9.7.3":
+"@react-spring/three@~9.7.1":
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==
@@ -2051,7 +2078,7 @@
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", "@react-spring/web@~9.7.1":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.3.tgz#d9f4e17fec259f1d65495a19502ada4f5b57fa3d"
integrity sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==
@@ -2061,7 +2088,7 @@
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
-"@react-spring/zdog@~9.7.1", "@react-spring/zdog@~9.7.3":
+"@react-spring/zdog@~9.7.1":
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==
@@ -2188,26 +2215,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"
@@ -2218,51 +2230,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"
@@ -2277,20 +2264,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"
@@ -2302,17 +2275,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"
@@ -2321,14 +2283,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"
@@ -2339,16 +2293,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"
@@ -2358,15 +2302,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"
@@ -2382,19 +2317,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"
@@ -2521,6 +2456,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"
@@ -2567,29 +2507,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" "*"
@@ -2603,19 +2538,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" "*"
@@ -2627,9 +2562,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"
@@ -2638,26 +2573,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==
-
-"@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==
- 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"
+"@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@^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.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"
@@ -2669,14 +2605,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":
@@ -2687,33 +2624,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"
@@ -2728,32 +2665,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"
-
-"@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==
- 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"
+ semver "^7.5.4"
+ ts-api-utils "^1.0.1"
+
+"@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.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"
@@ -2763,19 +2699,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":
@@ -2973,23 +2910,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"
@@ -3004,6 +2941,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"
@@ -3400,6 +3342,16 @@ 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"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
@@ -3476,17 +3428,17 @@ 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:
- version "3.1.0"
- resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz"
- integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+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 "^3.1.0"
+ restore-cursor "^4.0.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==
+cli-truncate@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389"
+ integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==
dependencies:
slice-ansi "^3.0.0"
string-width "^4.2.0"
@@ -3560,16 +3512,28 @@ 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"
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
@@ -3580,7 +3544,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==
@@ -3642,17 +3606,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"
@@ -3995,6 +3948,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"
@@ -4002,13 +3962,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"
@@ -4229,6 +4182,19 @@ dotenv@^10.0.0:
resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
+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==
+
+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"
+
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
@@ -4258,13 +4224,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"
@@ -4580,14 +4539,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"
@@ -4602,14 +4553,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"
@@ -4672,11 +4623,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"
@@ -4707,6 +4653,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"
@@ -4999,17 +4965,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==
@@ -5287,10 +5248,15 @@ human-signals@^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"
@@ -5420,15 +5386,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"
@@ -5574,6 +5540,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"
@@ -5608,11 +5579,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"
@@ -5663,11 +5629,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"
@@ -5680,6 +5641,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"
@@ -5946,7 +5912,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==
@@ -5956,25 +5922,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==
- 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"
+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:
+ 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"
@@ -5999,19 +5961,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"
@@ -6112,15 +6072,16 @@ lodash@^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"
@@ -6212,12 +6173,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"
@@ -6294,7 +6255,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==
@@ -6312,6 +6273,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"
@@ -6397,7 +6363,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:
@@ -6467,11 +6433,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"
@@ -6486,12 +6447,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"
@@ -6500,15 +6461,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"
@@ -6638,6 +6599,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"
@@ -6738,6 +6706,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"
@@ -6867,6 +6842,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"
@@ -6904,6 +6884,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"
@@ -6933,13 +6918,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"
@@ -7692,21 +7670,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"
@@ -7740,18 +7718,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"
@@ -8077,10 +8043,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"
@@ -8182,9 +8148,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"
@@ -8205,13 +8171,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"
@@ -8267,11 +8226,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"
@@ -8369,7 +8323,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==
@@ -8415,15 +8369,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"
@@ -8433,6 +8378,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"
@@ -8530,17 +8483,17 @@ 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"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -8549,6 +8502,15 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
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"
@@ -8606,22 +8568,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"
@@ -8637,6 +8597,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"
@@ -8759,13 +8724,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"
@@ -8803,7 +8761,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==
@@ -8910,7 +8868,7 @@ 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.3.8, "through@>=2.2.7 <3":
version "2.3.8"
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@@ -8936,10 +8894,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"
@@ -8980,6 +8938,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"
@@ -9060,11 +9023,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"
@@ -9075,7 +9033,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==
@@ -9390,23 +9348,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==
- 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==
+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"
+ ansi-styles "^6.1.0"
+ string-width "^5.0.1"
+ strip-ansi "^7.0.1"
wrappy@1:
version "1.0.2"
@@ -9461,7 +9410,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==