summaryrefslogtreecommitdiff
path: root/web/source/settings/components
diff options
context:
space:
mode:
Diffstat (limited to 'web/source/settings/components')
-rw-r--r--web/source/settings/components/error.tsx91
-rw-r--r--web/source/settings/components/form/inputs.tsx26
-rw-r--r--web/source/settings/components/form/mutation-button.tsx4
3 files changed, 79 insertions, 42 deletions
diff --git a/web/source/settings/components/error.tsx b/web/source/settings/components/error.tsx
index 15c3bccd4..a2b4772dc 100644
--- a/web/source/settings/components/error.tsx
+++ b/web/source/settings/components/error.tsx
@@ -17,7 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import React from "react";
+import { SerializedError } from "@reduxjs/toolkit";
+import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
+import React, { ReactNode } from "react";
function ErrorFallback({ error, resetErrorBoundary }) {
return (
@@ -44,39 +46,70 @@ function ErrorFallback({ error, resetErrorBoundary }) {
);
}
-function Error({ error }) {
- /* eslint-disable-next-line no-console */
- console.error("Rendering error:", error);
- let message;
+interface GtsError {
+ /**
+ * Error message returned from the API.
+ */
+ error: string;
+
+ /**
+ * For OAuth errors: description of the error.
+ */
+ error_description?: string;
+}
+
+interface ErrorProps {
+ error: FetchBaseQueryError | SerializedError | Error | undefined;
+
+ /**
+ * Optional function to clear the error.
+ * If provided, rendered error will have
+ * a "dismiss" button.
+ */
+ reset?: () => void;
+}
- if (error.data != undefined) { // RTK Query error with data
- if (error.status) {
- message = (<>
- <b>{error.status}:</b> {error.data.error}
- {error.data.error_description &&
- <p>
- {error.data.error_description}
- </p>
- }
- </>);
- } else {
- message = error.data.error;
- }
- } else if (error.name != undefined || error.type != undefined) { // JS error
- message = (<>
- <b>{error.type && error.name}:</b> {error.message}
- </>);
- } else if (error.status && typeof error.error == "string") {
- message = (<>
- <b>{error.status}:</b> {error.error}
- </>);
+function Error({ error, reset }: ErrorProps) {
+ if (error === undefined) {
+ return null;
+ }
+
+ /* eslint-disable-next-line no-console */
+ console.error("caught error: ", error);
+
+ let message: ReactNode;
+ if ("status" in error) {
+ // RTK Query error with data.
+ const gtsError = error.data as GtsError;
+ const errMsg = gtsError.error_description ?? gtsError.error;
+ message = <>Code {error.status} {errMsg}</>;
} else {
- message = error.message ?? error;
+ // SerializedError or Error.
+ const errMsg = error.message ?? JSON.stringify(error);
+ message = (
+ <>{error.name && `${error.name}: `}{errMsg}</>
+ );
+ }
+
+ let className = "error";
+ if (reset) {
+ className += " with-dismiss";
}
return (
- <div className="error">
- {message}
+ <div className={className}>
+ <span>{message}</span>
+ { reset &&
+ <span
+ className="dismiss"
+ onClick={reset}
+ role="button"
+ tabIndex={0}
+ >
+ <span>Dismiss</span>
+ <i className="fa fa-fw fa-close" aria-hidden="true" />
+ </span>
+ }
</div>
);
}
diff --git a/web/source/settings/components/form/inputs.tsx b/web/source/settings/components/form/inputs.tsx
index f82937fc1..c68095d95 100644
--- a/web/source/settings/components/form/inputs.tsx
+++ b/web/source/settings/components/form/inputs.tsx
@@ -29,6 +29,7 @@ import type {
RadioFormInputHook,
TextFormInputHook,
} from "../../lib/form/types";
+import { nanoid } from "nanoid";
export interface TextInputProps extends React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
@@ -92,22 +93,25 @@ export interface FileInputProps extends React.DetailedHTMLProps<
export function FileInput({ label, field, ...props }: FileInputProps) {
const { onChange, ref, infoComponent } = field;
+ const id = nanoid();
return (
<div className="form-field file">
- <label>
- <div className="label">{label}</div>
+ <label className="label-label" htmlFor={id}>
+ {label}
+ </label>
+ <label className="label-button" htmlFor={id}>
<div className="file-input button">Browse</div>
- {infoComponent}
- {/* <a onClick={removeFile("header")}>remove</a> */}
- <input
- type="file"
- className="hidden"
- onChange={onChange}
- ref={ref ? ref as RefObject<HTMLInputElement> : undefined}
- {...props}
- />
</label>
+ <input
+ id={id}
+ type="file"
+ className="hidden"
+ onChange={onChange}
+ ref={ref ? ref as RefObject<HTMLInputElement> : undefined}
+ {...props}
+ />
+ {infoComponent}
</div>
);
}
diff --git a/web/source/settings/components/form/mutation-button.tsx b/web/source/settings/components/form/mutation-button.tsx
index 1e6d8c968..5d831cd24 100644
--- a/web/source/settings/components/form/mutation-button.tsx
+++ b/web/source/settings/components/form/mutation-button.tsx
@@ -51,9 +51,9 @@ export default function MutationButton({
}
return (
- <div className={wrapperClassName}>
+ <div className={wrapperClassName ? wrapperClassName : "mutation-button"}>
{(showError && targetsThisButton && result.error) &&
- <Error error={result.error} />
+ <Error error={result.error} reset={result.reset} />
}
<button
type="submit"