feat(dashboard-v2): implement LoginForm component
This commit is contained in:
parent
a5a6757bf1
commit
d64d8212d1
|
@ -25,9 +25,11 @@
|
|||
"classnames": "^2.3.1",
|
||||
"copy-text-to-clipboard": "^3.0.1",
|
||||
"dayjs": "^1.10.8",
|
||||
"formik": "^2.2.9",
|
||||
"gatsby": "^4.6.2",
|
||||
"gatsby-plugin-postcss": "^5.7.0",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"ky": "^0.30.0",
|
||||
"nanoid": "^3.3.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"postcss": "^8.4.6",
|
||||
|
@ -39,7 +41,8 @@
|
|||
"react-use": "^17.3.2",
|
||||
"skynet-js": "^3.0.2",
|
||||
"swr": "^1.2.2",
|
||||
"tailwindcss": "^3.0.23"
|
||||
"tailwindcss": "^3.0.23",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.4",
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import PropTypes from "prop-types";
|
||||
import cn from "classnames";
|
||||
import { Field } from "formik";
|
||||
|
||||
export const TextField = ({ id, label, name, error, touched, ...props }) => {
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-1">
|
||||
{label && (
|
||||
<label className="font-sans uppercase text-palette-300 text-xs" htmlFor={id}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<Field
|
||||
id={id}
|
||||
name={name}
|
||||
className={cn("w-full py-2 px-4 bg-palette-100 rounded-sm placeholder:text-palette-200 outline-1", {
|
||||
"focus:outline outline-palette-200": !error || !touched,
|
||||
"outline outline-error": touched && error,
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
{touched && error && (
|
||||
<div className="text-error">
|
||||
<small>{error}</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/** Besides noted properties, it accepts all props accepted by:
|
||||
* - a regular <input> element
|
||||
* - Formik's <Field> component
|
||||
*/
|
||||
TextField.propTypes = {
|
||||
/**
|
||||
* ID for the field. Used to couple <label> and <input> elements
|
||||
*/
|
||||
id: PropTypes.string,
|
||||
/**
|
||||
* Label for the field
|
||||
*/
|
||||
label: PropTypes.string,
|
||||
/**
|
||||
* Name of the field
|
||||
*/
|
||||
name: PropTypes.string.isRequired,
|
||||
/**
|
||||
* Validation error message
|
||||
*/
|
||||
error: PropTypes.string,
|
||||
/**
|
||||
* Indicates wether or not the user touched the field already.
|
||||
*/
|
||||
touched: PropTypes.bool,
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from "./TextField";
|
|
@ -0,0 +1,88 @@
|
|||
import PropTypes from "prop-types";
|
||||
import { Formik, Form } from "formik";
|
||||
import { Link } from "gatsby";
|
||||
import * as Yup from "yup";
|
||||
|
||||
import HighlightedLink from "../HighlightedLink";
|
||||
import { TextField } from "../Form/TextField";
|
||||
import { Button } from "../Button";
|
||||
|
||||
import accountsService from "../../services/accountsService";
|
||||
|
||||
const loginSchema = Yup.object().shape({
|
||||
email: Yup.string().required("Email is required").email("Please provide a valid email address"),
|
||||
password: Yup.string().required("Password is required"),
|
||||
});
|
||||
|
||||
const INVALID_CREDENTIALS_ERRORS = ["password mismatch", "user not found"];
|
||||
|
||||
export const LoginForm = ({ onSuccess }) => (
|
||||
<Formik
|
||||
initialValues={{
|
||||
email: "",
|
||||
password: "",
|
||||
}}
|
||||
validationSchema={loginSchema}
|
||||
onSubmit={async (values, { setErrors }) => {
|
||||
try {
|
||||
await accountsService.post("login", {
|
||||
json: values,
|
||||
});
|
||||
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
if (err.response) {
|
||||
const data = await err.response.json();
|
||||
|
||||
if (INVALID_CREDENTIALS_ERRORS.includes(data.message)) {
|
||||
setErrors({
|
||||
email: "Invalid e-mail address or password",
|
||||
password: "Invalid e-mail address or password",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({ errors, touched }) => (
|
||||
<Form className="flex flex-col gap-4">
|
||||
<h3 className="mt-4 mb-8">Log in to your account</h3>
|
||||
<TextField
|
||||
type="text"
|
||||
id="email"
|
||||
name="email"
|
||||
label="Email address"
|
||||
error={errors.email}
|
||||
touched={touched.email}
|
||||
/>
|
||||
<TextField
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
label="Password"
|
||||
error={errors.password}
|
||||
touched={touched.password}
|
||||
/>
|
||||
<div>
|
||||
<Link to="/auth/recover" className="text-sm inline transition-colors hover:text-primary">
|
||||
Forgot your password?
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-center mt-4">
|
||||
<Button type="submit" className="px-12" $primary>
|
||||
Log in
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-center mt-8">
|
||||
Don't have an account? <HighlightedLink to="/auth/signup">Sign up</HighlightedLink>
|
||||
</p>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
|
||||
LoginForm.propTypes = {
|
||||
onSuccess: PropTypes.func.isRequired,
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from "./LoginForm";
|
|
@ -0,0 +1,3 @@
|
|||
import ky from "ky";
|
||||
|
||||
export default ky.create({ prefixUrl: "/api" });
|
|
@ -3373,6 +3373,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@^4.14.175":
|
||||
version "4.14.180"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670"
|
||||
integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==
|
||||
|
||||
"@types/lodash@^4.14.92":
|
||||
version "4.14.178"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
|
||||
|
@ -6366,6 +6371,11 @@ deep-object-diff@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.7.tgz#348b3246f426427dd633eaa50e1ed1fc2eafc7e4"
|
||||
integrity sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==
|
||||
|
||||
deepmerge@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
|
||||
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
|
||||
|
||||
deepmerge@^4.0.0, deepmerge@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
|
@ -7951,6 +7961,19 @@ format@^0.2.0:
|
|||
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
|
||||
integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
|
||||
|
||||
formik@^2.2.9:
|
||||
version "2.2.9"
|
||||
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0"
|
||||
integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==
|
||||
dependencies:
|
||||
deepmerge "^2.1.1"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
lodash "^4.17.21"
|
||||
lodash-es "^4.17.21"
|
||||
react-fast-compare "^2.0.1"
|
||||
tiny-warning "^1.0.2"
|
||||
tslib "^1.10.0"
|
||||
|
||||
forwarded@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
|
@ -10357,6 +10380,11 @@ klona@^2.0.4:
|
|||
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
|
||||
integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
|
||||
|
||||
ky@^0.30.0:
|
||||
version "0.30.0"
|
||||
resolved "https://registry.yarnpkg.com/ky/-/ky-0.30.0.tgz#a3d293e4f6c4604a9a4694eceb6ce30e73d27d64"
|
||||
integrity sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==
|
||||
|
||||
language-subtag-registry@~0.3.2:
|
||||
version "0.3.21"
|
||||
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
|
||||
|
@ -10510,6 +10538,11 @@ lock@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/lock/-/lock-1.1.0.tgz#53157499d1653b136ca66451071fca615703fa55"
|
||||
integrity sha1-UxV0mdFlOxNspmRRBx/KYVcD+lU=
|
||||
|
||||
lodash-es@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
|
||||
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
||||
|
||||
lodash.clonedeep@4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
|
@ -11183,6 +11216,11 @@ nano-css@^5.3.1:
|
|||
stacktrace-js "^2.0.2"
|
||||
stylis "^4.0.6"
|
||||
|
||||
nanoclone@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
|
||||
integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
|
||||
|
||||
nanoid@^3.1.23, nanoid@^3.2.0, nanoid@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
|
||||
|
@ -12647,6 +12685,11 @@ proper-lockfile@^4.1.2:
|
|||
retry "^0.12.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
property-expr@^2.0.4:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4"
|
||||
integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==
|
||||
|
||||
property-information@^5.0.0, property-information@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
|
||||
|
@ -12942,6 +12985,11 @@ react-error-overlay@^6.0.9:
|
|||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6"
|
||||
integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==
|
||||
|
||||
react-fast-compare@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
||||
|
||||
react-fast-compare@^3.0.1, react-fast-compare@^3.1.1, react-fast-compare@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
||||
|
@ -14830,6 +14878,11 @@ timsort@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tiny-warning@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
tinycolor2@^1.4.1:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
||||
|
@ -14929,6 +14982,11 @@ token-types@^4.1.1:
|
|||
"@tokenizer/token" "^0.3.0"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
toposort@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
|
||||
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
|
@ -16036,6 +16094,19 @@ yoga-layout-prebuilt@^1.10.0:
|
|||
dependencies:
|
||||
"@types/yoga-layout" "1.9.2"
|
||||
|
||||
yup@^0.32.11:
|
||||
version "0.32.11"
|
||||
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"
|
||||
integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.15.4"
|
||||
"@types/lodash" "^4.14.175"
|
||||
lodash "^4.17.21"
|
||||
lodash-es "^4.17.21"
|
||||
nanoclone "^0.2.1"
|
||||
property-expr "^2.0.4"
|
||||
toposort "^2.0.2"
|
||||
|
||||
yurnalist@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yurnalist/-/yurnalist-2.1.0.tgz#44cf7ea5a33a8fab4968cc8c2970489f93760902"
|
||||
|
|
Reference in New Issue