Add sign up funtion, confirm email layout
This commit is contained in:
parent
769ea33a46
commit
52c9f9a3b1
1
.env
Normal file
1
.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
NEXT_PUBLIC_BASE_API=https://api.procurement.fishrungames.com/api
|
37
package-lock.json
generated
37
package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.52.2",
|
"react-hook-form": "^7.52.2",
|
||||||
|
"sass": "^1.77.8",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
@ -764,7 +765,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@ -1053,7 +1053,6 @@
|
|||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
@ -1075,7 +1074,6 @@
|
|||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.1.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
@ -1201,7 +1199,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
@ -1225,7 +1222,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
},
|
},
|
||||||
@ -2231,7 +2227,6 @@
|
|||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
},
|
},
|
||||||
@ -2354,7 +2349,6 @@
|
|||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@ -2670,6 +2664,11 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw=="
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@ -2800,7 +2799,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
},
|
},
|
||||||
@ -2885,7 +2883,6 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -2930,7 +2927,6 @@
|
|||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
},
|
},
|
||||||
@ -2966,7 +2962,6 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
@ -3589,7 +3584,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -3919,7 +3913,6 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
@ -4243,7 +4236,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
},
|
},
|
||||||
@ -4435,6 +4427,22 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sass": {
|
||||||
|
"version": "1.77.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz",
|
||||||
|
"integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
"immutable": "^4.0.0",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sass": "sass.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.23.2",
|
"version": "0.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||||
@ -4935,7 +4943,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.52.2",
|
"react-hook-form": "^7.52.2",
|
||||||
|
"sass": "^1.77.8",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
14
src/app/[locale]/sign-up/confirm-email/icons/mail.svg
Normal file
14
src/app/[locale]/sign-up/confirm-email/icons/mail.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="24.000000" height="24.000000" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<desc>
|
||||||
|
Created with Pixso.
|
||||||
|
</desc>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip55_8430">
|
||||||
|
<rect id="mail" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<rect id="mail" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||||
|
<g clip-path="url(#clip55_8430)">
|
||||||
|
<path id="Icon" d="M20 4C21.0996 4 22 4.90039 22 6L22 18C22 19.0996 21.0996 20 20 20L4 20C2.90039 20 2 19.0996 2 18L2 6C2 4.90039 2.90039 4 4 4L20 4ZM22 6L12 13L2 6" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 739 B |
34
src/app/[locale]/sign-up/confirm-email/page.tsx
Normal file
34
src/app/[locale]/sign-up/confirm-email/page.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import mail from "./icons/mail.svg";
|
||||||
|
import ConfirmEmailForm from "@/widgets/forms/ConfirmEmailForm/ConfirmEmailForm";
|
||||||
|
|
||||||
|
const ConfirmEmail = ({
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
searchParams: {
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="py-[40px] px-[90px]">
|
||||||
|
<div className="h-full min-h-[800px] w-full flex justify-center">
|
||||||
|
<div className="flex items-center flex-col gap-6">
|
||||||
|
<div className="min-w-[60px] min-h-[60px] flex items-center justify-center rounded-full border-2 border-blue">
|
||||||
|
<Image src={mail} alt="Mail icon" width={36} height={36} />
|
||||||
|
</div>
|
||||||
|
<div className="mb-2 flex flex-col items-center gap-2 text-center">
|
||||||
|
<h2 className="text-6 font-bold leading-8 text-gray-900">
|
||||||
|
Проверьте свою почту
|
||||||
|
</h2>
|
||||||
|
<p className="leading-6 text-gray-500">
|
||||||
|
Мы отправили код на почту {searchParams.email}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ConfirmEmailForm email={searchParams.email} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmEmail;
|
@ -41,11 +41,11 @@ const AuthInput: React.FC<IAuthInputProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{error ? (
|
{error && (
|
||||||
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||||||
{error} <Image src={alert} alt="Alert Icon" />
|
{error} <Image src={alert} alt="Alert Icon" />
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
140
src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.module.scss
Normal file
140
src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.module.scss
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
.confirmEmailForm {
|
||||||
|
width: 360px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__inputs {
|
||||||
|
max-width: 360px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
label {
|
||||||
|
align-self: flex-start;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.confirmEmailForm__inputsWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
max-width: 55px;
|
||||||
|
height: 64px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid rgb(208, 213, 221);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 60px;
|
||||||
|
color: rgb(54, 149, 216);
|
||||||
|
|
||||||
|
::placeholder {
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 60px;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border: 1px solid rgb(54, 149, 216);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#confirmEmailForm__inputActive {
|
||||||
|
border: 1px solid rgb(54, 149, 216);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 18px;
|
||||||
|
height: 44px;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 24px;
|
||||||
|
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-email-form__send-code,
|
||||||
|
#confirm-email-form__send-code_active {
|
||||||
|
background-color: rgb(158, 167, 175);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-email-form__send-code_active {
|
||||||
|
background-color: rgb(54, 149, 216);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-email-form__resend-code,
|
||||||
|
#confirm-email-form__resend-code_active {
|
||||||
|
border: 1px solid rgb(158, 167, 175);
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
color: rgb(158, 167, 175);
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirm-email-form__resend-code_active {
|
||||||
|
background-color: rgb(54, 149, 216);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__error {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__timer {
|
||||||
|
text-align: center;
|
||||||
|
margin: 32px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
color: rgb(102, 112, 133);
|
||||||
|
span {
|
||||||
|
color: rgb(54, 149, 216);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.confirm-email-form {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__inputs {
|
||||||
|
&-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.tsx
Normal file
147
src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.tsx
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
|
import { useRouter } from "@/shared/config/navigation";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
import Loader from "@/shared/ui/Loader/Loader";
|
||||||
|
import s from "./ConfirmEmailForm.module.scss";
|
||||||
|
|
||||||
|
interface IConfirmEmailFormProps {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfirmEmailForm: React.FC<IConfirmEmailFormProps> = ({
|
||||||
|
email,
|
||||||
|
}: IConfirmEmailFormProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const [otp, setOtp] = useState(new Array(6).fill(""));
|
||||||
|
const [seconds, setSeconds] = useState<number>(0);
|
||||||
|
const [minutes, setMinutes] = useState<number>(1);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
const [loader, setLoader] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleChange = (e: any, index: number) => {
|
||||||
|
if (isNaN(+e.target.value)) return false;
|
||||||
|
|
||||||
|
setOtp([...otp.map((data, i) => (i === index ? e.target.value : data))]);
|
||||||
|
|
||||||
|
if (e.target.value && e.target.nextSibling) {
|
||||||
|
e.target.nextSibling.focus();
|
||||||
|
} else if (!e.target.value && e.target.previousSibling) {
|
||||||
|
e.target.previousSibling.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit: React.MouseEventHandler<HTMLFormElement> = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const code = {
|
||||||
|
code: +otp.join(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoader(true);
|
||||||
|
|
||||||
|
const response = await apiInstance.post("/users/confirm/", code);
|
||||||
|
|
||||||
|
if (response.status === 200 || response.status === 201) {
|
||||||
|
router.push("/sign-in");
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
if ([400, 404].includes(error.response?.status as number)) {
|
||||||
|
setError("Неверный код подтверждения");
|
||||||
|
} else {
|
||||||
|
setError("Ошибка на стороне сервера");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("Произошла непредвиденная ошибка");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
email,
|
||||||
|
};
|
||||||
|
const response = await apiInstance.post("/users/resend_code/", data);
|
||||||
|
|
||||||
|
if ([200, 201].includes(response.status)) {
|
||||||
|
setMinutes(1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(
|
||||||
|
"Проблема на стороне сервера или вы достигли максимальное количество попыток"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (minutes === 0 && seconds === 0) return;
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
if (seconds === 0) {
|
||||||
|
setMinutes((prev) => Math.max(0, prev - 1));
|
||||||
|
setSeconds(59);
|
||||||
|
} else {
|
||||||
|
setSeconds((prev) => prev - 1);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, [minutes, seconds]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} className={s.confirmEmailForm}>
|
||||||
|
<div className={s.confirmEmailForm__inputs}>
|
||||||
|
<label>Код подтверждения</label>
|
||||||
|
<div className={s.confirmEmailForm__inputsWrapper}>
|
||||||
|
{otp.map((data, index) => (
|
||||||
|
<input
|
||||||
|
id={data ? s.confirmEmailForm__inputActive : ""}
|
||||||
|
key={index}
|
||||||
|
onChange={(e) => handleChange(e, index)}
|
||||||
|
value={data}
|
||||||
|
maxLength={1}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{error ? <p className={s.confirmEmailForm__error}>{error}</p> : null}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
disabled={otp.join("").length === 6 ? false : true}
|
||||||
|
id={`${s.confirmEmailForm__sendCode}${
|
||||||
|
otp.join("").length === 6 ? "__active" : ""
|
||||||
|
}`}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{loader ? <Loader /> : "Подтвердить"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className={s.confirmEmailForm__timer}>
|
||||||
|
Отправить код повторно через{" "}
|
||||||
|
<span>
|
||||||
|
0{minutes}:{seconds.toString().length === 1 ? `0${seconds}` : seconds}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleClick}
|
||||||
|
disabled={minutes === 0 && seconds === 0 ? false : true}
|
||||||
|
id={`${s.confirmEmailForm__resendCode}${
|
||||||
|
minutes === 0 && seconds === 0 ? "__active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{loader ? <Loader /> : "Отправить код повторно"}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmEmailForm;
|
@ -1,110 +1,66 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import AuthInput from "@/features/AuthInput";
|
|
||||||
import Loader from "@/shared/ui/Loader/Loader";
|
import Loader from "@/shared/ui/Loader/Loader";
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { Link, useRouter } from "@/shared/config/navigation";
|
import { z } from "zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React from "react";
|
||||||
|
import alert from "./icons/alert-circle.svg";
|
||||||
|
import Image from "next/image";
|
||||||
|
import eye_off from "./icons/eye-off.svg";
|
||||||
|
import eye_on from "./icons/eye-on.svg";
|
||||||
|
|
||||||
const SignUpForm = () => {
|
const SignUpForm = () => {
|
||||||
const [checkbox, setCheckbox] = useState<boolean>(false);
|
const [resError, setResError] = React.useState<string>("");
|
||||||
const [emailWarning, setEmailWarning] = useState<string>("");
|
const [loader, setLoader] = React.useState<boolean>(false);
|
||||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
const [showPassword, setShowPassword] = React.useState(false);
|
||||||
const [passwordConfirmWarning, setPasswordConfirmWarning] =
|
const [showPasswordTwo, setShowPasswordTwo] = React.useState(false);
|
||||||
useState<string>("");
|
|
||||||
const [error, setError] = useState<string>("");
|
|
||||||
const [loader, setLoader] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const handleSubmit: React.MouseEventHandler<HTMLFormElement> = async (e) => {
|
const signUpFormScheme = z
|
||||||
e.preventDefault();
|
.object({
|
||||||
const formData = new FormData(e.currentTarget);
|
email: z.string().email("Неверный формат email"),
|
||||||
const regex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/;
|
password: z.string().min(8, "Пароль должен содержать минимум 8 символов"),
|
||||||
|
password_repeat: z
|
||||||
|
.string()
|
||||||
|
.min(8, "Пароль должен содержать минимум 8 символов"),
|
||||||
|
})
|
||||||
|
.refine((data) => data.password === data.password_repeat, {
|
||||||
|
message: "Пароли не совпадают",
|
||||||
|
path: ["password_repeat"],
|
||||||
|
});
|
||||||
|
|
||||||
if (!formData.get("email")?.toString()) {
|
type FormFields = z.infer<typeof signUpFormScheme>;
|
||||||
setError("");
|
|
||||||
setPasswordWarning("");
|
|
||||||
setPasswordConfirmWarning("");
|
|
||||||
setEmailWarning("Заполните поле Email");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.get("password")?.toString()) {
|
const {
|
||||||
setError("");
|
register,
|
||||||
setEmailWarning("");
|
handleSubmit,
|
||||||
setPasswordConfirmWarning("");
|
formState: { errors, isSubmitting },
|
||||||
setPasswordWarning("Заполните поле Пароль");
|
} = useForm<FormFields>({
|
||||||
return;
|
resolver: zodResolver(signUpFormScheme),
|
||||||
}
|
});
|
||||||
|
|
||||||
if ((formData.get("password")?.toString().length as number) < 8) {
|
|
||||||
setError("");
|
|
||||||
setEmailWarning("");
|
|
||||||
setPasswordConfirmWarning("");
|
|
||||||
setPasswordWarning("Пароль должен содержать минимум 8 символов");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!regex.test(formData.get("password")?.toString() as string)) {
|
|
||||||
setError("");
|
|
||||||
setEmailWarning("");
|
|
||||||
setPasswordConfirmWarning("");
|
|
||||||
setPasswordWarning(
|
|
||||||
"Пароль должен содержать по меньшей мере 1 прописную букву, одну заглавную букву и одну цифру"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.get("password2")?.toString()) {
|
|
||||||
setError("");
|
|
||||||
setEmailWarning("");
|
|
||||||
setPasswordWarning("");
|
|
||||||
setPasswordConfirmWarning("Заполните поле потверждения");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
formData.get("password")?.toString() !==
|
|
||||||
formData.get("password2")?.toString()
|
|
||||||
) {
|
|
||||||
setError("");
|
|
||||||
setEmailWarning("");
|
|
||||||
setPasswordWarning("");
|
|
||||||
setPasswordConfirmWarning("Пароли не совпадают");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkbox) {
|
|
||||||
setEmailWarning("");
|
|
||||||
setPasswordWarning("");
|
|
||||||
setPasswordConfirmWarning("");
|
|
||||||
setError("Необходимо принять политику конфиденциальности");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormFields) => {
|
||||||
try {
|
try {
|
||||||
setError("");
|
const res = await apiInstance.post("/auth/register/", data);
|
||||||
setEmailWarning("");
|
const encodedEmail = encodeURIComponent(data.email);
|
||||||
setPasswordWarning("");
|
console.log(encodedEmail);
|
||||||
setPasswordConfirmWarning("");
|
|
||||||
setLoader(true);
|
|
||||||
|
|
||||||
const res = await apiInstance.post("/users/register/", formData);
|
|
||||||
|
|
||||||
if ([200, 201].includes(res.status)) {
|
if ([200, 201].includes(res.status)) {
|
||||||
router.push(`/sign-up/confirm-email/?email=${formData.get("email")}`);
|
router.push(`sign-up/confirm-email?email=${encodedEmail}`);
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
if ([401, 400].includes(error.response?.status as number)) {
|
if ([401, 400].includes(error.response?.status as number)) {
|
||||||
setError("Такой пользователь уже существует");
|
setResError("Такой пользователь уже существует");
|
||||||
} else if (error.response?.status.toString().slice(0, 1) === "5") {
|
} else if (error.response?.status.toString().slice(0, 1) === "5") {
|
||||||
setError("Ошибка на стороне сервера");
|
setResError("Ошибка на стороне сервера");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setError("Непредвиденная ошибка");
|
setResError("Непредвиденная ошибка");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
@ -112,31 +68,95 @@ const SignUpForm = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="mb-2 w-[360px] flex flex-col" onSubmit={handleSubmit}>
|
<form
|
||||||
|
className="mb-2 w-[360px] flex flex-col"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
>
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
<AuthInput
|
<div>
|
||||||
type="email"
|
<label className="text-[14px] leading-5 text-gray-700">Email</label>
|
||||||
label="Email"
|
<div
|
||||||
placeholder="Введите email"
|
className={`flex items-center${
|
||||||
error={emailWarning}
|
errors.email?.message && "border border-red-400"
|
||||||
name="email"
|
}`}
|
||||||
/>
|
>
|
||||||
<AuthInput
|
<input
|
||||||
isPassword
|
placeholder="Email"
|
||||||
label="Пароль"
|
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px] border border-gray-300 rounded-lg shadow-sm bg-white"
|
||||||
placeholder="Введите пароль"
|
type="text"
|
||||||
error={passwordWarning}
|
{...register("email", { required: true })}
|
||||||
name="password"
|
|
||||||
/>
|
|
||||||
<AuthInput
|
|
||||||
isPassword
|
|
||||||
label="Пароль потверждения"
|
|
||||||
placeholder="Повторите пароль"
|
|
||||||
error={passwordConfirmWarning}
|
|
||||||
name="password2"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{error ? <p className="text-sm leading-5 text-red-500">{error}</p> : null}
|
{errors?.email?.message && (
|
||||||
|
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||||||
|
{errors.email.message} <Image src={alert} alt="Alert Icon" />
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-[14px] leading-5 text-gray-700">Пароль</label>
|
||||||
|
<div
|
||||||
|
className={`flex items-center border border-gray-300 rounded-lg shadow-sm bg-white${
|
||||||
|
errors?.password?.message && "border border-red-400"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
placeholder="Пароль"
|
||||||
|
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px]"
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
{...register("password", { required: true })}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowPassword((prev) => !prev)}
|
||||||
|
type="button"
|
||||||
|
className="pr-2"
|
||||||
|
>
|
||||||
|
<Image src={showPassword ? eye_on : eye_off} alt="Eye Icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{errors?.password?.message && (
|
||||||
|
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||||||
|
{errors.password.message} <Image src={alert} alt="Alert Icon" />
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="text-[14px] leading-5 text-gray-700">
|
||||||
|
Пароль потверждения
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className={`flex items-center border border-gray-300 rounded-lg shadow-sm bg-white${
|
||||||
|
errors?.password_repeat?.message && "border border-red-400"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
placeholder="Пароль потверждения"
|
||||||
|
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px] "
|
||||||
|
type={showPasswordTwo ? "text" : "password"}
|
||||||
|
{...register("password_repeat", { required: true })}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowPasswordTwo((prev) => !prev)}
|
||||||
|
type="button"
|
||||||
|
className="pr-2"
|
||||||
|
>
|
||||||
|
<Image src={showPasswordTwo ? eye_on : eye_off} alt="Eye Icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{errors?.password_repeat?.message && (
|
||||||
|
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||||||
|
{errors.password_repeat.message}{" "}
|
||||||
|
<Image src={alert} alt="Alert Icon" />
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{errors.root && (
|
||||||
|
<p className="text-sm leading-5 text-red-500">{errors.root.message}</p>
|
||||||
|
)}
|
||||||
|
{resError && <p className="text-sm leading-5 text-red-500">{resError}</p>}
|
||||||
|
|
||||||
<div className="flex flex-col mt-[36px] gap-4">
|
<div className="flex flex-col mt-[36px] gap-4">
|
||||||
<button
|
<button
|
||||||
|
7
src/widgets/forms/icons/alert-circle.svg
Normal file
7
src/widgets/forms/icons/alert-circle.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<svg width="14.666992" height="14.666992" viewBox="0 0 14.667 14.667" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<desc>
|
||||||
|
Created with Pixso.
|
||||||
|
</desc>
|
||||||
|
<defs/>
|
||||||
|
<path id="Icon" d="M7.33398 14C3.65137 14 0.666992 11.0156 0.666992 7.33398C0.666992 3.65137 3.65137 0.666992 7.33398 0.666992C11.0156 0.666992 14 3.65137 14 7.33398C14 11.0156 11.0156 14 7.33398 14ZM7.33398 4.66699L7.33398 7.33398M7.33398 10L7.33984 10" stroke="#F04438" stroke-opacity="1.000000" stroke-width="1.333333" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 566 B |
15
src/widgets/forms/icons/eye-on.svg
Normal file
15
src/widgets/forms/icons/eye-on.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg width="24.000000" height="24.000000" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<desc>
|
||||||
|
Created with Pixso.
|
||||||
|
</desc>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip1668_50161">
|
||||||
|
<rect id="eye" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<rect id="eye" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||||
|
<g clip-path="url(#clip1668_50161)">
|
||||||
|
<path id="Vector" d="M12 4C19 4 23 12 23 12C23 12 19 20 12 20C5 20 1 12 1 12C1 12 5 4 12 4Z" stroke="#979797" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||||
|
<path id="Vector" d="M12 15C10.3428 15 9 13.6572 9 12C9 10.3428 10.3428 9 12 9C13.6572 9 15 10.3428 15 12C15 13.6572 13.6572 15 12 15Z" stroke="#979797" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 901 B |
Loading…
Reference in New Issue
Block a user