Added error boundary. Fixed #1976.
parent
d73def80c7
commit
3f1ed750f0
|
@ -0,0 +1,33 @@
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
import { ErrorBoundary as Boundary } from 'react-error-boundary';
|
||||||
|
import { Button } from 'react-basics';
|
||||||
|
import useMessages from 'hooks/useMessages';
|
||||||
|
import styles from './ErrorBoundry.module.css';
|
||||||
|
|
||||||
|
const logError = (error, info) => {
|
||||||
|
console.error(error, info.componentStack);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ErrorBoundary({ children }) {
|
||||||
|
const { formatMessage, messages } = useMessages();
|
||||||
|
|
||||||
|
const fallbackRender = ({ error, resetErrorBoundary }) => {
|
||||||
|
console.log({ error });
|
||||||
|
return (
|
||||||
|
<div className={styles.error} role="alert">
|
||||||
|
<h1>{formatMessage(messages.error)}</h1>
|
||||||
|
<h3>{error.message}</h3>
|
||||||
|
<pre>{error.stack}</pre>
|
||||||
|
<Button onClick={resetErrorBoundary}>OK</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Boundary fallbackRender={fallbackRender} onError={logError}>
|
||||||
|
{children}
|
||||||
|
</Boundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorBoundary;
|
|
@ -0,0 +1,19 @@
|
||||||
|
.error {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
z-index: var(--z-index-overlay);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 600px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error button {
|
||||||
|
align-self: center;
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ export function DateFilter({ websiteId, value, className }) {
|
||||||
if (data) {
|
if (data) {
|
||||||
setDateRange({ value, ...getDateRangeValues(new Date(data.createdAt), Date.now()) });
|
setDateRange({ value, ...getDateRangeValues(new Date(data.createdAt), Date.now()) });
|
||||||
}
|
}
|
||||||
} else {
|
} else if (value !== 'all') {
|
||||||
setDateRange(value);
|
setDateRange(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ export function DateFilter({ websiteId, value, className }) {
|
||||||
value: '90day',
|
value: '90day',
|
||||||
},
|
},
|
||||||
{ label: formatMessage(labels.thisYear), value: '1year' },
|
{ label: formatMessage(labels.thisYear), value: '1year' },
|
||||||
{
|
websiteId && {
|
||||||
label: formatMessage(labels.allTime),
|
label: formatMessage(labels.allTime),
|
||||||
value: 'all',
|
value: 'all',
|
||||||
divider: true,
|
divider: true,
|
||||||
|
@ -71,7 +71,7 @@ export function DateFilter({ websiteId, value, className }) {
|
||||||
value: 'custom',
|
value: 'custom',
|
||||||
divider: true,
|
divider: true,
|
||||||
},
|
},
|
||||||
];
|
].filter(n => n);
|
||||||
|
|
||||||
const renderValue = value => {
|
const renderValue = value => {
|
||||||
return value === 'custom' ? (
|
return value === 'custom' ? (
|
||||||
|
|
|
@ -53,7 +53,7 @@ export function parseDateRange(value, locale = 'en-US') {
|
||||||
|
|
||||||
const match = value.match(/^(?<num>[0-9-]+)(?<unit>hour|day|week|month|year)$/);
|
const match = value.match(/^(?<num>[0-9-]+)(?<unit>hour|day|week|month|year)$/);
|
||||||
|
|
||||||
if (!match) return;
|
if (!match) return {};
|
||||||
|
|
||||||
const { num, unit } = match.groups;
|
const { num, unit } = match.groups;
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
"react-basics": "^0.77.0",
|
"react-basics": "^0.77.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-error-boundary": "^4.0.4",
|
||||||
"react-intl": "^5.24.7",
|
"react-intl": "^5.24.7",
|
||||||
"react-simple-maps": "^2.3.0",
|
"react-simple-maps": "^2.3.0",
|
||||||
"react-spring": "^9.4.4",
|
"react-spring": "^9.4.4",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Script from 'next/script';
|
import Script from 'next/script';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import ErrorBoundary from 'components/common/ErrorBoundary';
|
||||||
import useLocale from 'hooks/useLocale';
|
import useLocale from 'hooks/useLocale';
|
||||||
import useConfig from 'hooks/useConfig';
|
import useConfig from 'hooks/useConfig';
|
||||||
import '@fontsource/inter/400.css';
|
import '@fontsource/inter/400.css';
|
||||||
|
@ -41,11 +42,26 @@ export default function App({ Component, pageProps }) {
|
||||||
textComponent={Wrapper}
|
textComponent={Wrapper}
|
||||||
onError={() => null}
|
onError={() => null}
|
||||||
>
|
>
|
||||||
|
<ErrorBoundary>
|
||||||
<Head>
|
<Head>
|
||||||
<link rel="icon" href={`${basePath}/favicon.ico`} />
|
<link rel="icon" href={`${basePath}/favicon.ico`} />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href={`${basePath}/apple-touch-icon.png`} />
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href={`${basePath}/favicon-32x32.png`} />
|
rel="apple-touch-icon"
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href={`${basePath}/favicon-16x16.png`} />
|
sizes="180x180"
|
||||||
|
href={`${basePath}/apple-touch-icon.png`}
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href={`${basePath}/favicon-32x32.png`}
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href={`${basePath}/favicon-16x16.png`}
|
||||||
|
/>
|
||||||
<link rel="manifest" href={`${basePath}/site.webmanifest`} />
|
<link rel="manifest" href={`${basePath}/site.webmanifest`} />
|
||||||
<link rel="mask-icon" href={`${basePath}/safari-pinned-tab.svg`} color="#5bbad5" />
|
<link rel="mask-icon" href={`${basePath}/safari-pinned-tab.svg`} color="#5bbad5" />
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
|
@ -56,6 +72,7 @@ export default function App({ Component, pageProps }) {
|
||||||
</Head>
|
</Head>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
{!pathname.includes('/share/') && <Script src={`${basePath}/telemetry.js`} />}
|
{!pathname.includes('/share/') && <Script src={`${basePath}/telemetry.js`} />}
|
||||||
|
</ErrorBoundary>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1497,7 +1497,7 @@
|
||||||
core-js-pure "^3.25.1"
|
core-js-pure "^3.25.1"
|
||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
"@babel/runtime@^7.0.0":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5":
|
||||||
version "7.21.0"
|
version "7.21.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
|
||||||
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
|
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
|
||||||
|
@ -8258,6 +8258,13 @@ react-dom@^18.2.0:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
scheduler "^0.23.0"
|
scheduler "^0.23.0"
|
||||||
|
|
||||||
|
react-error-boundary@^4.0.4:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.4.tgz#d2e84505b0a67cec7a6bf33b0146faadfe31597d"
|
||||||
|
integrity sha512-AbqMFx8bCsob8rCHZvJYQ42MQijK0/034RUvan9qrqyJCpazr8d9vKHrysbxcr6odoHLZvQEcYomFPoIqH9fow==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
|
||||||
react-fast-compare@^2.0.1:
|
react-fast-compare@^2.0.1:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz"
|
resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz"
|
||||||
|
|
Loading…
Reference in New Issue