diff --git a/ui/package-lock.json b/ui/package-lock.json index 1bcd290..dcdb639 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -14,7 +14,8 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-leaflet": "^4.2.0", - "stylelint-config-prettier-scss": "^0.0.1" + "stylelint-config-prettier-scss": "^0.0.1", + "yup": "^1.1.1" }, "devDependencies": { "@babel/core": "^7.21.0", @@ -15953,6 +15954,11 @@ "react-is": "^16.13.1" } }, + "node_modules/property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" + }, "node_modules/property-information": { "version": "5.6.0", "dev": true, @@ -18890,6 +18896,11 @@ "node": ">=0.6.0" } }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, "node_modules/tmpl": { "version": "1.0.5", "dev": true, @@ -18962,6 +18973,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/tr46": { "version": "0.0.3", "dev": true, @@ -20540,6 +20556,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yup": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.1.1.tgz", + "integrity": "sha512-KfCGHdAErqFZWA5tZf7upSUnGKuTOnsI3hUsLr7fgVtx+DK04NPV01A68/FslI4t3s/ZWpvXJmgXhd7q6ICnag==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "1.0.5", "dev": true, diff --git a/ui/package.json b/ui/package.json index c493781..262c23a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,13 +14,14 @@ "build-storybook": "build-storybook" }, "dependencies": { - "leaflet": "^1.9.3", "classnames": "^2.3.2", + "leaflet": "^1.9.3", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-leaflet": "^4.2.0", "react-icons": "^4.7.1", - "stylelint-config-prettier-scss": "^0.0.1" + "react-leaflet": "^4.2.0", + "stylelint-config-prettier-scss": "^0.0.1", + "yup": "^1.1.1" }, "devDependencies": { "@babel/core": "^7.21.0", diff --git a/ui/src/assets/images/logo/pngs/logo_accent.png b/ui/src/assets/images/logo/pngs/logo_accent.png new file mode 100644 index 0000000..fc3170e Binary files /dev/null and b/ui/src/assets/images/logo/pngs/logo_accent.png differ diff --git a/ui/src/assets/images/logo/svgs/logo_accent.svg b/ui/src/assets/images/logo/svgs/logo_accent.svg new file mode 100644 index 0000000..78a189f --- /dev/null +++ b/ui/src/assets/images/logo/svgs/logo_accent.svg @@ -0,0 +1,20 @@ + diff --git a/ui/src/components/Button/Button.jsx b/ui/src/components/Button/Button.jsx index 619cadf..1a82588 100644 --- a/ui/src/components/Button/Button.jsx +++ b/ui/src/components/Button/Button.jsx @@ -43,7 +43,7 @@ Button.propTypes = { /** color variant */ variant: PropTypes.oneOf(buttonVariants), /** event that will be triggered when the button is clicked */ - onClick: PropTypes.func.isRequired, + onClick: PropTypes.func, /** content that will be displayed in the button */ children: PropTypes.string.isRequired, /** additional css styles */ diff --git a/ui/src/components/LoginForm/LoginForm.jsx b/ui/src/components/LoginForm/LoginForm.jsx new file mode 100644 index 0000000..cdc38f1 --- /dev/null +++ b/ui/src/components/LoginForm/LoginForm.jsx @@ -0,0 +1,125 @@ +import styles from './LoginForm.module.scss' +import Button from '../Button/Button' +import { useRef, useState } from 'react' +import { object, string } from 'yup' +import logo from '../../assets/images/logo/svgs/logo_accent.svg' +import classNames from 'classnames' + +export default function LoginForm() { + const emailOrNickInput = useRef(null) + const passwordInput = useRef(null) + + const [errors, setErrors] = useState({}) + + const [loginError, setLoginError] = useState(false) + + const loginForm = useRef(null) + + const schema = object({ + emailOrNick: string().required('Pole "Email/nick" jest wymagane.'), + password: string().required('Pole "Hasło" jest wymagane.') + }) + + const handleChange = (event) => { + const { name } = event.target + if (errors[name]) { + setErrors((prev) => { + const newErrors = { ...prev } + delete newErrors[name] + return newErrors + }) + } + setLoginError(false) + } + + const handleSubmit = (event) => { + event.preventDefault() + schema + .validate( + { + emailOrNick: emailOrNickInput.current.value, + password: passwordInput.current.value + }, + { + abortEarly: false + } + ) + .then(() => { + console.log('ok') + // TODO: send data to server + setLoginError(true) // if failed to login + }) + .catch((error) => { + const newErrors = {} + error.inner.forEach((error) => { + newErrors[error.path] = error.message + }) + setErrors(newErrors) + loginForm.current[Object.keys(newErrors)[0]]?.focus() + }) + } + + return ( +