From 2c46993264a3d52dea6a930a0e9c34c99b18992b Mon Sep 17 00:00:00 2001 From: stacksmash76 <98354295+stacksmash76@users.noreply.github.com> Date: Thu, 12 May 2022 16:26:41 +0200 Subject: [PATCH] feat(web): error boundry and fixes (#270) * web: Added error handling. Fixed table overflow issues. Improved release status titles. Fixed a few bugs where certain forms/modals didn't close properly. feat(web): Added react-error-boundary and stacktracey for handling errors. In case of a recoverable/non-state updating error, a notification is thrown, otherwise the user is shown an error page with the appropriate stack trace and error type and cause with the possibility of resolving the error by reseting the page's state. enhancement(web/data-table/cells): Improved cell display behavior in tables -- it's now scrollable on mobile. Improved release status string readability. enhancement(web/settings/Irc): Made IRC refetch interval every 3 seconds for those who are not patient. fix(web/modals/DeleteModal): Fixed modal bug where it didn't close on button click. fix(web/forms/IndexerForms): Fixed bug where the form didn't close on button click. enhancement(web/ReleaseTable): Made the table more compact by utilizing 3em padding between cells instead of 6. enhancement(web/ActivityTable): Ditto as above. enhancement(web): Changed width of every page section from max-w-7xl (1280px to where applicable) chore(web/dashboard/ActivityTable): Reformatted the file to a saner syntax. enhancement(web/dashboard/StatsItem): Enlarged font size, set font to extrabold instead of bold and made padding consistent. fix(web/navbar): Fixed bold font not showing properly on Firefox due to an argument ordering issue. * chore: update pkg and fix broken proxy --- web/package.json | 4 +- web/public/index.html | 4 +- web/src/App.tsx | 73 +++-- web/src/components/alerts/ErrorPage.tsx | 87 +++++ web/src/components/alerts/index.tsx | 2 + web/src/components/data-table/Cells.tsx | 28 +- web/src/components/emptystates/index.tsx | 46 ++- web/src/components/inputs/input_wide.tsx | 6 +- web/src/components/modals/index.tsx | 7 +- web/src/forms/settings/IndexerForms.tsx | 3 + web/src/forms/settings/IrcForms.tsx | 2 +- web/src/index.css | 9 + web/src/screens/Base.tsx | 4 +- web/src/screens/Logs.tsx | 6 +- web/src/screens/Settings.tsx | 4 +- web/src/screens/dashboard/ActivityTable.tsx | 337 ++++++++++---------- web/src/screens/dashboard/Stats.tsx | 6 +- web/src/screens/dashboard/index.tsx | 2 +- web/src/screens/filters/details.tsx | 8 +- web/src/screens/filters/list.tsx | 9 +- web/src/screens/releases/ReleaseTable.tsx | 16 +- web/src/screens/releases/index.tsx | 6 +- web/src/screens/settings/Feed.tsx | 7 +- web/src/screens/settings/Irc.tsx | 6 +- web/src/setupProxy.js | 11 + web/yarn.lock | 51 +++ 26 files changed, 464 insertions(+), 280 deletions(-) create mode 100644 web/src/components/alerts/ErrorPage.tsx create mode 100644 web/src/setupProxy.js diff --git a/web/package.json b/web/package.json index b416ee7..f9778d9 100644 --- a/web/package.json +++ b/web/package.json @@ -2,7 +2,6 @@ "name": "web", "version": "0.1.0", "private": true, - "proxy": "http://127.0.0.1:7474", "homepage": ".", "dependencies": { "@fontsource/inter": "^4.5.4", @@ -13,6 +12,7 @@ "react": "^17.0.2", "react-cookie": "^4.1.1", "react-dom": "^17.0.2", + "react-error-boundary": "^3.1.4", "react-hot-toast": "^2.1.1", "react-multi-select-component": "4.2.5", "react-query": "^3.18.1", @@ -21,6 +21,7 @@ "react-scripts": "^5.0.0", "react-select": "5.0.0-beta.0", "react-table": "^7.7.0", + "stacktracey": "^2.1.8", "web-vitals": "^1.0.1" }, "scripts": { @@ -66,6 +67,7 @@ "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", "eslint-watch": "^8.0.0", + "http-proxy-middleware": "^2.0.6", "postcss": "^8.4.6", "tailwindcss": "^3.0.18", "typescript": "^4.1.2" diff --git a/web/public/index.html b/web/public/index.html index c4e8954..2b0e48a 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -26,8 +26,8 @@ - - + +
diff --git a/web/src/App.tsx b/web/src/App.tsx index 1782170..f4875e8 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,49 +1,64 @@ -import { Fragment } from "react"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; -import { QueryClient, QueryClientProvider } from "react-query"; +import { QueryClient, QueryClientProvider, useQueryErrorResetBoundary } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; -import { Toaster } from "react-hot-toast"; +import { ErrorBoundary } from "react-error-boundary"; +import { toast, Toaster } from "react-hot-toast"; import Base from "./screens/Base"; import { Login } from "./screens/auth/login"; import { Logout } from "./screens/auth/logout"; import { Onboarding } from "./screens/auth/onboarding"; + import { baseUrl } from "./utils"; - import { AuthContext, SettingsContext } from "./utils/Context"; +import { ErrorPage } from "./components/alerts"; +import Toast from "./components/notifications/Toast"; -function Protected() { - return ( - - - - - ) -} - -export const queryClient = new QueryClient(); +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { useErrorBoundary: true, }, + mutations: { + onError: (error) => { + // Use a format string to convert the error object to a proper string without much hassle. + const message = ( + typeof (error) === "object" && typeof ((error as Error).message) ? + (error as Error).message : + `${error}` + ); + toast.custom((t) => ); + } + }, + }, +}); export function App() { + const { reset } = useQueryErrorResetBoundary(); + const authContext = AuthContext.useValue(); const settings = SettingsContext.useValue(); return ( - - - - {authContext.isLoggedIn ? ( - - ) : ( - - - - - )} - - {settings.debug ? ( - - ) : null} + + + + + {authContext.isLoggedIn ? ( + + ) : ( + + + + + )} + + {settings.debug ? ( + + ) : null} + ); } \ No newline at end of file diff --git a/web/src/components/alerts/ErrorPage.tsx b/web/src/components/alerts/ErrorPage.tsx new file mode 100644 index 0000000..59c3029 --- /dev/null +++ b/web/src/components/alerts/ErrorPage.tsx @@ -0,0 +1,87 @@ +import StackTracey from "stacktracey"; +import type { FallbackProps } from "react-error-boundary"; +import { RefreshIcon } from "@heroicons/react/solid"; + +export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => { + const stack = new StackTracey(error).withSources(); + const summary = stack.clean().asTable({ + maxColumnWidths: { + callee: 48, + file: 48, + sourceLine: 256 + } + }); + + const type = String( + (error && error.constructor && error.constructor.name) + || typeof (error) + ); + const msg = String(error && error.message); + return ( +
+
+

+ We caught an unrecoverable error! +

+

+ Please consider reporting this error to our + {" "} + + GitHub page + + {" or to "} + + our official Discord channel + + . +

+ +
+
+ ); +} \ No newline at end of file diff --git a/web/src/components/alerts/index.tsx b/web/src/components/alerts/index.tsx index 8833122..8e8e141 100644 --- a/web/src/components/alerts/index.tsx +++ b/web/src/components/alerts/index.tsx @@ -28,3 +28,5 @@ export function AlertWarning({ title, text }: props) { ); } + +export { ErrorPage } from "./ErrorPage"; \ No newline at end of file diff --git a/web/src/components/data-table/Cells.tsx b/web/src/components/data-table/Cells.tsx index bbe3c85..d09e9cc 100644 --- a/web/src/components/data-table/Cells.tsx +++ b/web/src/components/data-table/Cells.tsx @@ -15,14 +15,11 @@ export const AgeCell = ({ value }: CellProps) => ( ); -export const ReleaseCell = ({ value }: CellProps) => ( -
- {value} -
-); - -export const IndexerCell = ({ value }: CellProps) => ( -
+export const TitleCell = ({ value }: CellProps) => ( +
{value}
); @@ -46,7 +43,6 @@ const StatusCellMap: Record = { "PUSH_REJECTED": { colors: "bg-blue-200 dark:bg-blue-100 text-blue-400 dark:text-blue-800 hover:bg-blue-300 dark:hover:bg-blue-400", icon: