diff options
Diffstat (limited to 'web/source/settings/components')
-rw-r--r-- | web/source/settings/components/error.tsx | 91 | ||||
-rw-r--r-- | web/source/settings/components/form/inputs.tsx | 26 | ||||
-rw-r--r-- | web/source/settings/components/form/mutation-button.tsx | 4 |
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" |