From 40893441ec189efa0e09650d3f8655c575c0d9cd Mon Sep 17 00:00:00 2001 From: Neraste Date: Sun, 14 Dec 2025 15:56:46 +0100 Subject: [PATCH] First attempt to use new forms --- package-lock.json | 17 +++++ package.json | 1 + src/components/generics/form/Fields.jsx | 99 +++++++++++++++++++++++++ src/components/generics/form/Form.jsx | 63 ++++++++++++++++ src/components/registration/Login.jsx | 57 +++++++------- src/contexts/index.js | 3 + 6 files changed, 215 insertions(+), 25 deletions(-) create mode 100644 src/components/generics/form/Fields.jsx create mode 100644 src/components/generics/form/Form.jsx create mode 100644 src/contexts/index.js diff --git a/package-lock.json b/package-lock.json index e8bcad30..ac85919e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-highlight-words": "^0.21.0", + "react-hook-form": "^7.68.0", "react-redux": "^9.2.0", "react-router": "^7.5.2", "react-transition-group": "^4.4.5", @@ -6454,6 +6455,22 @@ "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0" } }, + "node_modules/react-hook-form": { + "version": "7.68.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.68.0.tgz", + "integrity": "sha512-oNN3fjrZ/Xo40SWlHf1yCjlMK417JxoSJVUXQjGdvdRCU07NTFei1i1f8ApUAts+IVh14e4EdakeLEA+BEAs/Q==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 4d722831..43a7f3bd 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-highlight-words": "^0.21.0", + "react-hook-form": "^7.68.0", "react-redux": "^9.2.0", "react-router": "^7.5.2", "react-transition-group": "^4.4.5", diff --git a/src/components/generics/form/Fields.jsx b/src/components/generics/form/Fields.jsx new file mode 100644 index 00000000..d556cde9 --- /dev/null +++ b/src/components/generics/form/Fields.jsx @@ -0,0 +1,99 @@ +import { InlineFormContext } from 'contexts' +import PropTypes from 'prop-types' +import { useContext } from 'react' + +function Field({ children, ...rest }) { + const inline = useContext(InlineFormContext) + + if (inline) { + return {children} + } + + return {children} +} + +Field.propTypes = { + children: PropTypes.node.isRequired, +} + +function FieldInline({ children }) { + return
{children}
+} + +FieldInline.propTypes = { + children: PropTypes.node.isRequired, +} + +function FieldBlock({ id, label, errors, children }) { + let errorNotification + const error = errors?.[id] + if (error) { + if (error.type === 'required') { + errorNotification =

This field is requiered

+ } else { + errorNotification =

{error.message}

+ } + } + + return ( +
+ +
+ {children} + {errorNotification} +
+
+ ) +} + +FieldBlock.propTypes = { + id: PropTypes.string.isRequired, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, + errors: PropTypes.object, + children: PropTypes.node.isRequired, +} + +export function InputField({ id, label, type, register, errors, validation }) { + return ( + +
+ +
+
+ ) +} + +InputField.propTypes = { + id: PropTypes.string.isRequired, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, + type: PropTypes.string, + register: PropTypes.func, + errors: PropTypes.object, + validation: PropTypes.object, +} + +// export function SelectField({ +// id, +// label, +// options, +// multiple, +// registrer, +// errors, +// validation, +// }) { +// return ( +// +//
+// +//
+//
+// ) +// } diff --git a/src/components/generics/form/Form.jsx b/src/components/generics/form/Form.jsx new file mode 100644 index 00000000..6d75f976 --- /dev/null +++ b/src/components/generics/form/Form.jsx @@ -0,0 +1,63 @@ +import classNames from 'classnames' +import PropTypes from 'prop-types' +import { Form as FormOrigin } from 'react-hook-form' + +export function Form({ inline, children, ...rest }) { + if (inline) { + // wrap by InlineFormContext + return null + } + + return {children} +} + +Form.propTypes = { + inline: PropTypes.bool, + children: PropTypes.node, +} + +export function FormBlock({ + title, + action, + control, + noSubmit, + submitClass = 'primary', + submitText = 'Submit', + extraControls, + children, +}) { + return ( + + {title && ( +
+

{title}

+
+ )} +
{children}
+
+ {!noSubmit && ( + + )} + {extraControls} +
+
+ ) +} + +FormBlock.propTypes = { + title: PropTypes.string, + action: PropTypes.string.isRequired, + control: PropTypes.object.isRequired, + noSubmit: PropTypes.bool, + submitClass: PropTypes.string, + submitText: PropTypes.string, + extraControls: PropTypes.element, + children: PropTypes.node, +} diff --git a/src/components/registration/Login.jsx b/src/components/registration/Login.jsx index 293e145c..282fe7bf 100644 --- a/src/components/registration/Login.jsx +++ b/src/components/registration/Login.jsx @@ -1,7 +1,18 @@ +import { useForm } from 'react-hook-form' import { useSelector } from 'react-redux' import { Navigate, NavLink, useSearchParams } from 'react-router' -import { FormBlock, InputField } from 'components/generics/Form' +import { InputField } from 'components/generics/form/Fields' +import { Form } from 'components/generics/form/Form' + +function ForgottenPasswordLink() { + return ( + + Or reset your password{' '} + if you have forgotten it. + + ) +} export default function Login() { const isLoggedIn = useSelector((state) => !!state.token) @@ -9,36 +20,23 @@ export default function Login() { const [searchParams, _] = useSearchParams() + const { + register, + formState: { errors }, + control, + } = useForm() + if (isLoggedIn) { return } - let forgottenPasswordLink - if (serverSettings?.email_enabled) { - forgottenPasswordLink = ( - - {' '} - Or - reset your password - {' '} - if you have forgotten it. - - ) - } - return (

Login

- +
} - required + register={register} + errors={errors} + validation={{ required: true }} /> } type="password" - required + register={register} + errors={errors} + validation={{ required: true }} /> - +

New here? Create a new account. - {forgottenPasswordLink} + {serverSettings?.email_enabled && ( + <> + {' '} + + + )}

diff --git a/src/contexts/index.js b/src/contexts/index.js new file mode 100644 index 00000000..77414d28 --- /dev/null +++ b/src/contexts/index.js @@ -0,0 +1,3 @@ +import { createContext } from 'react' + +export const InlineFormContext = createContext(false)