renamed folders
No files matched your search
@@ -0,0 +1,145 @@
|
||||
@import "@/shared/ui/variables.scss";
|
||||
|
||||
.confirm-email-form {
|
||||
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-700;
|
||||
}
|
||||
|
||||
&-wrapper {
|
||||
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-300;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#confirm-email-form__input-active {
|
||||
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-500;
|
||||
}
|
||||
|
||||
&__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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import "./ConfirmEmailForm.scss";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
|
||||
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) {
|
||||
setError("Возникла ошибка");
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = async () => {
|
||||
const data = {
|
||||
email,
|
||||
};
|
||||
const response = await apiInstance.post(
|
||||
"/users/resend_code/",
|
||||
data
|
||||
);
|
||||
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
setMinutes(1);
|
||||
}
|
||||
};
|
||||
|
||||
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="confirm-email-form">
|
||||
<div className="confirm-email-form__inputs">
|
||||
<label>Код подтверждения</label>
|
||||
<div className="confirm-email-form__inputs-wrapper">
|
||||
{otp.map((data, index) => (
|
||||
<input
|
||||
id={data ? "confirm-email-form__input-active" : ""}
|
||||
key={index}
|
||||
onChange={(e) => handleChange(e, index)}
|
||||
value={data}
|
||||
maxLength={1}
|
||||
type="text"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{error ? (
|
||||
<p className="confirm-email-form__error">{error}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
disabled={otp.join("").length === 6 ? false : true}
|
||||
id={`confirm-email-form__send-code${
|
||||
otp.join("").length === 6 ? "_active" : ""
|
||||
}`}
|
||||
type="submit"
|
||||
>
|
||||
{loader ? <Loader /> : "Подтвердить"}
|
||||
</button>
|
||||
|
||||
<p className="confirm-email-form__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={`confirm-email-form__resend-code${
|
||||
minutes === 0 && seconds === 0 ? "_active" : ""
|
||||
}`}
|
||||
>
|
||||
{loader ? <Loader /> : "Отправить код повторно"}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmEmailForm;
|
||||
@@ -0,0 +1,80 @@
|
||||
.footer {
|
||||
padding: 48px 80px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
gap: 30px;
|
||||
background-color: rgb(15, 23, 42);
|
||||
a,
|
||||
li,
|
||||
h4 {
|
||||
height: fit-content;
|
||||
color: white;
|
||||
justify-content: flex-start;
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-weight: 500;
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
a,
|
||||
li {
|
||||
color: rgb(226, 232, 240);
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__links,
|
||||
&__contacts,
|
||||
&__apps,
|
||||
&__apps-btns,
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.footer {
|
||||
padding: 48px 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.footer {
|
||||
padding: 48px 30px;
|
||||
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
|
||||
&__apps {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.footer {
|
||||
padding: 48px 16px;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 60px;
|
||||
|
||||
&__apps,
|
||||
&__contacts,
|
||||
&__links,
|
||||
&__apps-btns,
|
||||
ul,
|
||||
li,
|
||||
a {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import "./Footer.scss";
|
||||
import logo from "../../shared/assets/logo.svg";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { LINKS } from "@/shared/variables/links";
|
||||
import youtube from "./icons/youtube.svg";
|
||||
import facebook from "./icons/facebook.svg";
|
||||
import instagram from "./icons/instagram.svg";
|
||||
import app_store_btn from "./icons/app-store-btn.svg";
|
||||
import play_market_btn from "./icons/play-market-btn.svg";
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<footer className="footer">
|
||||
<Link href="/">
|
||||
<Image src={logo} alt="Logo" />
|
||||
</Link>
|
||||
<div className="footer__links">
|
||||
<h4>Навигация</h4>
|
||||
<ul>
|
||||
{LINKS.map((link) => (
|
||||
<li key={link.id}>
|
||||
<Link href={link.pathname} key={link.id}>
|
||||
{link.pagename}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="footer__contacts">
|
||||
<h4>Контакты</h4>
|
||||
<ul>
|
||||
<li>namename@gmail.com</li>
|
||||
<li>+09646895467</li>
|
||||
<li>
|
||||
{[youtube, facebook, instagram].map((net, i) => (
|
||||
<Link key={i} href="#">
|
||||
<Image src={net} alt="Net Icon" key={net} />
|
||||
</Link>
|
||||
))}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="footer__apps">
|
||||
<h4>Скачивай наше приложение</h4>
|
||||
<div className="footer__apps-btns">
|
||||
{[app_store_btn, play_market_btn].map((app, i) => (
|
||||
<Link key={i} href="#">
|
||||
<Image src={app} alt="App Button" key={app} />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -0,0 +1,31 @@
|
||||
<svg width="119.664062" height="40.000000" viewBox="0 0 119.664 40" 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="Vector" d="M110.135 0L9.53467 0C9.16797 0 8.80566 0 8.43994 0.00195312C8.13379 0.00390625 7.83008 0.00976562 7.521 0.0146484C6.84961 0.0224609 6.17969 0.0820312 5.51709 0.191406C4.85547 0.303711 4.21484 0.514648 3.61621 0.818359C3.01855 1.125 2.47217 1.52246 1.99756 1.99707C1.52051 2.4707 1.12256 3.01855 0.819336 3.61816C0.515625 4.2168 0.304688 4.85938 0.194336 5.52148C0.0830078 6.18359 0.0229492 6.85254 0.0151367 7.52344C0.00585938 7.83008 0.00488281 8.1377 0 8.44434L0 31.5586C0.00488281 31.8691 0.00585938 32.1699 0.0151367 32.4805C0.0229492 33.1514 0.0830078 33.8213 0.194336 34.4824C0.304199 35.1455 0.515137 35.7881 0.819336 36.3867C1.12256 36.9854 1.52002 37.5303 1.99756 38.001C2.4707 38.4785 3.01709 38.876 3.61621 39.1797C4.21484 39.4844 4.85547 39.6973 5.51709 39.8105C6.17969 39.9199 6.84961 39.9785 7.521 39.9873C7.83008 39.9941 8.13379 39.998 8.43994 39.998C8.80566 40 9.16797 40 9.53467 40L110.135 40C110.494 40 110.859 40 111.219 39.998C111.523 39.998 111.836 39.9941 112.141 39.9873C112.811 39.9795 113.479 39.9199 114.141 39.8105C114.805 39.6963 115.448 39.4834 116.049 39.1797C116.647 38.876 117.193 38.4775 117.666 38.001C118.142 37.5283 118.541 36.9834 118.848 36.3867C119.149 35.7871 119.358 35.1455 119.467 34.4824C119.578 33.8203 119.64 33.1514 119.652 32.4805C119.656 32.1699 119.656 31.8691 119.656 31.5586C119.664 31.1953 119.664 30.834 119.664 30.4648L119.664 9.53613C119.664 9.16992 119.664 8.80664 119.656 8.44434C119.656 8.1377 119.656 7.83008 119.652 7.52344C119.64 6.85254 119.578 6.18359 119.467 5.52148C119.358 4.85938 119.149 4.21777 118.848 3.61816C118.23 2.41504 117.251 1.43652 116.049 0.818359C115.448 0.515625 114.804 0.304688 114.141 0.191406C113.479 0.0810547 112.811 0.0224609 112.141 0.0146484C111.836 0.00976562 111.523 0.00390625 111.219 0.00195312C110.859 0 110.494 0 110.135 0L110.135 0Z" fill="#A6A6A6" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M8.44482 39.125C8.14014 39.125 7.84277 39.1211 7.54053 39.1143C6.91455 39.1064 6.28955 39.0518 5.67139 38.9512C5.09521 38.8516 4.53662 38.667 4.01465 38.4033C3.49756 38.1416 3.02588 37.7979 2.61768 37.3867C2.20361 36.9795 1.85889 36.5078 1.59717 35.9902C1.33252 35.4688 1.14941 34.9102 1.0542 34.333C0.951172 33.7129 0.895508 33.0859 0.887695 32.458C0.881348 32.2471 0.873047 31.5449 0.873047 31.5449L0.873047 8.44434C0.873047 8.44434 0.881836 7.75293 0.887695 7.5498C0.895508 6.92285 0.950684 6.29688 1.05322 5.67773C1.14893 5.09961 1.33203 4.53906 1.59668 4.01562C1.85742 3.49805 2.2002 3.02539 2.61182 2.61816C3.02295 2.20605 3.49609 1.86035 4.01416 1.59473C4.53516 1.33203 5.09229 1.14844 5.66748 1.05176C6.2876 0.950195 6.91455 0.894531 7.54297 0.887695L8.44531 0.875L111.214 0.875L112.127 0.887695C112.75 0.895508 113.371 0.949219 113.985 1.0498C114.566 1.14941 115.129 1.33398 115.656 1.59766C116.694 2.13281 117.539 2.97949 118.071 4.01855C118.332 4.53711 118.512 5.09375 118.606 5.66699C118.71 6.29102 118.769 6.92188 118.78 7.55469C118.783 7.83691 118.783 8.1416 118.783 8.44434C118.791 8.81934 118.791 9.17578 118.791 9.53613L118.791 30.4648C118.791 30.8281 118.791 31.1826 118.783 31.54C118.783 31.8652 118.783 32.1631 118.779 32.4697C118.768 33.0908 118.71 33.71 118.608 34.3232C118.515 34.9043 118.333 35.4678 118.068 35.9932C117.805 36.5059 117.462 36.9736 117.053 37.3789C116.645 37.793 116.172 38.1377 115.653 38.4014C115.128 38.667 114.566 38.8525 113.985 38.9512C113.367 39.0518 112.743 39.1064 112.116 39.1143C111.823 39.1211 111.517 39.125 111.219 39.125L110.135 39.127L8.44482 39.125Z" fill="#000000" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="<Path>" d="M24.769 20.3018C24.7798 19.4668 25.0015 18.6484 25.4136 17.9219C25.8257 17.1963 26.4146 16.5859 27.1255 16.1494C26.6738 15.5039 26.0776 14.9727 25.3853 14.5986C24.6924 14.2236 23.9214 14.0166 23.1343 13.9912C21.4551 13.8154 19.8271 14.9961 18.9717 14.9961C18.0991 14.9961 16.7817 14.0088 15.3628 14.0381C14.4453 14.0684 13.5508 14.335 12.7671 14.8125C11.9834 15.291 11.3364 15.9639 10.8901 16.7666C8.95605 20.1143 10.3984 25.0352 12.2515 27.7422C13.1782 29.0674 14.2612 30.5479 15.6787 30.4951C17.0659 30.4375 17.584 29.6113 19.2583 29.6113C20.917 29.6113 21.4033 30.4951 22.8491 30.4619C24.3379 30.4375 25.2754 29.1309 26.1699 27.793C26.8359 26.8486 27.3481 25.8047 27.6885 24.7002C26.8237 24.335 26.0854 23.7227 25.5664 22.9404C25.0474 22.1582 24.77 21.2402 24.769 20.3018Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="<Path>" d="M22.0376 12.2109C22.8491 11.2373 23.249 9.98535 23.1519 8.7207C21.9121 8.85059 20.7671 9.44336 19.9443 10.3799C19.5425 10.8379 19.2344 11.3701 19.0381 11.9473C18.8418 12.5244 18.7612 13.1338 18.8008 13.7422C19.4209 13.748 20.0347 13.6133 20.5952 13.3486C21.1558 13.084 21.6489 12.6943 22.0376 12.2109Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M42.3022 27.1396L37.5688 27.1396L36.4321 30.4961L34.4272 30.4961L38.9106 18.0781L40.9937 18.0781L45.4771 30.4961L43.438 30.4961L42.3022 27.1396ZM38.0591 25.5908L41.811 25.5908L39.9614 20.1436L39.9097 20.1436L38.0591 25.5908Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M55.1597 25.9697C55.1597 28.7832 53.6538 30.5908 51.3813 30.5908C50.8057 30.6211 50.2334 30.4883 49.7295 30.208C49.2256 29.9277 48.811 29.5117 48.5327 29.0068L48.4897 29.0068L48.4897 33.4912L46.6313 33.4912L46.6313 21.4424L48.4302 21.4424L48.4302 22.9482L48.4644 22.9482C48.7554 22.4453 49.1772 22.0312 49.6846 21.749C50.1924 21.4678 50.7671 21.3291 51.3472 21.3477C53.645 21.3477 55.1597 23.1641 55.1597 25.9697ZM53.2495 25.9697C53.2495 24.1367 52.3022 22.9316 50.8569 22.9316C49.437 22.9316 48.4819 24.1621 48.4819 25.9697C48.4819 27.7939 49.437 29.0156 50.8569 29.0156C52.3022 29.0156 53.2495 27.8193 53.2495 25.9697Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M65.1245 25.9697C65.1245 28.7832 63.6187 30.5908 61.3462 30.5908C60.7705 30.6211 60.1982 30.4883 59.6943 30.208C59.1904 29.9277 58.7759 29.5117 58.4976 29.0068L58.4546 29.0068L58.4546 33.4912L56.5962 33.4912L56.5962 21.4424L58.395 21.4424L58.395 22.9482L58.4292 22.9482C58.7202 22.4453 59.1421 22.0312 59.6494 21.749C60.1572 21.4678 60.7314 21.3291 61.312 21.3477C63.6099 21.3477 65.1245 23.1641 65.1245 25.9697ZM63.2144 25.9697C63.2144 24.1367 62.2671 22.9316 60.8218 22.9316C59.4019 22.9316 58.4468 24.1621 58.4468 25.9697C58.4468 27.7939 59.4019 29.0156 60.8218 29.0156C62.2671 29.0156 63.2144 27.8193 63.2144 25.9697L63.2144 25.9697Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M71.7104 27.0361C71.8481 28.2676 73.0444 29.0762 74.6792 29.0762C76.2456 29.0762 77.3726 28.2676 77.3726 27.1572C77.3726 26.1934 76.6929 25.6162 75.0835 25.2207L73.4741 24.833C71.1938 24.2822 70.1353 23.2158 70.1353 21.4854C70.1353 19.3428 72.0024 17.8711 74.6538 17.8711C77.2778 17.8711 79.0767 19.3428 79.1372 21.4854L77.2612 21.4854C77.1489 20.2461 76.1245 19.498 74.6274 19.498C73.1304 19.498 72.106 20.2549 72.106 21.3564C72.106 22.2344 72.7603 22.751 74.3608 23.1465L75.729 23.4824C78.2769 24.085 79.3354 25.1084 79.3354 26.9248C79.3354 29.248 77.4849 30.7031 74.5415 30.7031C71.7876 30.7031 69.9282 29.2822 69.8081 27.0361L71.7104 27.0361Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M83.3462 19.2998L83.3462 21.4424L85.0679 21.4424L85.0679 22.9141L83.3462 22.9141L83.3462 27.9053C83.3462 28.6807 83.6909 29.042 84.4478 29.042C84.6523 29.0381 84.856 29.0244 85.0591 28.999L85.0591 30.4619C84.7188 30.5254 84.373 30.5547 84.0269 30.5479C82.1938 30.5479 81.479 29.8594 81.479 28.1035L81.479 22.9141L80.1626 22.9141L80.1626 21.4424L81.479 21.4424L81.479 19.2998L83.3462 19.2998Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M86.0649 25.9697C86.0649 23.1211 87.7427 21.3311 90.3589 21.3311C92.9839 21.3311 94.6538 23.1211 94.6538 25.9697C94.6538 28.8262 92.9927 30.6084 90.3589 30.6084C87.7261 30.6084 86.0649 28.8262 86.0649 25.9697ZM92.7603 25.9697C92.7603 24.0156 91.8647 22.8623 90.3589 22.8623C88.853 22.8623 87.9585 24.0244 87.9585 25.9697C87.9585 27.9316 88.853 29.0762 90.3589 29.0762C91.8647 29.0762 92.7603 27.9316 92.7603 25.9697L92.7603 25.9697Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M96.186 21.4424L97.9585 21.4424L97.9585 22.9834L98.0015 22.9834C98.1216 22.502 98.4033 22.0762 98.7998 21.7783C99.1968 21.4805 99.6836 21.3281 100.179 21.3477C100.394 21.3467 100.607 21.3701 100.816 21.417L100.816 23.1553C100.545 23.0723 100.264 23.0342 99.981 23.043C99.7109 23.0312 99.4419 23.0791 99.1919 23.1826C98.9424 23.2852 98.7173 23.4404 98.5337 23.6387C98.3496 23.8369 98.2104 24.0723 98.126 24.3281C98.0415 24.585 98.0137 24.8574 98.0444 25.126L98.0444 30.4961L96.186 30.4961L96.186 21.4424Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M109.384 27.8369C109.134 29.4805 107.534 30.6084 105.486 30.6084C102.852 30.6084 101.217 28.8438 101.217 26.0127C101.217 23.1729 102.861 21.3311 105.408 21.3311C107.913 21.3311 109.488 23.0518 109.488 25.7969L109.488 26.4336L103.093 26.4336L103.093 26.5459C103.064 26.8789 103.105 27.2148 103.216 27.5303C103.326 27.8467 103.502 28.1348 103.732 28.3779C103.963 28.6201 104.242 28.8115 104.552 28.9375C104.861 29.0635 105.194 29.123 105.529 29.1104C105.968 29.1514 106.409 29.0498 106.785 28.8203C107.162 28.5908 107.455 28.2461 107.62 27.8369L109.384 27.8369ZM103.102 25.1348L107.628 25.1348C107.645 24.835 107.6 24.5352 107.495 24.2539C107.39 23.9727 107.229 23.7168 107.02 23.501C106.812 23.2852 106.561 23.1143 106.283 23C106.006 22.8857 105.708 22.8301 105.408 22.8369C105.105 22.835 104.805 22.8936 104.525 23.0078C104.245 23.123 103.99 23.292 103.776 23.5059C103.562 23.7188 103.392 23.9727 103.276 24.2529C103.161 24.5322 103.101 24.832 103.102 25.1348L103.102 25.1348Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M37.8262 8.73047C38.2158 8.70215 38.6069 8.76172 38.9707 8.90234C39.335 9.04395 39.6631 9.26465 39.9316 9.54785C40.2002 9.83203 40.4028 10.1719 40.5239 10.543C40.6455 10.9141 40.6831 11.3076 40.6338 11.6953C40.6338 13.6016 39.6035 14.6973 37.8262 14.6973L35.6709 14.6973L35.6709 8.73047L37.8262 8.73047ZM36.5977 13.8535L37.7227 13.8535C38.001 13.8701 38.2798 13.8242 38.5381 13.7197C38.7969 13.6152 39.0288 13.4541 39.2173 13.249C39.4058 13.043 39.5459 12.7979 39.6279 12.5312C39.7095 12.2646 39.731 11.9834 39.6904 11.707C39.728 11.4326 39.7046 11.1523 39.6216 10.8877C39.5386 10.623 39.3979 10.3809 39.2095 10.1768C39.0215 9.97266 38.7905 9.8125 38.5337 9.70898C38.2764 9.60449 37.9995 9.55859 37.7227 9.57324L36.5977 9.57324L36.5977 13.8535Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M41.6812 12.4434C41.6528 12.1475 41.6865 11.8496 41.7803 11.5674C41.874 11.2852 42.0259 11.0254 42.2261 10.8057C42.4258 10.5859 42.6694 10.4102 42.9414 10.29C43.2134 10.1699 43.5073 10.1074 43.8047 10.1074C44.1016 10.1074 44.3955 10.1699 44.6675 10.29C44.9395 10.4102 45.1831 10.5859 45.3833 10.8057C45.583 11.0254 45.7349 11.2852 45.8286 11.5674C45.9224 11.8496 45.9565 12.1475 45.9282 12.4434C45.957 12.7402 45.9233 13.0391 45.8296 13.3213C45.7363 13.6045 45.5845 13.8643 45.3848 14.084C45.1846 14.3047 44.9409 14.4805 44.6685 14.6016C44.3965 14.7217 44.1021 14.7842 43.8047 14.7842C43.5068 14.7842 43.2124 14.7217 42.9404 14.6016C42.6685 14.4805 42.4243 14.3047 42.2246 14.084C42.0244 13.8643 41.8726 13.6045 41.7793 13.3213C41.6855 13.0391 41.6519 12.7402 41.6812 12.4434ZM45.0142 12.4434C45.0142 11.4678 44.5757 10.8965 43.8062 10.8965C43.0337 10.8965 42.5991 11.4678 42.5991 12.4434C42.5991 13.4277 43.0337 13.9941 43.8062 13.9941C44.5757 13.9941 45.0142 13.4238 45.0142 12.4434L45.0142 12.4434Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M51.5732 14.6973L50.6514 14.6973L49.7207 11.3809L49.6504 11.3809L48.7236 14.6973L47.8105 14.6973L46.5693 10.1943L47.4707 10.1943L48.2773 13.6299L48.3438 13.6299L49.2695 10.1943L50.1221 10.1943L51.0479 13.6299L51.1182 13.6299L51.9209 10.1943L52.8096 10.1943L51.5732 14.6973Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M53.8535 10.1943L54.709 10.1943L54.709 10.9092L54.7754 10.9092C54.8882 10.6523 55.0781 10.4365 55.3188 10.293C55.5596 10.1494 55.8394 10.084 56.1191 10.1074C56.3384 10.0908 56.5581 10.123 56.7627 10.2041C56.9673 10.2842 57.1514 10.4092 57.3013 10.5693C57.4507 10.7305 57.5625 10.9229 57.6274 11.1328C57.6929 11.3428 57.71 11.5645 57.6777 11.7822L57.6777 14.6963L56.7891 14.6963L56.7891 12.0049C56.7891 11.2812 56.4746 10.9219 55.8174 10.9219C55.6685 10.9141 55.52 10.9395 55.3818 10.9961C55.2441 11.0518 55.1196 11.1367 55.0176 11.2451C54.9155 11.3535 54.8379 11.4824 54.7905 11.624C54.7427 11.7646 54.7266 11.9141 54.7422 12.0625L54.7422 14.6973L53.8535 14.6973L53.8535 10.1943Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M59.0938 8.43652L59.9824 8.43652L59.9824 14.6973L59.0938 14.6973L59.0938 8.43652Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M61.2183 12.4443C61.1899 12.1475 61.2236 11.8496 61.3174 11.5674C61.4111 11.2852 61.563 11.0254 61.7632 10.8057C61.9629 10.5859 62.2065 10.4102 62.4785 10.29C62.7505 10.1699 63.0444 10.1074 63.3418 10.1074C63.6392 10.1074 63.9331 10.1699 64.2051 10.29C64.4771 10.4102 64.7207 10.5859 64.9204 10.8057C65.1206 11.0254 65.2725 11.2852 65.3662 11.5674C65.46 11.8496 65.4937 12.1475 65.4658 12.4443C65.4946 12.7402 65.4609 13.0391 65.3672 13.3213C65.2734 13.6045 65.1221 13.8643 64.9219 14.084C64.7222 14.3047 64.478 14.4805 64.2061 14.6016C63.9336 14.7217 63.6396 14.7842 63.3418 14.7842C63.0439 14.7842 62.75 14.7217 62.4775 14.6016C62.2056 14.4805 61.9614 14.3047 61.7617 14.084C61.5615 13.8643 61.4102 13.6045 61.3164 13.3213C61.2227 13.0391 61.1895 12.7402 61.2183 12.4443ZM64.5513 12.4443C64.5513 11.4678 64.1123 10.8975 63.3433 10.8975C62.5708 10.8975 62.1362 11.4678 62.1362 12.4443C62.1362 13.4277 62.5708 13.9941 63.3433 13.9941C64.1128 13.9941 64.5513 13.4238 64.5513 12.4443L64.5513 12.4443Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M66.4009 13.4238C66.4009 12.6133 67.0044 12.1455 68.0757 12.0791L69.2954 12.0088L69.2954 11.6201C69.2954 11.1445 68.981 10.876 68.3735 10.876C67.8774 10.876 67.5337 11.0586 67.4351 11.377L66.5747 11.377C66.6655 10.6035 67.3931 10.1074 68.4146 10.1074C69.5435 10.1074 70.1802 10.6689 70.1802 11.6201L70.1802 14.6973L69.3247 14.6973L69.3247 14.0645L69.2544 14.0645C69.1118 14.291 68.9111 14.4766 68.6738 14.6006C68.436 14.7246 68.1699 14.7842 67.9019 14.7715C67.7129 14.791 67.522 14.7705 67.3408 14.7119C67.1602 14.6533 66.9937 14.5576 66.8525 14.4307C66.7109 14.3037 66.5977 14.1484 66.52 13.9746C66.4424 13.8018 66.4019 13.6143 66.4009 13.4238ZM69.2954 13.0391L69.2954 12.6621L68.1958 12.7324C67.5757 12.7744 67.2944 12.9854 67.2944 13.3818C67.2944 13.7871 67.646 14.0234 68.1294 14.0234C68.271 14.0381 68.4141 14.0234 68.5503 13.9814C68.686 13.9395 68.8125 13.8701 68.9209 13.7783C69.0298 13.6865 69.1191 13.5742 69.1836 13.4463C69.2476 13.3193 69.2856 13.1807 69.2954 13.0391Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M71.3481 12.4443C71.3481 11.0215 72.0796 10.1201 73.2173 10.1201C73.4985 10.1064 73.7778 10.1738 74.0225 10.3145C74.2671 10.4541 74.4668 10.6602 74.5981 10.9092L74.6646 10.9092L74.6646 8.43652L75.5532 8.43652L75.5532 14.6973L74.7017 14.6973L74.7017 13.9854L74.6313 13.9854C74.4897 14.2334 74.2832 14.4375 74.0337 14.5762C73.7847 14.7139 73.5024 14.7822 73.2173 14.7715C72.0718 14.7715 71.3481 13.8701 71.3481 12.4443ZM72.2661 12.4443C72.2661 13.3994 72.7163 13.9736 73.4692 13.9736C74.2183 13.9736 74.6812 13.3906 74.6812 12.4482C74.6812 11.5098 74.2134 10.918 73.4692 10.918C72.7212 10.918 72.2661 11.4971 72.2661 12.4443L72.2661 12.4443Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M79.23 12.4434C79.2017 12.1475 79.2354 11.8496 79.3291 11.5674C79.4229 11.2852 79.5747 11.0254 79.7749 10.8057C79.9746 10.5859 80.2183 10.4102 80.4902 10.29C80.7622 10.1699 81.0562 10.1074 81.3535 10.1074C81.6504 10.1074 81.9443 10.1699 82.2163 10.29C82.4883 10.4102 82.7319 10.5859 82.9321 10.8057C83.1318 11.0254 83.2837 11.2852 83.3774 11.5674C83.4712 11.8496 83.5054 12.1475 83.4771 12.4434C83.5059 12.7402 83.4722 13.0391 83.3784 13.3213C83.2852 13.6045 83.1333 13.8643 82.9336 14.084C82.7334 14.3047 82.4897 14.4805 82.2173 14.6016C81.9453 14.7217 81.6509 14.7842 81.3535 14.7842C81.0557 14.7842 80.7612 14.7217 80.4893 14.6016C80.2173 14.4805 79.9731 14.3047 79.7734 14.084C79.5732 13.8643 79.4214 13.6045 79.3281 13.3213C79.2344 13.0391 79.2007 12.7402 79.23 12.4434ZM82.563 12.4434C82.563 11.4678 82.1245 10.8965 81.355 10.8965C80.5825 10.8965 80.1479 11.4678 80.1479 12.4434C80.1479 13.4277 80.5825 13.9941 81.355 13.9941C82.1245 13.9941 82.563 13.4238 82.563 12.4434Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M84.6694 10.1943L85.5249 10.1943L85.5249 10.9092L85.5913 10.9092C85.7041 10.6523 85.894 10.4365 86.1348 10.293C86.3755 10.1494 86.6553 10.084 86.9351 10.1074C87.1543 10.0908 87.374 10.123 87.5786 10.2041C87.7832 10.2842 87.9673 10.4092 88.1172 10.5693C88.2666 10.7305 88.3784 10.9229 88.4434 11.1328C88.5088 11.3428 88.5259 11.5645 88.4937 11.7822L88.4937 14.6963L87.605 14.6963L87.605 12.0049C87.605 11.2812 87.2905 10.9219 86.6333 10.9219C86.4844 10.9141 86.3359 10.9395 86.1978 10.9961C86.0601 11.0518 85.9355 11.1367 85.8335 11.2451C85.7314 11.3535 85.6538 11.4824 85.6064 11.624C85.5586 11.7646 85.5425 11.9141 85.5581 12.0625L85.5581 14.6973L84.6694 14.6973L84.6694 10.1943Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M93.5151 9.07324L93.5151 10.2148L94.4907 10.2148L94.4907 10.9639L93.5151 10.9639L93.5151 13.2793C93.5151 13.751 93.7095 13.957 94.1519 13.957C94.2651 13.957 94.3784 13.9502 94.4907 13.9365L94.4907 14.6768C94.3311 14.7051 94.1694 14.7207 94.0073 14.7217C93.019 14.7217 92.6255 14.374 92.6255 13.5059L92.6255 10.9629L91.9106 10.9629L91.9106 10.2148L92.6255 10.2148L92.6255 9.07324L93.5151 9.07324Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M95.7046 8.43652L96.5854 8.43652L96.5854 10.918L96.6558 10.918C96.7739 10.6582 96.9692 10.4424 97.2148 10.2979C97.4604 10.1533 97.7446 10.0879 98.0288 10.1113C98.2466 10.0996 98.4648 10.1357 98.667 10.2178C98.8691 10.2998 99.0508 10.4258 99.1987 10.5859C99.3472 10.7461 99.458 10.9375 99.5239 11.1455C99.5894 11.3535 99.6084 11.5732 99.5796 11.79L99.5796 14.6973L98.6899 14.6973L98.6899 12.0088C98.6899 11.29 98.355 10.9258 97.7271 10.9258C97.5742 10.9131 97.4209 10.9346 97.2769 10.9873C97.1333 11.04 97.0029 11.124 96.8945 11.2334C96.7866 11.3418 96.7036 11.4727 96.6519 11.6172C96.5996 11.7607 96.5796 11.915 96.5933 12.0674L96.5933 14.6973L95.7046 14.6973L95.7046 8.43652Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M104.761 13.4814C104.641 13.8926 104.379 14.249 104.022 14.4873C103.666 14.7256 103.237 14.8301 102.81 14.7842C102.514 14.792 102.219 14.7354 101.946 14.6182C101.674 14.5 101.43 14.3252 101.232 14.1035C101.034 13.8828 100.887 13.6211 100.8 13.3369C100.713 13.0537 100.689 12.7539 100.73 12.46C100.69 12.165 100.715 11.8652 100.801 11.5801C100.888 11.2959 101.035 11.0332 101.231 10.8096C101.428 10.5869 101.671 10.4092 101.942 10.2871C102.214 10.166 102.509 10.1045 102.806 10.1074C104.059 10.1074 104.815 10.9639 104.815 12.3779L104.815 12.6875L101.635 12.6875L101.635 12.7373C101.622 12.9023 101.642 13.0693 101.696 13.2256C101.75 13.3828 101.837 13.5264 101.95 13.6475C102.062 13.7695 102.2 13.8652 102.352 13.9307C102.504 13.9961 102.669 14.0293 102.834 14.0273C103.047 14.0527 103.263 14.0146 103.453 13.917C103.644 13.8203 103.802 13.6689 103.906 13.4814L104.761 13.4814ZM101.635 12.0303L103.91 12.0303C103.921 11.8789 103.9 11.7275 103.85 11.585C103.798 11.4424 103.718 11.3115 103.614 11.2012C103.51 11.0918 103.383 11.0049 103.243 10.9473C103.104 10.8887 102.953 10.8604 102.801 10.8643C102.647 10.8623 102.495 10.8906 102.353 10.9482C102.21 11.0068 102.081 11.0928 101.973 11.2012C101.864 11.3096 101.778 11.4395 101.72 11.582C101.662 11.7236 101.633 11.877 101.635 12.0303L101.635 12.0303Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
@@ -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="clip161_49334">
|
||||
<rect id="icon / jam-icons / outline & logos / facebook" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="icon / jam-icons / outline & logos / facebook" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip161_49334)">
|
||||
<path id="Vector" d="M9.04639 5.86523L9.04639 8.61328L7.03223 8.61328L7.03223 11.9727L9.04639 11.9727L9.04639 21.959L13.1802 21.959L13.1802 11.9736L15.9551 11.9736C15.9551 11.9736 16.2153 10.3633 16.3413 8.60059L13.1973 8.60059L13.1973 6.30273C13.1973 5.95996 13.6475 5.49805 14.0933 5.49805L16.3472 5.49805L16.3472 2L13.2832 2C8.94336 2 9.04639 5.36328 9.04639 5.86523Z" fill="#E2E8F0" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1007 B |
@@ -0,0 +1,16 @@
|
||||
<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="clip161_49336">
|
||||
<rect id="icon / jam-icons / outline & logos / instagram" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="icon / jam-icons / outline & logos / instagram" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip161_49336)">
|
||||
<path id="Vector" d="M16.0171 2L7.94678 2C6.37012 2.00195 4.8584 2.62891 3.74365 3.74414C2.62842 4.85938 2.00146 6.37109 2 7.94824L2 16.0176C2.00195 17.5947 2.62891 19.1064 3.74414 20.2217C4.85938 21.3359 6.37109 21.9639 7.94824 21.9648L16.0181 21.9648C17.5947 21.9629 19.1064 21.3359 20.2217 20.2207C21.3364 19.1055 21.9634 17.5938 21.9648 16.0166L21.9648 7.94727C21.9634 6.37012 21.3359 4.8584 20.2207 3.74316C19.1055 2.62891 17.5938 2.00195 16.0171 2L16.0171 2ZM19.957 16.0166C19.957 16.5342 19.855 17.0469 19.6572 17.5244C19.459 18.0029 19.1689 18.4375 18.8032 18.8027C18.437 19.1689 18.0029 19.459 17.5249 19.6572C17.0469 19.8555 16.5342 19.957 16.0171 19.957L7.94678 19.957C6.90234 19.957 5.90039 19.542 5.16162 18.8027C4.42285 18.0635 4.00781 17.0615 4.00781 16.0166L4.00781 7.94727C4.0083 6.90234 4.42334 5.90039 5.16211 5.16211C5.90137 4.42285 6.90332 4.00781 7.94824 4.00781L16.0181 4.00781C17.063 4.00781 18.0645 4.42383 18.8032 5.16211C19.542 5.90137 19.957 6.90332 19.957 7.94824L19.957 16.0176L19.957 16.0166Z" fill="#E2E8F0" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M11.9819 6.81934C10.6133 6.82129 9.30127 7.36621 8.3335 8.33398C7.36621 9.30176 6.82178 10.6133 6.81982 11.9824C6.82129 13.3516 7.36572 14.6641 8.3335 15.6318C9.30176 16.5996 10.6138 17.1445 11.9829 17.1465C13.3521 17.1445 14.6646 16.6006 15.6328 15.6318C16.6006 14.6641 17.145 13.3516 17.147 11.9824C17.1445 10.6133 16.5996 9.30078 15.6313 8.33398C14.6631 7.36621 13.3506 6.82129 11.9819 6.82031L11.9819 6.81934ZM11.9819 15.1387C11.145 15.1387 10.3428 14.8057 9.75098 14.2139C9.15918 13.623 8.82666 12.8203 8.82666 11.9834C8.82666 11.1465 9.15918 10.3438 9.75098 9.75195C10.3428 9.16113 11.145 8.82812 11.9819 8.82812C12.8184 8.82812 13.6211 9.16113 14.2129 9.75195C14.8042 10.3438 15.1367 11.1465 15.1367 11.9834C15.1367 12.8203 14.8042 13.623 14.2129 14.2139C13.6211 14.8057 12.8184 15.1387 11.9819 15.1387Z" fill="#E2E8F0" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M18.3931 6.8584C18.3931 7.54102 17.8394 8.09473 17.1558 8.09473C16.4727 8.09473 15.9189 7.54102 15.9189 6.8584C15.9189 6.1748 16.4727 5.62109 17.1558 5.62109C17.8394 5.62109 18.3931 6.1748 18.3931 6.8584Z" fill="#E2E8F0" fill-opacity="1.000000" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg width="135.000000" height="40.000000" viewBox="0 0 135 40" 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="Vector" d="M5 0L130 0C132.761 0 135 2.23828 135 5L135 35C135 37.7617 132.761 40 130 40L5 40C2.23877 40 0 37.7617 0 35L0 5C0 2.23828 2.23877 0 5 0Z" fill="#000000" fill-opacity="1.000000" fill-rule="evenodd"/>
|
||||
<path id="Vector" d="M130 0.799805C131.114 0.799805 132.182 1.24219 132.97 2.03027C133.757 2.81738 134.2 3.88574 134.2 5L134.2 35C134.2 36.1143 133.757 37.1826 132.97 37.9697C132.182 38.7578 131.114 39.2002 130 39.2002L5 39.2002C3.88623 39.2002 2.81787 38.7578 2.03027 37.9697C1.24268 37.1826 0.799805 36.1143 0.799805 35L0.799805 5C0.799805 3.88574 1.24268 2.81738 2.03027 2.03027C2.81787 1.24219 3.88623 0.799805 5 0.799805L130 0.799805ZM130 0L5 0C3.67383 0 2.40234 0.526367 1.46436 1.46484C0.526855 2.40234 0 3.67383 0 5L0 35C0 36.3262 0.526855 37.5977 1.46436 38.5352C2.40234 39.4736 3.67383 40 5 40L130 40C131.326 40 132.598 39.4736 133.536 38.5352C134.473 37.5977 135 36.3262 135 35L135 5C135 3.67383 134.473 2.40234 133.536 1.46484C132.598 0.526367 131.326 0 130 0Z" fill="#A6A6A6" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="GET IT ON" d="M44.3757 10.4858L46.4666 10.4858L46.4666 13.3869Q45.9961 13.5465 45.4989 13.6242Q45.0018 13.702 44.388 13.702Q43.4551 13.702 42.8147 13.3296Q42.1743 12.9572 41.8429 12.2678Q41.5115 11.5783 41.5115 10.629Q41.5115 9.70018 41.8777 9.00866Q42.2439 8.31715 42.9375 7.93457Q43.631 7.55198 44.6049 7.55198Q45.1041 7.55198 45.5521 7.64405Q46.0002 7.73611 46.3848 7.90388L46.1188 8.50946Q45.7874 8.36625 45.3885 8.26396Q44.9895 8.16166 44.564 8.16166Q43.8315 8.16166 43.3098 8.46445Q42.7881 8.76725 42.5119 9.31964Q42.2357 9.87203 42.2357 10.629Q42.2357 11.3778 42.4771 11.9323Q42.7186 12.4867 43.23 12.7936Q43.7415 13.1005 44.5476 13.1005Q44.9527 13.1005 45.2432 13.0534Q45.5337 13.0063 45.771 12.9409L45.771 11.1078L44.3757 11.1078L44.3757 10.4858ZM51.2418 13.6201L47.911 13.6201L47.911 7.63791L51.2418 7.63791L51.2418 8.25168L48.6067 8.25168L48.6067 10.1871L51.0904 10.1871L51.0904 10.7927L48.6067 10.7927L48.6067 13.0063L51.2418 13.0063L51.2418 13.6201ZM54.4047 13.6201L53.705 13.6201L53.705 8.25168L51.8228 8.25168L51.8228 7.63791L56.2788 7.63791L56.2788 8.25168L54.4047 8.25168L54.4047 13.6201ZM59.3599 13.6201L59.3599 7.63791L60.0555 7.63791L60.0555 13.6201L59.3599 13.6201ZM63.5376 13.6201L62.8379 13.6201L62.8379 8.25168L60.9557 8.25168L60.9557 7.63791L65.4117 7.63791L65.4117 8.25168L63.5376 8.25168L63.5376 13.6201ZM73.6812 10.6208Q73.6812 11.3123 73.5052 11.8791Q73.3293 12.4458 72.9835 12.8529Q72.6378 13.26 72.1263 13.481Q71.6148 13.702 70.9397 13.702Q70.2441 13.702 69.7265 13.481Q69.2088 13.26 68.8672 12.8509Q68.5255 12.4417 68.3557 11.8729Q68.1859 11.3042 68.1859 10.6126Q68.1859 9.69608 68.4887 9.00457Q68.7915 8.31306 69.4073 7.92843Q70.0231 7.5438 70.952 7.5438Q71.8399 7.5438 72.4475 7.92434Q73.0551 8.30487 73.3682 8.99434Q73.6812 9.68381 73.6812 10.6208ZM68.9183 10.6208Q68.9183 11.3819 69.1352 11.9384Q69.3521 12.4949 69.8001 12.7977Q70.2482 13.1005 70.9397 13.1005Q71.6353 13.1005 72.0792 12.7977Q72.5232 12.4949 72.736 11.9384Q72.9488 11.3819 72.9488 10.6208Q72.9488 9.46285 72.4639 8.81021Q71.979 8.15757 70.952 8.15757Q70.2563 8.15757 69.8063 8.45627Q69.3562 8.75497 69.1372 9.30532Q68.9183 9.85566 68.9183 10.6208ZM79.6879 13.6201L78.8859 13.6201L75.6248 8.59539L75.5921 8.59539Q75.6043 8.79589 75.6207 9.05163Q75.6371 9.30736 75.6473 9.59583Q75.6575 9.88431 75.6575 10.183L75.6575 13.6201L75.011 13.6201L75.011 7.63791L75.8089 7.63791L79.0578 12.6463L79.0864 12.6463Q79.0783 12.5031 79.066 12.2391Q79.0537 11.9752 79.0435 11.6704Q79.0332 11.3655 79.0332 11.1037L79.0332 7.63791L79.6879 7.63791L79.6879 13.6201Z" fill="#FFFFFF" fill-opacity="1.000000" fill-rule="evenodd"/>
|
||||
<path id="GET IT ON" d="M44.3757 10.4858L46.4666 10.4858L46.4666 13.3869Q45.9961 13.5465 45.4989 13.6242Q45.0018 13.702 44.388 13.702Q43.4551 13.702 42.8147 13.3296Q42.1743 12.9572 41.8429 12.2678Q41.5115 11.5783 41.5115 10.629Q41.5115 9.70018 41.8777 9.00866Q42.2439 8.31715 42.9375 7.93457Q43.631 7.55198 44.6049 7.55198Q45.1041 7.55198 45.5521 7.64405Q46.0002 7.73611 46.3848 7.90388L46.1188 8.50946Q45.7874 8.36625 45.3885 8.26396Q44.9895 8.16166 44.564 8.16166Q43.8315 8.16166 43.3098 8.46445Q42.7881 8.76725 42.5119 9.31964Q42.2357 9.87203 42.2357 10.629Q42.2357 11.3778 42.4771 11.9323Q42.7186 12.4867 43.23 12.7936Q43.7415 13.1005 44.5476 13.1005Q44.9527 13.1005 45.2432 13.0534Q45.5337 13.0063 45.771 12.9409L45.771 11.1078L44.3757 11.1078L44.3757 10.4858ZM51.2418 13.6201L47.911 13.6201L47.911 7.63791L51.2418 7.63791L51.2418 8.25168L48.6067 8.25168L48.6067 10.1871L51.0904 10.1871L51.0904 10.7927L48.6067 10.7927L48.6067 13.0063L51.2418 13.0063L51.2418 13.6201ZM54.4047 13.6201L53.705 13.6201L53.705 8.25168L51.8228 8.25168L51.8228 7.63791L56.2788 7.63791L56.2788 8.25168L54.4047 8.25168L54.4047 13.6201ZM59.3599 13.6201L59.3599 7.63791L60.0555 7.63791L60.0555 13.6201L59.3599 13.6201ZM63.5376 13.6201L62.8379 13.6201L62.8379 8.25168L60.9557 8.25168L60.9557 7.63791L65.4117 7.63791L65.4117 8.25168L63.5376 8.25168L63.5376 13.6201ZM73.6812 10.6208Q73.6812 11.3123 73.5052 11.8791Q73.3293 12.4458 72.9835 12.8529Q72.6378 13.26 72.1263 13.481Q71.6148 13.702 70.9397 13.702Q70.2441 13.702 69.7265 13.481Q69.2088 13.26 68.8672 12.8509Q68.5255 12.4417 68.3557 11.8729Q68.1859 11.3042 68.1859 10.6126Q68.1859 9.69608 68.4887 9.00457Q68.7915 8.31306 69.4073 7.92843Q70.0231 7.5438 70.952 7.5438Q71.8399 7.5438 72.4475 7.92434Q73.0551 8.30487 73.3682 8.99434Q73.6812 9.68381 73.6812 10.6208ZM68.9183 10.6208Q68.9183 11.3819 69.1352 11.9384Q69.3521 12.4949 69.8001 12.7977Q70.2482 13.1005 70.9397 13.1005Q71.6353 13.1005 72.0792 12.7977Q72.5232 12.4949 72.736 11.9384Q72.9488 11.3819 72.9488 10.6208Q72.9488 9.46285 72.4639 8.81021Q71.979 8.15757 70.952 8.15757Q70.2563 8.15757 69.8063 8.45627Q69.3562 8.75497 69.1372 9.30532Q68.9183 9.85566 68.9183 10.6208ZM79.6879 13.6201L78.8859 13.6201L75.6248 8.59539L75.5921 8.59539Q75.6043 8.79589 75.6207 9.05163Q75.6371 9.30736 75.6473 9.59583Q75.6575 9.88431 75.6575 10.183L75.6575 13.6201L75.011 13.6201L75.011 7.63791L75.8089 7.63791L79.0578 12.6463L79.0864 12.6463Q79.0783 12.5031 79.066 12.2391Q79.0537 11.9752 79.0435 11.6704Q79.0332 11.3655 79.0332 11.1037L79.0332 7.63791L79.6879 7.63791L79.6879 13.6201Z" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="0.200000"/>
|
||||
<path id="Vector" d="M68.1401 21.75C67.2974 21.752 66.4746 22.0039 65.7749 22.4736C65.0757 22.9434 64.5312 23.6094 64.2104 24.3887C63.8901 25.168 63.8076 26.0244 63.9741 26.8506C64.1401 27.6768 64.5479 28.4346 65.145 29.0293C65.7422 29.624 66.502 30.0273 67.3286 30.1904C68.1558 30.3525 69.0117 30.2656 69.7896 29.9414C70.5674 29.6172 71.2314 29.0703 71.6978 28.3682C72.1641 27.667 72.4121 26.8428 72.4102 26C72.4185 25.4375 72.313 24.8799 72.1011 24.3594C71.8892 23.8389 71.5747 23.3662 71.1763 22.9697C70.7778 22.5732 70.3037 22.2607 69.7822 22.0508C69.2607 21.8418 68.7021 21.7393 68.1401 21.75ZM68.1401 28.5801C67.6235 28.6162 67.1084 28.4961 66.6611 28.2354C66.2139 27.9746 65.8555 27.5859 65.6323 27.1182C65.4092 26.6514 65.3315 26.1279 65.4097 25.6162C65.4883 25.1045 65.7183 24.6279 66.0713 24.249C66.4238 23.8701 66.8823 23.6055 67.3867 23.4902C67.8916 23.376 68.4194 23.415 68.9014 23.6045C69.3833 23.793 69.7974 24.1221 70.0898 24.5498C70.3818 24.9766 70.5391 25.4824 70.54 26C70.5562 26.3281 70.5063 26.6562 70.394 26.9639C70.2812 27.2725 70.1079 27.5557 69.8843 27.7959C69.6606 28.0361 69.3911 28.2295 69.0913 28.3643C68.792 28.499 68.4683 28.5723 68.1401 28.5801ZM58.8203 21.75C57.9775 21.752 57.1543 22.0039 56.4551 22.4736C55.7554 22.9434 55.2109 23.6094 54.8906 24.3887C54.5698 25.168 54.4878 26.0244 54.6538 26.8506C54.8203 27.6768 55.2275 28.4346 55.8247 29.0293C56.4219 29.624 57.1821 30.0273 58.0088 30.1904C58.8354 30.3525 59.6919 30.2656 60.4697 29.9414C61.2471 29.6172 61.9111 29.0703 62.3779 28.3682C62.8442 27.667 63.0923 26.8428 63.0903 26C63.0981 25.4375 62.9932 24.8799 62.7812 24.3594C62.5693 23.8389 62.2549 23.3662 61.8564 22.9697C61.458 22.5732 60.9839 22.2607 60.4624 22.0508C59.9404 21.8418 59.3823 21.7393 58.8203 21.75ZM58.8203 28.5801C58.3037 28.6162 57.7886 28.4961 57.3408 28.2354C56.8936 27.9746 56.5352 27.5859 56.312 27.1182C56.0889 26.6514 56.0117 26.1279 56.0898 25.6162C56.168 25.1045 56.3984 24.6279 56.751 24.249C57.1035 23.8701 57.562 23.6055 58.0669 23.4902C58.5718 23.376 59.0991 23.415 59.5811 23.6045C60.0635 23.793 60.4775 24.1221 60.7695 24.5498C61.062 24.9766 61.2188 25.4824 61.2202 26C61.2363 26.3281 61.1865 26.6562 61.0737 26.9639C60.9614 27.2725 60.7881 27.5557 60.5645 27.7959C60.3403 28.0361 60.0708 28.2295 59.7715 28.3643C59.4717 28.499 59.1484 28.5723 58.8203 28.5801ZM47.7402 23.0596L47.7402 24.8604L52.0601 24.8604C51.9922 25.708 51.6401 26.5078 51.0601 27.1299C50.627 27.5713 50.1055 27.917 49.5303 28.1445C48.9551 28.3721 48.3384 28.4756 47.7202 28.4502C46.4473 28.4502 45.2261 27.9443 44.3262 27.0439C43.4258 26.1436 42.9199 24.9229 42.9199 23.6504C42.9199 22.377 43.4258 21.1562 44.3262 20.2559C45.2261 19.3555 46.4473 18.8496 47.7202 18.8496C48.9404 18.832 50.1191 19.2949 51 20.1396L52.27 18.8701C51.6743 18.2803 50.9663 17.8154 50.1885 17.5029C49.4106 17.1904 48.5781 17.0371 47.7402 17.0498C46.8511 17.0156 45.9644 17.1602 45.1328 17.4766C44.3013 17.793 43.542 18.2744 42.9009 18.8906C42.2598 19.5078 41.7495 20.2471 41.4009 21.0664C41.0527 21.8848 40.873 22.7656 40.873 23.6553C40.873 24.5449 41.0527 25.4248 41.4009 26.2441C41.7495 27.0625 42.2598 27.8018 42.9009 28.4189C43.542 29.0361 44.3013 29.5166 45.1328 29.833C45.9644 30.1494 46.8511 30.2949 47.7402 30.2598C48.5957 30.2939 49.4487 30.1465 50.2432 29.8281C51.0376 29.5088 51.7554 29.0264 52.3501 28.4102C53.4004 27.2559 53.96 25.7393 53.9102 24.1797C53.9136 23.8047 53.8838 23.4297 53.8203 23.0596L47.7402 23.0596ZM93.0503 24.46C92.7891 23.6924 92.3018 23.0225 91.6514 22.5391C91.0015 22.0547 90.2197 21.7793 89.4102 21.75C88.8633 21.749 88.3218 21.8604 87.8193 22.0762C87.3169 22.293 86.8643 22.6104 86.4893 23.0088C86.1147 23.4072 85.8252 23.8779 85.6396 24.3926C85.4541 24.9072 85.376 25.4541 85.4102 26C85.4014 26.9131 85.6885 27.8037 86.228 28.54C86.7681 29.2764 87.5312 29.8184 88.4043 30.085C89.2773 30.3516 90.2134 30.3291 91.0723 30.0195C91.9312 29.7109 92.667 29.1318 93.1699 28.3701L91.7202 27.3701C91.5039 27.7305 91.1973 28.0273 90.8306 28.2334C90.4644 28.4385 90.0503 28.5439 89.6299 28.54C89.1968 28.5576 88.7681 28.4443 88.4004 28.2139C88.0327 27.9834 87.7432 27.6475 87.5703 27.25L93.2603 24.9004L93.0503 24.46ZM87.25 25.8799C87.23 25.5693 87.272 25.2578 87.3735 24.9639C87.4751 24.6689 87.6343 24.3984 87.8418 24.166C88.0498 23.9346 88.3018 23.7461 88.583 23.6133C88.8643 23.4795 89.1689 23.4043 89.48 23.3896C89.8022 23.3701 90.123 23.4463 90.4023 23.6084C90.6816 23.7695 90.9072 24.0107 91.0503 24.2998L87.25 25.8799ZM82.6299 30L84.5 30L84.5 17.5L82.6299 17.5L82.6299 30ZM79.5703 22.7002L79.5 22.7002C79.2207 22.3818 78.876 22.1279 78.4893 21.9551C78.1025 21.7822 77.6836 21.6953 77.2603 21.7002C76.1675 21.7529 75.1367 22.2246 74.3818 23.0166C73.6274 23.8086 73.2065 24.8604 73.2065 25.9551C73.2065 27.0488 73.6274 28.1016 74.3818 28.8936C75.1367 29.6855 76.1675 30.1572 77.2603 30.21C77.6841 30.2188 78.1045 30.1328 78.4917 29.96C78.8789 29.7871 79.2231 29.5312 79.5 29.21L79.5703 29.21L79.5703 29.8203C79.5703 31.4502 78.7002 32.3203 77.30Line truncated
|
||||
<path id="Vector" d="M20.7197 19.4199L10.0698 30.7197C10.186 31.1475 10.3994 31.542 10.6924 31.874C10.9858 32.2061 11.3516 32.4668 11.7612 32.6348C12.1709 32.8027 12.6138 32.874 13.0557 32.8438C13.4976 32.8135 13.9268 32.6826 14.3101 32.46L26.3101 25.54L20.7197 19.4199Z" fill="#EA4335" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M31.4902 17.5L26.3101 14.5L20.4902 19.6904L26.3301 25.54L31.4805 22.54C31.938 22.2949 32.3208 21.9297 32.5874 21.4844C32.8545 21.0391 32.9951 20.5293 32.9951 20.0098C32.9951 19.4902 32.8545 18.9814 32.5874 18.5352C32.3208 18.0898 31.938 17.7256 31.4805 17.4805L31.4902 17.5Z" fill="#FBBC04" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M10.0698 9.28027C10.0171 9.5166 9.99365 9.75879 10 10L10 30C10.002 30.25 10.0352 30.499 10.1001 30.7402L21.1001 19.7402L10.0698 9.28027Z" fill="#4285F4" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M20.7998 20L26.3101 14.4893L14.3101 7.5498C13.8594 7.28125 13.3447 7.13965 12.8198 7.13965C12.1875 7.1416 11.5728 7.35059 11.0708 7.73535C10.5684 8.12012 10.2065 8.65918 10.04 9.26953L20.7998 20Z" fill="#34A853" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -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="clip161_49333">
|
||||
<rect id="icon / jam-icons / outline & logos / youtube" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="icon / jam-icons / outline & logos / youtube" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip161_49333)">
|
||||
<path id="Vector" d="M17.812 5.0166L6.14502 5.0166C3.85498 5.0166 2 6.85156 2 9.11523L2 14.8838C2 17.1475 3.85596 18.9834 6.14502 18.9834L17.812 18.9834C20.1021 18.9834 21.957 17.1475 21.957 14.8838L21.957 9.11523C21.957 6.85156 20.1011 5.01562 17.812 5.01562L17.812 5.0166ZM15.0088 12.2793L9.55176 14.8545C9.51855 14.8711 9.48193 14.8779 9.44482 14.876C9.4082 14.874 9.37256 14.8623 9.34082 14.8428C9.30957 14.8232 9.28369 14.7959 9.26562 14.7637C9.24756 14.7314 9.23828 14.6953 9.23779 14.6582L9.23779 9.34961C9.23877 9.3125 9.24854 9.27637 9.26709 9.24414C9.28564 9.21191 9.31201 9.18457 9.34375 9.16504C9.37549 9.14648 9.41162 9.13574 9.44873 9.13379C9.48584 9.13184 9.52295 9.13965 9.55615 9.15625L15.0142 11.8916C15.0503 11.9102 15.0811 11.9375 15.1021 11.9727C15.123 12.0068 15.1343 12.0469 15.1338 12.0879C15.1328 12.1279 15.1211 12.168 15.0991 12.2021C15.0771 12.2363 15.0459 12.2637 15.0088 12.2803L15.0088 12.2793Z" fill="#E2E8F0" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import "@/shared/ui/auth-classes.scss";
|
||||
import { useState } from "react";
|
||||
import SendEmail from "./send-email/send-email";
|
||||
import ConfirmCode from "./confirm-code/confirm-code";
|
||||
|
||||
const ForgotPasswordForm = () => {
|
||||
const [changeForm, setChangeForm] = useState<boolean>(false);
|
||||
return changeForm ? (
|
||||
<ConfirmCode setChangeForm={setChangeForm} />
|
||||
) : (
|
||||
<SendEmail setChangeForm={setChangeForm} />
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPasswordForm;
|
||||
@@ -0,0 +1,32 @@
|
||||
.confirm-code {
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
|
||||
button {
|
||||
padding: 10px 18px;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
background: rgb(54, 149, 216);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-code-send-email {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
color: rgb(54, 149, 216);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.confirm-code {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import Image from "next/image";
|
||||
import key from "./icons/key.svg";
|
||||
import AuthInput from "@/features/AuthInput/AuthInput";
|
||||
import "./confirm-code.scss";
|
||||
import { useState } from "react";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ITokens } from "@/shared/types/token-type";
|
||||
|
||||
interface IConfirmCodeProps {
|
||||
setChangeForm: (boolean: boolean) => void;
|
||||
}
|
||||
|
||||
const ConfirmCode: React.FC<IConfirmCodeProps> = ({
|
||||
setChangeForm,
|
||||
}: IConfirmCodeProps) => {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
if (!formData.get("code")) {
|
||||
return setError("Заполните поле");
|
||||
}
|
||||
|
||||
try {
|
||||
setError("");
|
||||
setLoader(true);
|
||||
const res = await apiInstance.post<ITokens>(
|
||||
"/users/password_reset/code/",
|
||||
formData
|
||||
);
|
||||
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
localStorage.setItem(
|
||||
"transitional",
|
||||
JSON.stringify(res.data)
|
||||
);
|
||||
router.push("/sign-in/reset-code");
|
||||
}
|
||||
} catch (error) {
|
||||
setError("An error ocured");
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="auth-wrapper">
|
||||
<div className="auth-icon2">
|
||||
<Image src={key} alt="Key Icon" />
|
||||
</div>
|
||||
<div className="auth-header">
|
||||
<h2>Введите код</h2>
|
||||
<p>Введите код для сброса и восстановления пароля</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="confirm-code">
|
||||
<AuthInput
|
||||
type="text"
|
||||
name="code"
|
||||
label="Код сброса пароля"
|
||||
error={error}
|
||||
placeholder="Введите код"
|
||||
/>
|
||||
<button type="submit">
|
||||
{loader ? <Loader /> : "Сбросить пароль"}
|
||||
</button>
|
||||
</form>
|
||||
<button
|
||||
onClick={() => setChangeForm(false)}
|
||||
className="confirm-code-send-email"
|
||||
>
|
||||
Получить код
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmCode;
|
||||
@@ -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="clip1668_53329">
|
||||
<rect id="key" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="key" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip1668_53329)">
|
||||
<path id="Vector" d="M21 2L19 4L15.5 7.5L11.3906 11.6104L11.3896 11.6113C10.3516 10.6094 8.96289 10.0547 7.52051 10.0674C6.07812 10.0801 4.69922 10.6582 3.67969 11.6777C2.65918 12.6973 2.08105 14.0771 2.06836 15.5195C2.05566 16.9609 2.61035 18.3506 3.6123 19.3877C4.12207 19.9043 4.72852 20.3145 5.39746 20.5957C6.06543 20.877 6.7832 21.0225 7.50879 21.0254C8.23438 21.0273 8.95312 20.8867 9.62402 20.6104C10.2939 20.333 10.9033 19.9268 11.416 19.4141C11.9297 18.9014 12.3359 18.292 12.6123 17.6211C12.8887 16.9502 13.0293 16.2324 13.0273 15.5068C13.0244 14.7812 12.8789 14.0635 12.5977 13.3945C12.3174 12.7256 11.9062 12.1191 11.3906 11.6104M15.5 7.5L18.5 10.5L22 7L19 4" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -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 |
@@ -0,0 +1,32 @@
|
||||
.send-email {
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
|
||||
button {
|
||||
padding: 10px 18px;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
background: rgb(54, 149, 216);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.send-email-confirm-code {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
color: rgb(54, 149, 216);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.send-email {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import Image from "next/image";
|
||||
import mail from "./icons/mail.svg";
|
||||
import AuthInput from "@/features/AuthInput/AuthInput";
|
||||
import "./send-email.scss";
|
||||
import { useState } from "react";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
|
||||
interface ISendEmailProps {
|
||||
setChangeForm: (boolean: boolean) => void;
|
||||
}
|
||||
|
||||
const SendEmail: React.FC<ISendEmailProps> = ({
|
||||
setChangeForm,
|
||||
}: ISendEmailProps) => {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
|
||||
const handleSubmit: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
if (!formData.get("email")) {
|
||||
return setError("Заполните поле");
|
||||
}
|
||||
|
||||
try {
|
||||
setError("");
|
||||
setLoader(true);
|
||||
const res = await apiInstance.post(
|
||||
"/users/password_reset/",
|
||||
formData
|
||||
);
|
||||
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
setChangeForm(true);
|
||||
}
|
||||
} catch (error) {
|
||||
setError("An error ocured");
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="auth-wrapper">
|
||||
<div className="auth-icon2">
|
||||
<Image src={mail} alt="Key Icon" />
|
||||
</div>
|
||||
<div className="auth-header">
|
||||
<h2>Введите email</h2>
|
||||
<p>
|
||||
Введите email и мы отправим код для восстановления пароля
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="send-email">
|
||||
<AuthInput
|
||||
type="text"
|
||||
name="email"
|
||||
label="Email"
|
||||
error={error}
|
||||
placeholder="Введите email"
|
||||
/>
|
||||
<button type="submit">
|
||||
{loader ? <Loader /> : "Отправить код"}
|
||||
</button>
|
||||
</form>
|
||||
<button
|
||||
onClick={() => setChangeForm(true)}
|
||||
className="send-email-confirm-code"
|
||||
>
|
||||
Потвердить код
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SendEmail;
|
||||
@@ -0,0 +1,79 @@
|
||||
.header {
|
||||
position: relative;
|
||||
height: 386px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__background {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__text {
|
||||
padding: 0 90px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 26px;
|
||||
color: white;
|
||||
|
||||
h1 {
|
||||
max-width: 80%;
|
||||
text-wrap: balance;
|
||||
font-size: 52px;
|
||||
font-weight: 800;
|
||||
line-height: 62px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
line-height: 29px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.header {
|
||||
&__text {
|
||||
padding: 0 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.header {
|
||||
&__text {
|
||||
gap: 22px;
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.header {
|
||||
height: 250px;
|
||||
&__text {
|
||||
padding: 0 16px;
|
||||
gap: 12px;
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
line-height: 106%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import "./Header.scss";
|
||||
import Image from "next/image";
|
||||
import background from "./assets/background.svg";
|
||||
import HeaderLink from "./HeaderLink/HeaderLink";
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<header className="header">
|
||||
<Image
|
||||
className="header__background"
|
||||
src={background}
|
||||
alt="Background Image"
|
||||
/>
|
||||
<div className="header__text">
|
||||
<h1>
|
||||
Borem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
</h1>
|
||||
<p>
|
||||
Borem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<HeaderLink />
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,31 @@
|
||||
.header-link {
|
||||
width: 480px;
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
bottom: -10%;
|
||||
z-index: 10;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
border-radius: 15px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2);
|
||||
|
||||
img {
|
||||
width: 36px;
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.header-link {
|
||||
width: 95%;
|
||||
bottom: -20%;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import "./HeaderLink.scss";
|
||||
import Image from "next/image";
|
||||
import arrow_right from "./icons/arrow-right-circle.svg";
|
||||
import Link from "next/link";
|
||||
|
||||
const HeaderLink = () => {
|
||||
return (
|
||||
<Link href="/create-report" className="header-link">
|
||||
Отметить разбитую дорогу
|
||||
<Image src={arrow_right} alt="Arrow Right Icon" />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderLink;
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg width="32.000000" height="32.000000" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<desc>
|
||||
Created with Pixso.
|
||||
</desc>
|
||||
<defs/>
|
||||
<rect id="arrow-right-circle" width="32.000000" height="32.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<path id="Vector" d="M16 29.332C8.63623 29.332 2.6665 23.3633 2.6665 16C2.6665 8.63477 8.63623 2.66602 16 2.66602C23.3638 2.66602 29.333 8.63477 29.333 16C29.333 23.3633 23.3638 29.332 16 29.332Z" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
<path id="Vector" d="M16 21.332L21.3335 16L16 10.666" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M10.6665 16L21.333 16" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 934 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="1440.000000" height="386.000000" viewBox="0 0 1440 386" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<desc>
|
||||
Created with Pixso.
|
||||
</desc>
|
||||
<defs>
|
||||
<pattern id="pattern_161_492460" patternContentUnits="objectBoundingBox" width="1.000000" height="1.000000">
|
||||
<use xlink:href="#image161_49246_0" transform="matrix(0.002079,0,0,0.007756,0,-0.434581)"/>
|
||||
</pattern>
|
||||
<image id="image161_49246_0" width="481.000000" height="241.000000" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAeEAAADxCAYAAAD86w8hAAAABHNCSVQICAgIfAhkiAAAIABJREFUeJysvVmPLUlyJvaZeZzc7q17q5vN5rREScRAgIDRwwDzNNC79NP1QA0JisMhNc2tSQ63atZyt8yzhJvpwczczT3iZGY1x4FC3TwR4Yu5uy2fmZvTH/5/f6ufH1cAK8AFqgoAKMSoKmAQ2H6CEAAIAAZDwMyoWsBYEU+ICKQKIgKYARGIfd6eqRC4CAQMEoKi2gvMELF6QQIFwLXYv6MTyt4/goiAFBAmKAGkAKMX6+lcZPrb3lDVNvbW12iDCBQ0gFr/AEDU2k3vxbe5rlzys2gzvpvbtb+jnt7P/P7mN6d5/22mwvW/7Rvd+RvYo2R8q6pQVrByG1u0rTqOLfd3Hsu1v2f6jm2MdJjbYV9TUIaibr65Vp6bwzz2536Z69ibu5fK3phyYRCqzmv65TL39RpNr/Un3iUFlHwtE4Gx3Q/WoIIKA6tAeKRFp5OAqKT9Udv6mddL64fvQTCDtLZnrW5lGCfB5lmmRu9zSRQav6m19r0P44csi//b3mddYFzigv09M64LEQGhNP7G3l5vnQGI1aTcvyVBTXMR/DCvBFK1ulFB8HGRNB4a9Qz0zN/HN/k31nFufV8xLY2GoiuYjC7B2wkFQsaXFt+TNpYKgIf6hr5p8Je+RgBOMkmcQp3a878BBolCKNo0mRb9iTV7kQsWWlBJQBo1MFSr0df/DcB4HSrU50MJYJS+jsvSeLhq9foEStY7k5uCh/sDlsul4vHpCQQAtKJCwXp90RMRiCueE28xqOGbxtyNMWdmD2WAVu+colDe6H0h5Elo/VMAvGXogAnlPUH3UpkF5fzbHkPM9Hqeef/ryktjea79TvdRwF3v774ac61NEtssL76/894u435Fm8+Vf43Q25a81TurIzIFENLXt0DBGJWHrBDYhh3Xy2uF32+yvqL+plB6W1UAcmX6N61z7t/eMwCNJntjCAXanq2DQhzv/pj+saIx7b39H7/tj/vywnhrn1MCSM+Dogmc2oihBaCa6sgKqSYF4wyR2ANrU0Tinb19KvFv/z8zo0a/5PVz2vgcxnljIgCXZAxslaGhP3Jsbdo6Pz/TaJrwl/oW63VeA3uGSBqPCVl7jysgrJDgeWm8rf8EsChUL52/v9S36W8hNOUg12uvKnw4jQRaFbXeYlFVQBXEDAVQQPC90gnP6j+59QqGACjaLUPTcASqxRm9wmhHacEpWuWZudMKSAHYBDCjoK1XyYRvQ4Nq/16rtInKC9YmgrDH4DQt3nmT7wnXTI/8bP77mqCeN9LMgPc2zK5ikTYDgIGxznVsrUrdraO/nxkDATua8F4f2/uFQDvPNt/sCOofKzCfYzDPCbZBsUzCMH87vD+8Q+2/Ya4VvrvsnZLqzJZTa1sVxNTWXl4rRXjcyb7+pXAXpIWh9fXWb2OsPCpUhbuSO6AG07fzGt0TjFnRiPryuBcelWlmBnzfCgAhbUpz7nOmY1Zoniti3Nh4va/IYMpEBdSsVkfRdsYyj3HgKyFsGq/c609C+Fq98/7s1rDV6Sgggn82cYF5yOzoA3HQqYKgIBCIAVMUFUTlWeW28QL7o/0eBg+3vdoYsr+mIOqKqfVbnOdXf6cjG7aGCoAVoIR47pRZ8Wg7a6DfTMv+f21zD7AqKlvbPH1lBqVCVkUhNiGZeBMJANLN7F4zwkrI0bSH+la2Dq3akVVyY3cRBJwrBhVTiFgjEEOh/m8BNyuXfaCFCaqGgqoWX6QZOiip49lWjhKWrQIKVDJSEcisYu4DDsFAFGqO2x4TUw/iyAs8PSY5FkQwufmdPeGZ28p/b5h4Ykxz/+bv8qTGN1kT3RPYMyO/1odZk53H6P9qm+66NX1dWM59fE752Rt3Xtx7ysc8N/H7nkWWlau53va+KrLWMFvjRAXQOrwTa7iPo8NMlBRDIhPGUSp1A2DTn9UEK4iAAhCxQZ8gKJPXBUABJkOLZktgb/x79GqjYHLNfxSsswU1r50oWdgSEbgqqJQGTyONUeGoFNtYwWTjCFSGbFyhWFxTfEspm/1wrRjzTmveBbNJXqcBheAYmf41y/uasra3/vbW3VzHTMvn6txT6vO7e0oSkYllq36iV7NG99EuU8jzL3tr6fr6ykq8CMAcKJz9VsqolI3rzAR8h3ODp1xtbmo31cvhMnE5owoqsH0d3xQXiswmdCnWGCPTTQyuAMiUDQKgLmQ095XZ+6qg+JYJpBVFGUowxKMUkzv/95/8pX7/4QkhJ4ioidxOmHGSQghSYah0eLpbZUGtELCjRSUiwySISIOqjCgMWgVS1HjgCxbSvCGfExTXBNJLdcwIynObbG9T7v39nNBS1YHpvDS211gIc5tWX0W2hnLbuW+zNq1qPr4ZVZqZRP4urJECcphmHEuMdc96nJ/tMaN5jOH7yYx2VQErd0WOo/7om6JCmwAd90B3pWRov/kl4T4qOSDDkM/NRVi6ZhGq+U5daMRzpe13slZ7v3Rl7arSE4xIFYsSVtrS7traCat+T2EUEYN+YXt2rkMIKDL2Y0C20t/zGHM7AFDR3wtFZob2e/tGiwUE9ZiFC8StHfJxaWPS1wTnho54/T7zAQKQBjFbPdT6l+v+MZb+3L9X8zCdLXvFrKeN81wgsu7Wfe2315fRrXOtzAbHHo1iTVwzlKKEsSUiIFGgLAArwi8NUQgTStRfGaCKsMzD1ZRqTOMQiIxK+NB/MV5Uw1L3dffV/Q3o9//kV/rD50/NyjVfxoJw2I/N5UaTX5byoM15zbyMgwdtfh8I6Bs2W6/hN35pop+zhK6V1wjt54T5a0soHM9ZJdf695JluCfA94RTfm8PAh3/rSDZh/ArdFN/CLcfO769cb625LHQaoxYkqXJuq9sKfcgw12robpFerW4AlBXEyBADzgiMYU0aFmlv7PXfw8SU1UoFWMKw0s9gKYQYxUPcFETeCJiAswtZVnrMJ6ahlHE+Alr32t7/qtdRqvjs7xf83Ol5+sJATArUeLKBteX/b5mQcjgzmBFo0P0R1Ud5YhOCkDF4l1gwUvstA2FS0jAKxrysDo8nvuf1926rm3tz+O8XkKR238nIzF778y/7wnd1ygLe3t4o0BtlLoIwsxC+opynurtz9mV1+v9v1auGTYZ7Zp54WuVAwsq9OAw7YGm196N9rvxYrFMWalQdSUV6AIdQEYdBABE8ObNPej3/+Sv9MOX4zCQHChFym6ed8FrBDSfiICASThbKz7RO9bStXItsCcLj2vCNYSdqjZLa45mvKbp/vcQtm0MLyyA5wTmc8J2v2y1yWxlgsnmaNoo5Eyf2YSFEtu/UxSliABlAdfaNUzaWtLZ6jQh1xGUVYGFzA8yjCdZSHtj3at/jx7DxkR1oeUQo0XnQIWbVRpBSCLShJeuRkeBtv5zkY5WKVvwA2BqLCXhWmkUPqwWM6HVFFmIRWQKtf6Y/9iEgvU5lFJpwlk19pa2+VDVbkXuWZBJEKlqE24igkWpCeF4T5VQiN1K27H+kpKRy64QnlxGpGgWQ363vQ/sCu3ddR7jCkRAFLpwV1rS89zHmSbtmWrv8xUrfFZQ6sYruC0NGUj86TVIw6ZvaX1HH/YQgfn3jNQJtKEFWUH+MVbrc3xnVrpfY2hcQxj+e5VZybvWl725sSJNeDZDRSs0R+snIdz4kwKCCFQWU6gbD9P2+6Y7LmMf7u4tMGujGbGiBT6xO/uJWwQfNKJABYaMs2++AiIL5W/MWoE9YbFrqfnRBV6KbTYd/an7Gpb9XUppfwtGSHWP6HmyAhqfBfNeX/eez78/p5HFxrgmcPO/96w1733+qsO8TuJCBGJuke6leGReC7RSqEkDsH+kWCwiMkLwmaF6Abj4/BN60BaQ55OZUS8rChcgje/ggm5ZE6MMBpgZJdCEYht3KHG8ZWSDIhb+G4tDMW3bpA1AAJdi/RYFisGTFHN/qaaocIddwQqVAtAIqZeFIBGU4rEPSh5sQmunRVWAi3NEwkKL9aeNR/376sEw2gJ4VD0CQyMw8QIId4WBndZQj14FoCkqMzEGcpoUMIT6iQd7QCACVKoJtEAOnkFJYr81BhWrjzuCoFVAC4OYQMF10lRnQdmsyiyw9/ZZOylCILE+Qmz1tYA4yo1YmzbfU/Cm/00BQTtMD+1rMPrUqlIYrP2Sjk4EFMbqBjgrAIWfGBRQg8KfQYya5e79I4ClH4sBWxCPUpwqCf4LrOgoRSHzfRof7LEJRq5QrqwvK+kQu9AJBlT0NZBjDGbeFfzTUE5pxs8en57LjCq8DknILs8gGQ3rtlnJs3KyM9ZKbHo2OwDl4xPlcVkFy2Ee1nIcTTL5uPp+7ftOlcDOpGOta6kgh6/p9//kr/SHz09OkG5KhxBrRcaNsgeFzMR4rlyzBufnLdQ82vENttePPcFp0Xlb/9xz1u9e5N41OK3D752BiQgKShKeFvEd/hWA3RfhMJgyhFaIC7siGWrpvptAHzQxjYBDRcIq0QGmHJWhkY7k/hCrM/n43fexB7VFX1r/pFg8gW9qY1ZujWKFYtndNM3d4dZhtrBm6+ml0ufSNykutvBpab9tvklwMDAGMhH8bLoqSjF/8cgcetSn1dGP7HVFp8NvLUq2WWaOKrkHuiFIohDdi5r2bxJE3c5QunCy80YzEwt7+kphs+xVCjJz6/R/nd/uNaVZT75em6IVCoYHZW2t5kDiZhSJwNBm9cGVgabscQGkdjpN6ykUlYYapd83wpLsgBWqQLm0tUwKVBXMAqSNh7sCw6DBoqc4tNW+Mzi0sjSoPFAT90SMyn0ej2jaNwwoQbHuC7Wk9Ga+sBd3sCcQr+3NQF/2fg+5En/H/omLine truncated
|
||||
</defs>
|
||||
<rect id="Rectangle 2" width="1440.000000" height="386.000000" fill="url(#pattern_161_492460)" fill-opacity="1.000000"/>
|
||||
<rect id="Rectangle 2" width="1440.000000" height="386.000000" fill="#000000" fill-opacity="0.300000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 KiB |
@@ -0,0 +1,44 @@
|
||||
.home-map {
|
||||
width: 100%;
|
||||
height: 580px;
|
||||
border-radius: 8px;
|
||||
|
||||
.leaflet-popup {
|
||||
margin-bottom: 30px;
|
||||
&-content-wrapper {
|
||||
border-radius: 8px;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
&-tip,
|
||||
&-close-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-content {
|
||||
padding: 12px 16px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: #3998e8;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.home-map {
|
||||
height: 416px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.home-map {
|
||||
height: 370px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import "./HomeMap.scss";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import {
|
||||
MapContainer,
|
||||
Marker,
|
||||
Popup,
|
||||
TileLayer,
|
||||
useMap,
|
||||
} from "react-leaflet";
|
||||
import geo_green_icon from "./icons/geo-green.svg";
|
||||
import geo_orange_icon from "./icons/geo-orange.svg";
|
||||
import geo_pink_icon from "./icons/geo-pink.svg";
|
||||
import geo_purple_icon from "./icons/geo-purple.svg";
|
||||
import geo_red_icon from "./icons/geo-red.svg";
|
||||
import geo_yellow_icon from "./icons/geo-yellow.svg";
|
||||
import { DivIcon, Icon, LatLngTuple } from "leaflet";
|
||||
import { StaticImageData } from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import L from "leaflet";
|
||||
import { ILocation } from "@/shared/types/location-type";
|
||||
|
||||
interface IData {
|
||||
id: number;
|
||||
location: ILocation[];
|
||||
category: number;
|
||||
}
|
||||
|
||||
interface ILatLng {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
|
||||
interface IHomeMapProps {
|
||||
data: IData[] | undefined;
|
||||
latLng: ILatLng;
|
||||
}
|
||||
|
||||
const HomeMap: React.FC<IHomeMapProps> = ({
|
||||
data,
|
||||
latLng,
|
||||
}: IHomeMapProps) => {
|
||||
const [position, setPosition] = useState<ILatLng>({
|
||||
lat: 42.8746,
|
||||
lng: 74.606,
|
||||
});
|
||||
const createCustomIcon = (icon: StaticImageData) => {
|
||||
const customIcon = new Icon({
|
||||
iconUrl: icon.src,
|
||||
iconSize: [32, 32],
|
||||
});
|
||||
|
||||
return customIcon;
|
||||
};
|
||||
|
||||
const icons: Record<string, DivIcon> = {
|
||||
1: createCustomIcon(geo_red_icon),
|
||||
2: createCustomIcon(geo_pink_icon),
|
||||
3: createCustomIcon(geo_purple_icon),
|
||||
4: createCustomIcon(geo_orange_icon),
|
||||
5: createCustomIcon(geo_green_icon),
|
||||
6: createCustomIcon(geo_yellow_icon),
|
||||
};
|
||||
|
||||
const LocationMark = () => {
|
||||
const map = useMap();
|
||||
|
||||
map.setView(L.latLng(latLng), map.getZoom(), { animate: true });
|
||||
|
||||
useEffect(() => {
|
||||
setPosition(latLng);
|
||||
}, [latLng]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<MapContainer
|
||||
center={position}
|
||||
zoom={14}
|
||||
scrollWheelZoom={false}
|
||||
className="home-map"
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
{data?.map((marker) => (
|
||||
<Marker
|
||||
key={marker.id}
|
||||
icon={icons[marker.category]}
|
||||
position={
|
||||
[
|
||||
+marker.location[0].latitude,
|
||||
+marker.location[0].longitude,
|
||||
] as LatLngTuple
|
||||
}
|
||||
>
|
||||
<Popup>
|
||||
<Link href={`/report/${marker.location[0].id}`}>
|
||||
{marker.location[0].address}
|
||||
</Link>
|
||||
</Popup>
|
||||
</Marker>
|
||||
))}
|
||||
<LocationMark />
|
||||
</MapContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeMap;
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="50" viewBox="0 0 38 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 0C8.52334 0 0 8.74812 0 19.501C0 32.8456 17.0031 48.5901 17.7271 49.4175C18.407 50.1948 19.5942 50.1935 20.2729 49.4175C20.9969 48.5901 38 32.8456 38 19.501C37.9998 8.74812 29.4766 0 19 0Z" fill="#3998E8" fill-opacity="0.7"/>
|
||||
<ellipse cx="18.7867" cy="19" rx="9.88775" ry="10" fill="white"/>
|
||||
<circle cx="18.5" cy="18.5" r="7.5" fill="#8FDE6A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="50" viewBox="0 0 38 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 0C8.52334 0 0 8.74812 0 19.501C0 32.8456 17.0031 48.5901 17.7271 49.4175C18.407 50.1948 19.5942 50.1935 20.2729 49.4175C20.9969 48.5901 38 32.8456 38 19.501C37.9998 8.74812 29.4766 0 19 0Z" fill="#3998E8" fill-opacity="0.7"/>
|
||||
<ellipse cx="18.7867" cy="19" rx="9.88775" ry="10" fill="white"/>
|
||||
<circle cx="18.5" cy="18.5" r="7.5" fill="#FFAC33"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="50" viewBox="0 0 38 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 0C8.52334 0 0 8.74812 0 19.501C0 32.8456 17.0031 48.5901 17.7271 49.4175C18.407 50.1948 19.5942 50.1935 20.2729 49.4175C20.9969 48.5901 38 32.8456 38 19.501C37.9998 8.74812 29.4766 0 19 0Z" fill="#3998E8" fill-opacity="0.7"/>
|
||||
<ellipse cx="18.7867" cy="19" rx="9.88775" ry="10" fill="white"/>
|
||||
<circle cx="18.5" cy="18.5" r="7.5" fill="#C288E2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="50" viewBox="0 0 38 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 0C8.52334 0 0 8.74812 0 19.501C0 32.8456 17.0031 48.5901 17.7271 49.4175C18.407 50.1948 19.5942 50.1935 20.2729 49.4175C20.9969 48.5901 38 32.8456 38 19.501C37.9998 8.74812 29.4766 0 19 0Z" fill="#3998E8" fill-opacity="0.7"/>
|
||||
<ellipse cx="18.7867" cy="19" rx="9.88775" ry="10" fill="white"/>
|
||||
<circle cx="18.5" cy="18.5" r="7.5" fill="#87289D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="50" viewBox="0 0 38 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 0C8.52334 0 0 8.74812 0 19.501C0 32.8456 17.0031 48.5901 17.7271 49.4175C18.407 50.1948 19.5942 50.1935 20.2729 49.4175C20.9969 48.5901 38 32.8456 38 19.501C37.9998 8.74812 29.4766 0 19 0Z" fill="#3998E8" fill-opacity="0.7"/>
|
||||
<ellipse cx="18.7867" cy="19" rx="9.88775" ry="10" fill="white"/>
|
||||
<circle cx="18.5" cy="18.5" r="7.5" fill="#E64452"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="50" viewBox="0 0 38 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 0C8.52334 0 0 8.74812 0 19.501C0 32.8456 17.0031 48.5901 17.7271 49.4175C18.407 50.1948 19.5942 50.1935 20.2729 49.4175C20.9969 48.5901 38 32.8456 38 19.501C37.9998 8.74812 29.4766 0 19 0Z" fill="#3998E8" fill-opacity="0.7"/>
|
||||
<ellipse cx="18.7867" cy="19" rx="9.88775" ry="10" fill="white"/>
|
||||
<circle cx="18.5" cy="18.5" r="7.5" fill="#FED363"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 461 B |
@@ -0,0 +1,95 @@
|
||||
.map-search {
|
||||
margin-bottom: 32px;
|
||||
width: 645px;
|
||||
position: relative;
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
height: 100%;
|
||||
width: 100px;
|
||||
border-radius: 0px 6px 6px 0px;
|
||||
background: rgb(72, 159, 225);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgb(197, 198, 197);
|
||||
border-radius: 6px 0px 0px 6px;
|
||||
|
||||
img {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 10px 0;
|
||||
padding-left: 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__recs {
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
position: absolute;
|
||||
top: 110%;
|
||||
left: 0;
|
||||
z-index: 10000;
|
||||
border-radius: 6px;
|
||||
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 12px 16px 0px rgba(58, 69, 75, 0.1),
|
||||
0px 4px 6px 0px rgba(58, 69, 75, 0.15);
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
ul {
|
||||
li:first-child {
|
||||
button {
|
||||
border-radius: 6px 6px 0px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
button {
|
||||
border-radius: 0px 0px 6px 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 20px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
text-align: start;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.map-search {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import "./MapSearch.scss";
|
||||
import Image from "next/image";
|
||||
import search from "./icons/search.svg";
|
||||
import { IDisplayMap } from "@/shared/types/map-type";
|
||||
|
||||
interface IMapSearchProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
options: IDisplayMap[];
|
||||
setMapSearch: (string: string) => void;
|
||||
setLatLng: ({ lat, lng }: { lat: number; lng: number }) => void;
|
||||
}
|
||||
|
||||
const MapSearch: React.FC<IMapSearchProps> = ({
|
||||
name,
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
setMapSearch,
|
||||
setLatLng,
|
||||
}: IMapSearchProps) => {
|
||||
const handleSubmit = (
|
||||
display_name: string,
|
||||
latLng: { lat: number; lng: number }
|
||||
) => {
|
||||
setMapSearch(display_name);
|
||||
setLatLng(latLng);
|
||||
};
|
||||
return (
|
||||
<div className="map-search">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSubmit(options[0].display_name, {
|
||||
lat: +options[0].lat,
|
||||
lng: +options[0].lon,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div className="map-search__input">
|
||||
<Image src={search} alt="Search Icon" />
|
||||
<input
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
name={name}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<button type="submit">Поиск</button>
|
||||
</form>
|
||||
|
||||
{options.length !== 0 ? (
|
||||
<div className="map-search__recs">
|
||||
<ul>
|
||||
{options.map((opt) => (
|
||||
<li key={opt.place_id}>
|
||||
<button
|
||||
onClick={() => {
|
||||
setMapSearch(opt.display_name);
|
||||
setLatLng({
|
||||
lat: +opt.lat,
|
||||
lng: +opt.lon,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{opt.display_name}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapSearch;
|
||||
@@ -0,0 +1,15 @@
|
||||
<svg width="24.000000" height="24.959961" viewBox="0 0 24 24.96" 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="clip18_5000">
|
||||
<rect id="search" width="24.000000" height="24.959999" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="search" width="24.000000" height="24.959999" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip18_5000)">
|
||||
<path id="Vector" d="M11 19.7598C6.58154 19.7598 3 16.0352 3 11.4404C3 6.84473 6.58154 3.12012 11 3.12012C15.4185 3.12012 19 6.84473 19 11.4404C19 16.0352 15.4185 19.7598 11 19.7598Z" stroke="#C5C6C5" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
<path id="Vector" d="M21 21.8408L16.6499 17.3164" stroke="#C5C6C5" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 932 B |
@@ -0,0 +1,67 @@
|
||||
.map-section {
|
||||
padding: 110px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__header {
|
||||
margin-bottom: 50px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
&__categories {
|
||||
margin-bottom: 60px;
|
||||
display: flex;
|
||||
gap: 90px;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 22px;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.map-section {
|
||||
&__header {
|
||||
margin-bottom: 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
&__categories {
|
||||
margin-bottom: 40px;
|
||||
gap: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.map-section {
|
||||
&__header {
|
||||
margin-bottom: 25px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__categories {
|
||||
margin-bottom: 25px;
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
"use client";
|
||||
|
||||
import "./MapSection.scss";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
ROAD_TYPES,
|
||||
ROAD_TYPES_COLORS,
|
||||
} from "@/shared/variables/road-types";
|
||||
import HomeMap from "./HomeMap/HomeMap";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMapStore } from "./mapSectionStore";
|
||||
import Switch from "./Switch/Switch";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
||||
import Paragraph from "@/shared/ui/components/Paragraph/Paragraph";
|
||||
import { useDebounce } from "use-debounce";
|
||||
import MapSearch from "./MapSearch/MapSearch";
|
||||
|
||||
interface IMapSectionProps {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface ILatLng {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
|
||||
const MapSection: React.FC<IMapSectionProps> = ({
|
||||
categories = "1,2,3,4,5,6",
|
||||
queryMap,
|
||||
queryRating,
|
||||
}: IMapSectionProps) => {
|
||||
const [mapSearch, setMapSearch] = useState<string>(queryMap || "");
|
||||
const [latLng, setLatLng] = useState<ILatLng>({
|
||||
lat: 42.8746,
|
||||
lng: 74.606,
|
||||
});
|
||||
const [query] = useDebounce(mapSearch, 500);
|
||||
const data = useMapStore(useShallow((state) => state.data));
|
||||
const searchedData = useMapStore(
|
||||
useShallow((state) => state.searchData)
|
||||
);
|
||||
|
||||
const getReports = useMapStore(
|
||||
useShallow((state) => state.getReports)
|
||||
);
|
||||
const getLocations = useMapStore(
|
||||
useShallow((state) => state.getLocations)
|
||||
);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
getReports(categories);
|
||||
}, [categories]);
|
||||
|
||||
useEffect(() => {
|
||||
router.push(
|
||||
`/?тип-дороги=${categories}${
|
||||
mapSearch ? `&поиск-на-карте=${mapSearch}` : ""
|
||||
}${queryRating ? `&поиск-рейтинг=${queryRating}` : ""}`,
|
||||
{
|
||||
scroll: false,
|
||||
}
|
||||
);
|
||||
|
||||
getLocations(query);
|
||||
}, [categories, query, queryRating]);
|
||||
|
||||
const setSearchParams = (category: string) => {
|
||||
const availableCategories = ["1", "2", "3", "4", "5", "6"];
|
||||
|
||||
if (!categories || !availableCategories.includes(category))
|
||||
return categories;
|
||||
|
||||
if (categories?.includes(category)) {
|
||||
const updatedCategories = categories
|
||||
?.replace(category + ",", "")
|
||||
.replace("," + category, "")
|
||||
.replace(category, "");
|
||||
|
||||
return updatedCategories;
|
||||
} else {
|
||||
const newValue = category + ",";
|
||||
const updatedCategories = newValue + categories;
|
||||
return updatedCategories;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="map-section">
|
||||
<div className="map-section__header">
|
||||
<Typography element="h3">Карта дорог</Typography>
|
||||
<Paragraph>
|
||||
Будьте в курсе последних новостей о дорожном движении,
|
||||
строительствах и мероприятиях!
|
||||
</Paragraph>
|
||||
</div>
|
||||
|
||||
<MapSearch
|
||||
setLatLng={setLatLng}
|
||||
setMapSearch={setMapSearch}
|
||||
options={searchedData}
|
||||
onChange={(e) => setMapSearch(e.target.value)}
|
||||
name="map-search"
|
||||
value={mapSearch}
|
||||
placeholder="Введите город, село или регион"
|
||||
/>
|
||||
<div className="map-section__categories">
|
||||
<ul className="map-section__categories_left">
|
||||
{[1, 2, 3].map((sw) => (
|
||||
<li key={sw}>
|
||||
<Switch
|
||||
href={`/?тип-дороги=${setSearchParams(
|
||||
sw.toString()
|
||||
)}`}
|
||||
color={ROAD_TYPES_COLORS[sw]}
|
||||
defaultState={categories.includes(sw.toString())}
|
||||
/>
|
||||
<label>{ROAD_TYPES[sw]}</label>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="map-section__categories_right">
|
||||
{[4, 5, 6].map((sw) => (
|
||||
<li key={sw}>
|
||||
<Switch
|
||||
href={`/?тип-дороги=${setSearchParams(
|
||||
sw.toString()
|
||||
)}`}
|
||||
color={ROAD_TYPES_COLORS[sw]}
|
||||
defaultState={categories.includes(sw.toString())}
|
||||
/>
|
||||
<label>{ROAD_TYPES[sw]}</label>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<HomeMap data={data?.results} latLng={latLng} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapSection;
|
||||
@@ -0,0 +1,21 @@
|
||||
.switch-active,
|
||||
.switch-passive {
|
||||
padding: 2px;
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
border-radius: 33px;
|
||||
|
||||
span {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-passive {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import "./Switch.scss";
|
||||
import Link from "next/link";
|
||||
|
||||
interface ISwitchProps {
|
||||
defaultState?: boolean;
|
||||
color?: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const Switch: React.FC<ISwitchProps> = ({
|
||||
defaultState,
|
||||
color,
|
||||
href,
|
||||
}: ISwitchProps) => {
|
||||
const [toggleSwitch, setToggleSwitch] = useState<boolean>(
|
||||
defaultState || false
|
||||
);
|
||||
return (
|
||||
<Link
|
||||
scroll={false}
|
||||
href={href}
|
||||
onClick={() => setToggleSwitch((prev) => !prev)}
|
||||
style={{
|
||||
backgroundColor: !toggleSwitch
|
||||
? "rgb(50, 48, 58)"
|
||||
: color
|
||||
? color
|
||||
: "rgb(230, 68, 82)",
|
||||
}}
|
||||
className={toggleSwitch ? "switch-active" : "switch-passive"}
|
||||
>
|
||||
<span />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default Switch;
|
||||
@@ -0,0 +1,63 @@
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { IFetch } from "@/shared/types/fetch-type";
|
||||
import { IList } from "@/shared/types/list-type";
|
||||
import { IDisplayMap } from "@/shared/types/map-type";
|
||||
import { IReport } from "@/shared/types/report-type";
|
||||
import axios from "axios";
|
||||
import { create } from "zustand";
|
||||
|
||||
interface IFetchReports extends IList {
|
||||
results: IReport[];
|
||||
}
|
||||
|
||||
interface IMapStore extends IFetch {
|
||||
data: IFetchReports;
|
||||
searchData: IDisplayMap[];
|
||||
getReports: (categories: string) => Promise<void>;
|
||||
getLocations: (query: string) => void;
|
||||
}
|
||||
|
||||
export const useMapStore = create<IMapStore>((set) => ({
|
||||
data: {
|
||||
count: 0,
|
||||
previous: null,
|
||||
next: null,
|
||||
results: [],
|
||||
},
|
||||
searchData: [],
|
||||
isLoading: false,
|
||||
error: "",
|
||||
getReports: async (categories: string = "1,2,3,4,5,6") => {
|
||||
try {
|
||||
set({ isLoading: true });
|
||||
|
||||
const response = await apiInstance.get<IFetchReports>(
|
||||
`/report/?category=${categories}`
|
||||
);
|
||||
|
||||
set({ data: response.data });
|
||||
} catch (error: any) {
|
||||
set({ error: error.message });
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
getLocations: async (query: string = "") => {
|
||||
const params: Record<string, any> = {
|
||||
q: query,
|
||||
format: "json",
|
||||
addressdetails: 1,
|
||||
polygon_geojson: 0,
|
||||
};
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
const url = `https://nominatim.openstreetmap.org/search?${queryString}`;
|
||||
|
||||
const response = await axios.get<IDisplayMap[]>(url);
|
||||
|
||||
const inKG = response.data.filter((location) => {
|
||||
return location.address.country_code.toLowerCase() === "kg";
|
||||
});
|
||||
|
||||
set({ searchData: inKG });
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,22 @@
|
||||
@import "../../../shared/ui/variables.scss";
|
||||
|
||||
.nav-auth-profile-lg,
|
||||
.nav-auth-signin-lg {
|
||||
width: fit-content;
|
||||
padding: 8px 16px;
|
||||
background-color: $light-blue;
|
||||
border-radius: 5px;
|
||||
font-weight: 900;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-auth-profile-sm,
|
||||
.nav-auth-signin-sm {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.nav-auth-profile-sm_active,
|
||||
.nav-auth-signin-sm_active {
|
||||
width: fit-content;
|
||||
color: $light-blue;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Link from "next/link";
|
||||
import "./NavAuth.scss";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
interface INavAuthProps {
|
||||
responsible?: boolean;
|
||||
setOpenMenu: (boolean: boolean) => void;
|
||||
}
|
||||
|
||||
const NavAuth: React.FC<INavAuthProps> = ({
|
||||
responsible,
|
||||
setOpenMenu,
|
||||
}: INavAuthProps) => {
|
||||
const session = useSession();
|
||||
const auth = session.status === "authenticated" ? true : false;
|
||||
const pathname = usePathname();
|
||||
return (
|
||||
<>
|
||||
{auth ? (
|
||||
<Link
|
||||
onClick={() => setOpenMenu(false)}
|
||||
href="/profile/personal"
|
||||
className={`nav-auth-profile-${
|
||||
responsible
|
||||
? `sm${pathname === "/profile" ? "_active" : ""}`
|
||||
: "lg"
|
||||
}`}
|
||||
>
|
||||
Профиль
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
onClick={() => setOpenMenu(false)}
|
||||
href="/sign-in"
|
||||
className={`nav-auth-signin-${
|
||||
responsible
|
||||
? `sm${pathname === "/sign-in" ? "_active" : ""}`
|
||||
: "lg"
|
||||
}`}
|
||||
>
|
||||
Войти
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavAuth;
|
||||
@@ -0,0 +1,36 @@
|
||||
.nav-language {
|
||||
position: relative;
|
||||
&__btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
&__select {
|
||||
min-width: 130px;
|
||||
position: absolute;
|
||||
top: 120%;
|
||||
}
|
||||
|
||||
&__option,
|
||||
&__option_active {
|
||||
padding: 10px 14px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
font-weight: 900;
|
||||
justify-content: flex-start;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&__option_active {
|
||||
background-color: rgb(249, 250, 251);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__option:last-child,
|
||||
&__option_active:last-child {
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import "./NavLanguage.scss";
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import globus from "./icons/globus.svg";
|
||||
import chevron from "./icons/chevron-down.svg";
|
||||
import check from "./icons/check.svg";
|
||||
|
||||
const NavLanguage = () => {
|
||||
const [language, setLanguage] = useState<string>("ru");
|
||||
const [openMenu, setOpenMenu] = useState<boolean>(false);
|
||||
const LANGUAGES = [
|
||||
{ id: 1, language: "Русский", index: "ru" },
|
||||
{ id: 2, language: "Кыргызча", index: "kg" },
|
||||
{ id: 3, language: "English", index: "en" },
|
||||
];
|
||||
return (
|
||||
<div className="nav-language">
|
||||
<button
|
||||
onClick={() => setOpenMenu((prev) => !prev)}
|
||||
className="nav-language__btn"
|
||||
>
|
||||
<Image src={globus} alt="globus icon" />
|
||||
<Image src={chevron} alt="chevron icon" />
|
||||
</button>
|
||||
{openMenu && (
|
||||
<div className="nav-language__select">
|
||||
{LANGUAGES.map((lang) => (
|
||||
<button
|
||||
className={`nav-language__option${
|
||||
language === lang.index ? "_active" : ""
|
||||
}`}
|
||||
onClick={() => setLanguage(lang.index)}
|
||||
key={lang.id}
|
||||
>
|
||||
{lang.language}
|
||||
{lang.index === language ? (
|
||||
<Image src={check} alt="check icon" />
|
||||
) : null}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavLanguage;
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg width="15.007812" height="10.834961" viewBox="0 0 15.0078 10.835" 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="M14.1699 0.835938L5.00391 10.0029L0.836914 5.83594" stroke="#3695D8" stroke-opacity="1.000000" stroke-width="1.666667" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
@@ -0,0 +1,14 @@
|
||||
<svg width="10.000000" height="6.000000" viewBox="0 0 10 6" 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="clip1290_49928">
|
||||
<rect id="IMAGE" width="10.000000" height="6.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#clip1290_49928)">
|
||||
<path id="Vector" d="M0.246094 1.47363C-0.0820312 1.13672 -0.0820312 0.589844 0.246094 0.25293C0.574219 -0.0839844 1.10645 -0.0839844 1.43457 0.25293L5.59375 4.52637C5.92285 4.86328 5.92285 5.41016 5.59375 5.74707C5.26562 6.08398 4.73438 6.08398 4.40527 5.74707L0.246094 1.47363Z" fill="#66727F" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
<path id="Vector" d="M8.56543 0.25293C8.89355 -0.0839844 9.4248 -0.0839844 9.75293 0.25293C10.082 0.589844 10.082 1.13672 9.75293 1.47363L5.59375 5.74707C5.26562 6.08398 4.7334 6.08398 4.40527 5.74707C4.07715 5.41016 4.07715 4.86328 4.40527 4.52637L8.56543 0.25293Z" fill="#66727F" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,16 @@
|
||||
<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="clip1290_49924">
|
||||
<rect id="globe" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="globe" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip1290_49924)">
|
||||
<path id="Vector" d="M12 22C6.47705 22 2 17.5225 2 12C2 6.47754 6.47705 2 12 2C17.5229 2 22 6.47754 22 12C22 17.5225 17.5229 22 12 22Z" stroke="#66727F" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
<path id="Vector" d="M2 12L22 12" stroke="#66727F" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M16 12C15.9229 15.708 14.5015 19.2617 12 22C9.49854 19.2617 8.07715 15.708 8 12C8.07715 8.29199 9.49854 4.73828 12 2C14.5015 4.73828 15.9229 8.29199 16 12Z" stroke="#66727F" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,25 @@
|
||||
@import "@/shared/ui/variables.scss";
|
||||
|
||||
.nav-menu {
|
||||
padding: 48px 30px 30px 30px;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
min-height: 500px;
|
||||
position: fixed;
|
||||
top: 72px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 26px;
|
||||
background-color: white;
|
||||
|
||||
a {
|
||||
justify-content: flex-start;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__link_active {
|
||||
color: $light-blue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { LINKS } from "@/shared/variables/links";
|
||||
import "./NavMenu.scss";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import NavAuth from "../NavAuth/NavAuth";
|
||||
|
||||
interface INavMenuProps {
|
||||
setOpenMenu: (boolean: boolean) => void;
|
||||
}
|
||||
|
||||
const NavMenu: React.FC<INavMenuProps> = ({
|
||||
setOpenMenu,
|
||||
}: INavMenuProps) => {
|
||||
const pathname = usePathname();
|
||||
return (
|
||||
<nav className="nav-menu">
|
||||
{LINKS.map((link) => (
|
||||
<Link
|
||||
onClick={() => setOpenMenu(false)}
|
||||
className={`nav-menu__link${
|
||||
pathname === link.pathname ? "_active" : ""
|
||||
}`}
|
||||
href={link.pathname}
|
||||
key={link.id}
|
||||
>
|
||||
{link.pagename}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
<NavAuth setOpenMenu={setOpenMenu} responsible />
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavMenu;
|
||||
@@ -0,0 +1,86 @@
|
||||
@import "../../shared/ui/variables.scss";
|
||||
|
||||
.navbar {
|
||||
padding: 0 90px;
|
||||
width: 100%;
|
||||
height: 78px;
|
||||
position: fixed;
|
||||
z-index: 10005;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
&__links {
|
||||
height: 40%;
|
||||
display: flex;
|
||||
gap: 60px;
|
||||
}
|
||||
|
||||
&__link,
|
||||
&__link_active {
|
||||
color: $black;
|
||||
opacity: 0.5;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__link_active {
|
||||
opacity: 1;
|
||||
font-weight: 900;
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
|
||||
&__lang-and-auth {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
&__menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.navbar {
|
||||
padding: 0 30px;
|
||||
|
||||
&__links {
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
&__lang-and-auth {
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.navbar {
|
||||
height: 72px;
|
||||
|
||||
&__links,
|
||||
&__lang-and-auth {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__menu {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.navbar {
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import "./Navbar.scss";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import logo from "@/shared/assets/logo.svg";
|
||||
import { LINKS } from "@/shared/variables/links";
|
||||
import NavLanguage from "./NavLanguage/NavLanguage";
|
||||
import NavAuth from "./NavAuth/NavAuth";
|
||||
import menu from "./icons/menu.svg";
|
||||
import cross from "./icons/cross.svg";
|
||||
|
||||
import NavMenu from "./NavMenu/NavMenu";
|
||||
import { useState } from "react";
|
||||
|
||||
const Navbar = () => {
|
||||
const pathname = usePathname();
|
||||
const [openMenu, setOpenMenu] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<section className="navbar">
|
||||
<Link href="/">
|
||||
<Image src={logo} alt="Logo" />
|
||||
</Link>
|
||||
|
||||
<nav className="navbar__links">
|
||||
{LINKS.map((link) => (
|
||||
<Link
|
||||
className={`navbar__link${
|
||||
pathname === link.pathname ? "_active" : ""
|
||||
}`}
|
||||
key={link.id}
|
||||
href={link.pathname}
|
||||
>
|
||||
{link.pagename}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="navbar__lang-and-auth">
|
||||
<NavLanguage />
|
||||
<NavAuth setOpenMenu={setOpenMenu} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setOpenMenu((prev) => !prev)}
|
||||
className="navbar__menu"
|
||||
>
|
||||
<Image src={openMenu ? cross : menu} alt="menu icon" />
|
||||
</button>
|
||||
|
||||
{openMenu && <NavMenu setOpenMenu={setOpenMenu} />}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
@@ -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="clip1256_72110">
|
||||
<rect id="x" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="x" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip1256_72110)">
|
||||
<path id="Vector" d="M18 6L6 18" stroke="#32303A" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M6 6L18 18" stroke="#32303A" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 780 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="20.000000" height="14.000000" viewBox="0 0 20 14" 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="M1 7L19 7M1 1L19 1M1 13L19 13" stroke="#344054" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 353 B |
@@ -0,0 +1,57 @@
|
||||
@import "../../shared/ui/variables.scss";
|
||||
|
||||
.news-section {
|
||||
padding: 60px 90px 100px 90px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&__list {
|
||||
margin-bottom: 40px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
&__read-more-btn {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
gap: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: $light-blue;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.news-section {
|
||||
padding: 60px 30px 100px 30px;
|
||||
|
||||
&__list {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.news-section {
|
||||
padding: 60px 16px 100px 16px;
|
||||
|
||||
&__list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
&__read-more-btn {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
background: rgb(204, 234, 251);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import "./NewsSection.scss";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { getNews } from "./newsSectionStore";
|
||||
import arrow_icon from "./icons/arrow-right.svg";
|
||||
import NewsCard from "@/entities/NewsCard/NewsCard";
|
||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
||||
|
||||
const NewsSection = async () => {
|
||||
const news = await getNews();
|
||||
return (
|
||||
<section className="news-section">
|
||||
<Typography element="h3">Новости</Typography>
|
||||
<ul className="news-section__list">
|
||||
{news?.map((article) => (
|
||||
<li key={article.id} className="news-section__card">
|
||||
<NewsCard
|
||||
id={article.id}
|
||||
title={article.title}
|
||||
image={article.image}
|
||||
description={article.description}
|
||||
date={article.created_at}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link href="/news" className="news-section__read-more-btn">
|
||||
Читать
|
||||
<Image src={arrow_icon} alt="Arrow Right Icon" />
|
||||
</Link>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewsSection;
|
||||
@@ -0,0 +1,15 @@
|
||||
<svg width="20.000000" height="20.000000" viewBox="0 0 20 20" 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="clip21_5866">
|
||||
<rect id="arrow-right" width="20.000000" height="20.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="arrow-right" width="20.000000" height="20.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip21_5866)">
|
||||
<path id="Vector" d="M4.16699 10L15.834 10" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M10 4.16602L15.833 9.99902L10 15.833" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 831 B |
@@ -0,0 +1,22 @@
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { IList } from "@/shared/types/list-type";
|
||||
import { INews } from "@/shared/types/news-type";
|
||||
import axios, { AxiosError } from "axios";
|
||||
|
||||
interface IFetchNews extends IList {
|
||||
results: INews[];
|
||||
}
|
||||
|
||||
export const getNews = async () => {
|
||||
try {
|
||||
const data = (await apiInstance.get<IFetchNews>("/news/")).data;
|
||||
|
||||
return data.results.slice(0, 4);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An error ocured");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
.change-password {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 11000;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&__error {
|
||||
color: rgb(240, 68, 56);
|
||||
}
|
||||
|
||||
&__success {
|
||||
color: rgb(72, 159, 225);
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 24px;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
border-radius: 12px;
|
||||
background-color: #fff;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 9px;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
line-height: 28px;
|
||||
color: rgb(16, 24, 40);
|
||||
}
|
||||
|
||||
a {
|
||||
align-self: flex-end;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 180%;
|
||||
color: rgb(72, 159, 225);
|
||||
}
|
||||
}
|
||||
&__btns {
|
||||
margin-top: 17px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
button {
|
||||
padding: 10px 18px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 24px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
}
|
||||
|
||||
button:first-child {
|
||||
border: 1px solid rgb(72, 159, 225);
|
||||
background-color: rgb(72, 159, 225);
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
button:last-child {
|
||||
border: 1px solid rgb(208, 213, 221);
|
||||
background-color: rgb(255, 255, 255);
|
||||
color: rgb(52, 64, 84);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import "./ChangePassword.scss";
|
||||
import Link from "next/link";
|
||||
import ChangePasswordInput from "./ChangePasswordInput/ChangePasswordInput";
|
||||
import { useState } from "react";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { useSession } from "next-auth/react";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
|
||||
interface IChangePasswordProps {
|
||||
closeWindow: (bool: boolean) => void;
|
||||
}
|
||||
|
||||
const ChangePassword: React.FC<IChangePasswordProps> = ({
|
||||
closeWindow,
|
||||
}: IChangePasswordProps) => {
|
||||
const session = useSession();
|
||||
const [oldPassword, setOldPassword] = useState<string>("");
|
||||
const [newPassword, setNewPassword] = useState<string>("");
|
||||
const [confirmNewPassword, setConfirmNewPassword] =
|
||||
useState<string>("");
|
||||
|
||||
const [warningOldPassword, setWarningOldPassword] =
|
||||
useState<string>("");
|
||||
const [warningNewPassword, setWarningNewPassword] =
|
||||
useState<string>("");
|
||||
const [warningConfirmNewPassword, setWarningConfirmNewPassword] =
|
||||
useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
const [success, setSuccess] = useState<boolean>(false);
|
||||
|
||||
const changePass = async () => {
|
||||
if (!oldPassword.trim()) {
|
||||
setError("");
|
||||
setWarningNewPassword("");
|
||||
setWarningConfirmNewPassword("");
|
||||
setWarningOldPassword("Пожалуйста введите старый пароль");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newPassword.trim()) {
|
||||
setError("");
|
||||
setWarningConfirmNewPassword("");
|
||||
setWarningOldPassword("");
|
||||
setWarningNewPassword("Пожалуйста введите новый пароль");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirmNewPassword.trim()) {
|
||||
setError("");
|
||||
setWarningNewPassword("");
|
||||
setWarningOldPassword("");
|
||||
setWarningConfirmNewPassword(
|
||||
"Пожалуйста потвердите новый пароль"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirmNewPassword !== newPassword) {
|
||||
setError("");
|
||||
setWarningNewPassword("");
|
||||
setWarningOldPassword("");
|
||||
setWarningConfirmNewPassword("Пароли отличаются");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
old_password: oldPassword,
|
||||
new_password1: newPassword,
|
||||
new_password2: confirmNewPassword,
|
||||
};
|
||||
|
||||
const Authorization = `Bearer ${session.data?.access_token}`;
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
setError("");
|
||||
setWarningNewPassword("");
|
||||
setWarningOldPassword("");
|
||||
setWarningConfirmNewPassword("");
|
||||
setLoader(true);
|
||||
const res = await apiInstance.patch(
|
||||
"/users/change_password/",
|
||||
data,
|
||||
config
|
||||
);
|
||||
|
||||
if ([200, 201].includes(res.status)) return setSuccess(true);
|
||||
} catch (error: any) {
|
||||
setError(error.message);
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => closeWindow(false)}
|
||||
className="change-password"
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="change-password__wrapper"
|
||||
>
|
||||
<h4>Изменить пароль</h4>
|
||||
<ChangePasswordInput
|
||||
onChange={(e) => setOldPassword(e.target.value)}
|
||||
value={oldPassword}
|
||||
placeholder="Введите старый пароль"
|
||||
label="Старый пароль"
|
||||
error={warningOldPassword}
|
||||
/>
|
||||
<Link href="/sign-in/forgot-password">Забыли пароль?</Link>
|
||||
<ChangePasswordInput
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
value={newPassword}
|
||||
placeholder="Введите новый пароль"
|
||||
label="Новый пароль"
|
||||
error={warningNewPassword}
|
||||
/>
|
||||
<ChangePasswordInput
|
||||
onChange={(e) => setConfirmNewPassword(e.target.value)}
|
||||
value={confirmNewPassword}
|
||||
placeholder="Повторите новый пароль"
|
||||
label="Потвердить новый пароль"
|
||||
error={warningConfirmNewPassword}
|
||||
/>
|
||||
{error ? (
|
||||
<p className="change-password__error">{error}</p>
|
||||
) : null}
|
||||
{success ? (
|
||||
<p className="change-password__success">
|
||||
Вы успешно поменяли пароль!
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div className="change-password__btns">
|
||||
<button type="button" onClick={changePass}>
|
||||
{loader ? <Loader /> : "Сохранить"}
|
||||
</button>
|
||||
<button type="button" onClick={() => closeWindow(false)}>
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangePassword;
|
||||
@@ -0,0 +1,43 @@
|
||||
.change-password-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
label {
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 20px;
|
||||
color: rgb(52, 64, 84);
|
||||
}
|
||||
|
||||
&__field,
|
||||
&__field-with-error {
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
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);
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: rgb(240, 68, 56);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import "./ChangePasswordInput.scss";
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import eye_off from "./icons/eye-off.svg";
|
||||
import eye_on from "./icons/eye-on.svg";
|
||||
|
||||
interface IChangePasswordInputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
label: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
const ChangePasswordInput: React.FC<IChangePasswordInputProps> = ({
|
||||
label,
|
||||
error,
|
||||
placeholder,
|
||||
name,
|
||||
onChange,
|
||||
value,
|
||||
}: IChangePasswordInputProps) => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
return (
|
||||
<div className="change-password-input">
|
||||
<label>{label}</label>
|
||||
<div
|
||||
className={`change-password-input__field${
|
||||
error ? "-with-error" : ""
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
name={name}
|
||||
placeholder={placeholder}
|
||||
type={isOpen ? "text" : "password"}
|
||||
/>
|
||||
<button
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
type="button"
|
||||
>
|
||||
<Image src={isOpen ? eye_on : eye_off} alt="Eye Icon" />
|
||||
</button>
|
||||
</div>
|
||||
{error ? <p>{error}</p> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangePasswordInput;
|
||||
@@ -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_50526">
|
||||
<rect id="eye-off" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="eye-off" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip1668_50526)">
|
||||
<path id="Vector" d="M17.9404 17.9404C16.2305 19.2432 14.1494 19.9648 12 20C5 20 1 12 1 12C2.24414 9.68164 3.96875 7.65625 6.05957 6.05957M9.90039 4.24023C10.5879 4.0791 11.293 3.99805 12 4C19 4 23 12 23 12C22.3926 13.1357 21.6689 14.2051 20.8398 15.1904M14.1201 14.1201C13.8457 14.415 13.5137 14.6514 13.1465 14.8154C12.7783 14.9795 12.3809 15.0674 11.9785 15.0742C11.5752 15.0811 11.1748 15.0078 10.8018 14.8564C10.4277 14.7061 10.0889 14.4814 9.80371 14.1963C9.51855 13.9111 9.29395 13.5723 9.14355 13.1982C8.99219 12.8252 8.91895 12.4248 8.92578 12.0215C8.93262 11.6191 9.02051 11.2217 9.18457 10.8535C9.34863 10.4863 9.58496 10.1543 9.87988 9.87988" stroke="#979797" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
<path id="Vector" d="M1 1L23 23" stroke="#979797" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -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 |
@@ -0,0 +1,79 @@
|
||||
.profile-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
|
||||
&__input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
label {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 135%;
|
||||
color: #686b7c;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
input {
|
||||
width: 100%;
|
||||
max-width: 957px;
|
||||
padding: 16px;
|
||||
border: 1px solid rgb(221, 222, 226);
|
||||
border-radius: 8px;
|
||||
background: rgb(255, 255, 255);
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 135%;
|
||||
color: #09090b;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__btn,
|
||||
&__btn_active {
|
||||
margin-top: 50px;
|
||||
width: fit-content;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background-color: rgb(158, 167, 175);
|
||||
cursor: auto;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 19px;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
&__btn_active {
|
||||
background-color: rgb(57, 152, 232);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__logout {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.profile-form {
|
||||
&__btn,
|
||||
&__btn_active {
|
||||
align-self: flex-end;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 150px;
|
||||
}
|
||||
|
||||
&__logout {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
"use client";
|
||||
|
||||
import "./ProfileForm.scss";
|
||||
import Image from "next/image";
|
||||
import pen from "./icons/pen.svg";
|
||||
import eye_off from "./icons/eye-off.svg";
|
||||
import eye_on from "./icons/eye-on.svg";
|
||||
import { useState } from "react";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { AxiosError } from "axios";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import LogoutButton from "@/features/LogoutButton/LogoutButton";
|
||||
import ChangePassword from "./ChangePassword/ChangePassword";
|
||||
|
||||
interface IProfileFormProps {
|
||||
id: number;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
}
|
||||
|
||||
const ProfileForm: React.FC<IProfileFormProps> = ({
|
||||
id,
|
||||
email,
|
||||
first_name,
|
||||
last_name,
|
||||
}: IProfileFormProps) => {
|
||||
const session = useSession();
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
|
||||
const [editFirstName, setEditFirstName] = useState<boolean>(true);
|
||||
const [editLastName, setEditLastName] = useState<boolean>(true);
|
||||
|
||||
const [firstName, setFirstName] = useState<string>(first_name);
|
||||
const [lastName, setLastName] = useState<string>(last_name);
|
||||
|
||||
const [openPopup, setOpenPopup] = useState<boolean>(false);
|
||||
|
||||
const thereAreChanges = () => {
|
||||
if (firstName !== first_name || lastName !== last_name)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const updateProfile: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const data = {
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
};
|
||||
|
||||
const Authorization = `Bearer ${session.data?.access_token}`;
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
setLoader(true);
|
||||
const res = await apiInstance.patch(
|
||||
"/users/profile/update/",
|
||||
data,
|
||||
config
|
||||
);
|
||||
if ([200, 201].includes(res.status)) {
|
||||
router.refresh();
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
setError(error.message);
|
||||
} else {
|
||||
setError("Возникла непредвиденная ошибка");
|
||||
}
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={updateProfile} className="profile-form">
|
||||
<div className="profile-form__input">
|
||||
<label>Имя</label>
|
||||
<div>
|
||||
<input
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
disabled={editFirstName}
|
||||
type="text"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEditFirstName((prev) => !prev)}
|
||||
>
|
||||
<Image src={pen} alt="Pen Icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="profile-form__input">
|
||||
<label>Фамилия</label>
|
||||
<div>
|
||||
<input
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
disabled={editLastName}
|
||||
type="text"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEditLastName((prev) => !prev)}
|
||||
>
|
||||
<Image src={pen} alt="Pen Icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="profile-form__input">
|
||||
<label>Email</label>
|
||||
<div>
|
||||
<input value={email} disabled type="text" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="profile-form__input">
|
||||
<label>Пароль</label>
|
||||
<div>
|
||||
<input
|
||||
value={"*****************************"}
|
||||
disabled={editLastName}
|
||||
type="password"
|
||||
/>
|
||||
<button onClick={() => setOpenPopup(true)} type="button">
|
||||
<Image src={pen} alt="Pen Icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{openPopup && <ChangePassword closeWindow={setOpenPopup} />}
|
||||
|
||||
{error ? <p>{error}</p> : null}
|
||||
|
||||
<button
|
||||
disabled={thereAreChanges()}
|
||||
type="submit"
|
||||
className={`profile-form__btn${
|
||||
thereAreChanges() ? "" : "_active"
|
||||
}`}
|
||||
>
|
||||
{loader ? <Loader /> : "Сохранить изменения"}
|
||||
</button>
|
||||
<LogoutButton className="profile-form__logout" />
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileForm;
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg width="21.908203" height="22.000000" viewBox="0 0 21.9082 22" 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="Vector (Stroke)" d="M14.7539 1.22754C16.3906 -0.40918 19.041 -0.40918 20.6777 1.22754C22.3145 2.86426 22.3145 5.51562 20.6777 7.15234L8.5918 19.2383C8.45508 19.375 8.28516 19.4707 8.09766 19.5166L4.51562 20.3867C4.47852 20.3984 4.44141 20.4072 4.4043 20.4141L2.23047 20.9424C1.875 21.0293 1.5 20.9238 1.24023 20.665C0.982422 20.4062 0.876953 20.0312 0.962891 19.6758L2.38867 13.8076C2.43555 13.6211 2.53125 13.4502 2.66797 13.3145L14.7539 1.22754ZM4.53711 18.2236L7.31641 17.5479L19.1953 5.66895C20.0117 4.85156 20.0117 3.52832 19.1953 2.71094C18.377 1.89355 17.0547 1.89355 16.2363 2.71094L4.35742 14.5889L3.68359 17.3652L4.53711 18.2236Z" fill="#B2B5BE" fill-opacity="1.000000" fill-rule="evenodd"/>
|
||||
<path id="Union" d="M14.5332 4.09473C14.9434 3.68652 15.6074 3.6875 16.0176 4.09766L19.123 7.21191C19.5312 7.62207 19.5312 8.28613 19.1211 8.69531C18.7109 9.10449 18.0469 9.10352 17.6367 8.69336L14.5312 5.57812C14.123 5.16797 14.123 4.50391 14.5332 4.09473ZM8.77734 19.9238C8.77734 19.3447 9.24609 18.875 9.82617 18.875L20.8594 18.875C21.4375 18.875 21.9082 19.3447 21.9082 19.9238C21.9082 20.5029 21.4375 20.9727 20.8594 20.9727L9.82617 20.9727C9.24609 20.9727 8.77734 20.5029 8.77734 19.9238Z" fill="#B2B5BE" fill-opacity="1.000000" fill-rule="evenodd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,72 @@
|
||||
.profile-nav {
|
||||
margin-bottom: 55px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
a:first-child {
|
||||
margin-right: 46px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
color: rgb(151, 151, 151);
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 16px;
|
||||
padding: 2px 10px;
|
||||
border-radius: 16px;
|
||||
background-color: rgb(224, 237, 248);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
color: rgb(23, 92, 211);
|
||||
cursor: auto;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
#profile-nav__link {
|
||||
text-decoration: underline;
|
||||
color: rgb(50, 48, 58);
|
||||
}
|
||||
|
||||
#profile-nav__create-report {
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background-color: rgb(57, 152, 232);
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 19px;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.profile-nav {
|
||||
div {
|
||||
a:first-child {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
button,
|
||||
a:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
"use client";
|
||||
|
||||
import "./ProfileNav.scss";
|
||||
import LogoutButton from "@/features/LogoutButton/LogoutButton";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const ProfileNav = () => {
|
||||
const pathname = usePathname();
|
||||
return (
|
||||
<nav className="profile-nav">
|
||||
<div>
|
||||
<Link
|
||||
id={
|
||||
pathname === "/profile/personal"
|
||||
? "profile-nav__link"
|
||||
: ""
|
||||
}
|
||||
href="/profile/personal"
|
||||
>
|
||||
Личные данные
|
||||
</Link>
|
||||
<Link
|
||||
id={
|
||||
pathname === "/profile/my-reports"
|
||||
? "profile-nav__link"
|
||||
: ""
|
||||
}
|
||||
href="/profile/my-reports"
|
||||
>
|
||||
Мои обращения
|
||||
</Link>
|
||||
<span>3</span>
|
||||
</div>
|
||||
|
||||
{pathname === "/profile/personal" ? (
|
||||
<LogoutButton />
|
||||
) : (
|
||||
<Link id="profile-nav__create-report" href="/create-report">
|
||||
Написать обращение
|
||||
</Link>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileNav;
|
||||
@@ -0,0 +1,134 @@
|
||||
.profile-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
&__wrapper {
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
padding: 0 40px;
|
||||
width: 100%;
|
||||
height: 68px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: rgb(244, 244, 244);
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 160px 271px 300px 164px 100px;
|
||||
justify-content: space-between;
|
||||
|
||||
td {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-color: rgb(244, 244, 244);
|
||||
}
|
||||
|
||||
td,
|
||||
button {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
tr {
|
||||
padding: 0 40px;
|
||||
height: 80px;
|
||||
display: grid;
|
||||
grid-template-columns: 160px 271px 300px 164px 100px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid rgb(241, 244, 249);
|
||||
|
||||
td {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#my-report-date {
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
|
||||
#my-report-location {
|
||||
font-size: 20px;
|
||||
color: rgb(50, 48, 58);
|
||||
}
|
||||
|
||||
#my-report-status {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#my-report-reviews {
|
||||
color: rgb(72, 159, 225);
|
||||
}
|
||||
|
||||
#my-report-likes {
|
||||
color: rgb(74, 192, 63);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
color: rgb(102, 102, 102);
|
||||
}
|
||||
|
||||
a {
|
||||
display: none;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background-color: rgb(57, 152, 232);
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 19px;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
}
|
||||
|
||||
.no-reports-in-profile {
|
||||
border: 1px solid rgb(197, 198, 197);
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.profile-table {
|
||||
a {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import Image from "next/image";
|
||||
import "./ProfileTable.scss";
|
||||
import arrows from "@/shared/icons/arrows.svg";
|
||||
import { IMyReportsList } from "@/shared/types/my-reports";
|
||||
import {
|
||||
REPORT_STATUS,
|
||||
REPORT_STATUS_COLORS,
|
||||
} from "@/shared/variables/report-status";
|
||||
import Link from "next/link";
|
||||
|
||||
interface IProfileTableProps {
|
||||
reports: IMyReportsList;
|
||||
}
|
||||
|
||||
const ProfileTable: React.FC<IProfileTableProps> = ({
|
||||
reports,
|
||||
}: IProfileTableProps) => {
|
||||
const params = [
|
||||
{ param: "Дата", handleClick() {} },
|
||||
{ param: "Адрес" },
|
||||
{ param: "Статус" },
|
||||
{ param: "Комментарии", handleClick() {} },
|
||||
{ param: "Рейтинг", handleClick() {} },
|
||||
];
|
||||
const sliceDate = (date: string) => {
|
||||
return `${date.slice(8, 10)}.${date.slice(5, 7)}.${date.slice(
|
||||
0,
|
||||
4
|
||||
)}`;
|
||||
};
|
||||
return (
|
||||
<div className="profile-table">
|
||||
<div
|
||||
className={`profile-table__wrapper ${
|
||||
reports.results.length ? "" : "no-reports-in-profile"
|
||||
}`}
|
||||
>
|
||||
{reports.results.length ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{params.map((p) => (
|
||||
<td key={p.param}>
|
||||
{p.handleClick ? (
|
||||
<button>
|
||||
{p.param}{" "}
|
||||
<Image src={arrows} alt="Arrows Icon" />
|
||||
</button>
|
||||
) : (
|
||||
p.param
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{reports.results.map((report) => (
|
||||
<tr key={report.id}>
|
||||
<td id="my-report-date">
|
||||
{sliceDate(report.created_at)}
|
||||
</td>
|
||||
<td id="my-report-location">
|
||||
{report.location[0].address}
|
||||
</td>
|
||||
<td
|
||||
id="my-report-status"
|
||||
style={{
|
||||
color: `${REPORT_STATUS_COLORS[report.status]}`,
|
||||
}}
|
||||
>
|
||||
{REPORT_STATUS[report.status]}
|
||||
</td>
|
||||
<td id="my-report-reviews">
|
||||
{report.count_reviews}
|
||||
</td>
|
||||
<td id="my-report-likes">{report.total_likes}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<p className="profile-table__message">
|
||||
Вы пока не оставили ни одного обращения.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Link id="my-reports-link" href="/create-report">
|
||||
Написать обращение
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileTable;
|
||||
@@ -0,0 +1,131 @@
|
||||
@import "../../shared/ui/variables.scss";
|
||||
|
||||
.rating-section {
|
||||
padding: 0 90px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__header {
|
||||
margin-bottom: 50px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
&__table {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
border: 1px solid rgb(213, 213, 213);
|
||||
border-radius: 6px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table {
|
||||
thead {
|
||||
padding: 0 30px;
|
||||
height: 76px;
|
||||
border-bottom: 1px solid rgb(241, 244, 249);
|
||||
tr {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 120px 200px 210px 380px 165px 92px;
|
||||
|
||||
td {
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgb(102, 112, 133);
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
color: rgb(102, 112, 133);
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
padding: 0 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
tr {
|
||||
height: 90px;
|
||||
padding: 10px 0;
|
||||
display: grid;
|
||||
grid-template-columns: 120px 200px 210px 380px 165px 92px;
|
||||
align-items: center;
|
||||
|
||||
td {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#rating-section-date {
|
||||
font-size: 16px;
|
||||
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
|
||||
#rating-section-link {
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: $light-blue;
|
||||
}
|
||||
}
|
||||
|
||||
#rating-section-description {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
#rating-section-reviews,
|
||||
#rating-section-likes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
#rating-section-reviews {
|
||||
color: $light-blue;
|
||||
}
|
||||
|
||||
#rating-section-likes {
|
||||
color: $light-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.rating-section {
|
||||
padding: 0 30px;
|
||||
|
||||
&__header {
|
||||
margin-bottom: 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.rating-section {
|
||||
padding: 0 16px;
|
||||
&__header {
|
||||
margin-bottom: 20px;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
"use client";
|
||||
import "./RatingSection.scss";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import SearchForm from "@/features/SearchForm/SearchForm";
|
||||
import arrow_down_icon from "./icons/arrow-down.svg";
|
||||
import arrow_up_icon from "./icons/arrow-up.svg";
|
||||
import like_icon from "./icons/like.svg";
|
||||
import message_icon from "./icons/message.svg";
|
||||
import { useRatingStore } from "./ratingSectionStore";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
ROAD_TYPES,
|
||||
ROAD_TYPES_COLORS,
|
||||
} from "@/shared/variables/road-types";
|
||||
import RoadType from "@/entities/RoadType/RoadType";
|
||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
||||
import Paragraph from "@/shared/ui/components/Paragraph/Paragraph";
|
||||
import arrows from "../../shared/icons/arrows.svg";
|
||||
|
||||
interface IRatingSectionProps {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
const RatingSection: React.FC<IRatingSectionProps> = ({
|
||||
categories = "1,2,3,4,5,6",
|
||||
queryMap,
|
||||
queryRating,
|
||||
}: IRatingSectionProps) => {
|
||||
const [ratingSearch, setRatingSearch] = useState<string>(
|
||||
queryRating || ""
|
||||
);
|
||||
const router = useRouter();
|
||||
|
||||
const reports = useRatingStore(useShallow((state) => state.data));
|
||||
const getReports = useRatingStore(
|
||||
useShallow((state) => state.getReports)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getReports(ratingSearch);
|
||||
}, []);
|
||||
|
||||
const handleSubmit: React.FormEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
setRatingSearch(formData.get("rating-search") as string);
|
||||
|
||||
router.push(
|
||||
`/?тип-дороги=${categories}${
|
||||
queryMap ? `&поиск-на-карте=${queryMap}` : ""
|
||||
}${ratingSearch ? `&поиск-рейтинг=${ratingSearch}` : ""}`,
|
||||
{
|
||||
scroll: false,
|
||||
}
|
||||
);
|
||||
|
||||
getReports(ratingSearch);
|
||||
};
|
||||
|
||||
const sliceDate = (date: string) => {
|
||||
return `${date.slice(8, 10)}.${date.slice(5, 7)}.${date.slice(
|
||||
0,
|
||||
4
|
||||
)}`;
|
||||
};
|
||||
|
||||
const sliceLocation = (location: string) => {
|
||||
if (location.length > 15) return `${location.slice(0, 15)}...`;
|
||||
|
||||
return location;
|
||||
};
|
||||
|
||||
const sliceDescription = (description: string) => {
|
||||
if (description.length > 49) {
|
||||
return `${description.slice(0, 50)}...`;
|
||||
}
|
||||
|
||||
return description;
|
||||
};
|
||||
|
||||
const params = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Дата",
|
||||
handleClick() {
|
||||
console.log(this.name);
|
||||
},
|
||||
},
|
||||
{ id: 2, name: "Адрес" },
|
||||
{ id: 3, name: "Статус" },
|
||||
{ id: 4, name: "Описание" },
|
||||
{
|
||||
id: 5,
|
||||
name: "Комментарии",
|
||||
handleClick() {
|
||||
console.log(this.name);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Рейтинг",
|
||||
handleClick() {
|
||||
console.log(this.name);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="rating-section">
|
||||
<div className="rating-section__header">
|
||||
<Typography element="h3">Рейтинг</Typography>
|
||||
<Paragraph>
|
||||
Обсуждаем дороги: рейтинг, опыт, комфорт в пути!
|
||||
</Paragraph>
|
||||
</div>
|
||||
<SearchForm
|
||||
onChange={(e) => setRatingSearch(e.target.value)}
|
||||
name="rating-search"
|
||||
placeholder="Введите адрес"
|
||||
value={ratingSearch}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
<div className="rating-section__table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{params.map((p) => (
|
||||
<td key={p.id}>
|
||||
{p.handleClick ? (
|
||||
<button onClick={p.handleClick}>
|
||||
{p.name}
|
||||
<Image src={arrows} alt="Up and Down Arrows" />
|
||||
</button>
|
||||
) : (
|
||||
p.name
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{reports.results.map((report) => (
|
||||
<tr key={report.id}>
|
||||
<td id="rating-section-date">
|
||||
{sliceDate(report.created_at)}
|
||||
</td>
|
||||
<td id="rating-section-link">
|
||||
<Link href={`/report/${report.id}`}>
|
||||
{sliceLocation(report.location[0].address)}
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<RoadType
|
||||
color={ROAD_TYPES_COLORS[report.category]}
|
||||
>
|
||||
{ROAD_TYPES[report.category]}
|
||||
</RoadType>
|
||||
</td>
|
||||
<td id="rating-section-description">
|
||||
{sliceDescription(report.description)}
|
||||
</td>
|
||||
<td id="rating-section-reviews">
|
||||
<Image src={message_icon} alt="Message Icon" />
|
||||
{report.count_reviews}
|
||||
</td>
|
||||
<td id="rating-section-likes">
|
||||
<Image src={like_icon} alt="Like Icon" />
|
||||
{report.total_likes}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default RatingSection;
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg width="26.000000" height="26.000000" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<desc>
|
||||
Created with Pixso.
|
||||
</desc>
|
||||
<defs/>
|
||||
<rect id="thumbs-up" width="26.000000" height="26.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<path id="Vector" d="M7.58301 11.917L4.33301 11.917C3.7583 11.917 3.20752 12.1455 2.80127 12.5518C2.39502 12.958 2.1665 13.5088 2.1665 14.084L2.1665 21.667C2.1665 22.2412 2.39502 22.793 2.80127 23.1992C3.20752 23.6055 3.7583 23.834 4.33301 23.834L7.58301 23.834L19.8027 23.834C20.3262 23.8398 20.833 23.6562 21.2305 23.3184C21.6289 22.9795 21.8916 22.5088 21.9697 21.9922L23.4648 12.2422C23.5117 11.9316 23.4912 11.6143 23.4033 11.3125C23.3154 11.0107 23.1641 10.7324 22.958 10.4951C22.752 10.2578 22.4961 10.0684 22.21 9.94043C21.9229 9.81152 21.6123 9.74707 21.2979 9.75L15.167 9.75L15.167 5.41699C15.167 4.55469 14.8242 3.72852 14.2148 3.11914C13.6055 2.50977 12.7783 2.16699 11.916 2.16699L7.58301 11.917L7.58301 23.834" stroke="#4AC03F" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg width="26.000000" height="26.000000" viewBox="0 0 26 26" 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="clip161_49524">
|
||||
<rect id="message-circle" width="26.000000" height="26.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="message-circle" width="26.000000" height="26.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip161_49524)">
|
||||
<path id="Vector" d="M21.7749 16.5752C21.0107 18.1045 19.8354 19.3906 18.3813 20.29C16.9272 21.1895 15.2515 21.666 13.5415 21.667C12.1118 21.6709 10.7012 21.3359 9.4248 20.6914L3.25 22.75L5.30811 16.5752C4.66357 15.2988 4.32959 13.8887 4.3335 12.458C4.33398 10.749 4.81055 9.07324 5.70996 7.61816C6.60938 6.16406 7.89551 4.98926 9.4248 4.22461C10.7012 3.58008 12.1118 3.24609 13.5415 3.25L14.0835 3.25C16.3413 3.375 18.4741 4.32812 20.0732 5.92676C21.6724 7.52637 22.6255 9.6582 22.75 11.917L22.75 12.458C22.7539 13.8887 22.4194 15.2988 21.7749 16.5752Z" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,50 @@
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { IFetch } from "@/shared/types/fetch-type";
|
||||
import { IList } from "@/shared/types/list-type";
|
||||
import { IReport } from "@/shared/types/report-type";
|
||||
import axios from "axios";
|
||||
import { create } from "zustand";
|
||||
|
||||
interface IFetchReports extends IList {
|
||||
results: IReport[];
|
||||
}
|
||||
|
||||
interface IRatingStore extends IFetch {
|
||||
data: IFetchReports;
|
||||
getReports: (categories: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useRatingStore = create<IRatingStore>((set) => ({
|
||||
data: {
|
||||
count: 0,
|
||||
previous: null,
|
||||
next: null,
|
||||
results: [],
|
||||
},
|
||||
isLoading: false,
|
||||
error: "",
|
||||
getReports: async (query: string = "") => {
|
||||
try {
|
||||
set({ isLoading: true });
|
||||
|
||||
const data = (await apiInstance.get<IFetchReports>("/report/"))
|
||||
.data;
|
||||
|
||||
const searched = data.results.filter((rating) => {
|
||||
return rating.location.some((location) => {
|
||||
return location.address
|
||||
.toLowerCase()
|
||||
.includes(query.toLowerCase());
|
||||
});
|
||||
});
|
||||
|
||||
data.results = [...searched];
|
||||
|
||||
set({ data: data });
|
||||
} catch (error: any) {
|
||||
set({ error: error.message });
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,7 @@
|
||||
import "./CreateReportMap.scss";
|
||||
|
||||
const CreateReportMap = () => {
|
||||
return <div>CreateReportMap</div>;
|
||||
};
|
||||
|
||||
export default CreateReportMap;
|
||||
@@ -0,0 +1,251 @@
|
||||
.report-form {
|
||||
padding: 40px 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px;
|
||||
border: 1px solid rgb(213, 213, 213);
|
||||
border-radius: 10px;
|
||||
|
||||
label {
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
line-height: 26px;
|
||||
color: rgb(50, 48, 58);
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
max-width: 801px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 22px;
|
||||
|
||||
input,
|
||||
textarea {
|
||||
height: 52px;
|
||||
padding: 16px 20px;
|
||||
width: 100%;
|
||||
border: 1px solid rgb(197, 198, 197);
|
||||
border-radius: 6px;
|
||||
background: rgb(255, 255, 255);
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: rgb(50, 48, 58);
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 276px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: rgb(197, 198, 197);
|
||||
}
|
||||
}
|
||||
|
||||
&__map {
|
||||
width: 100%;
|
||||
height: 410px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 286px;
|
||||
border: 1px solid rgb(197, 198, 197);
|
||||
border-radius: 6px;
|
||||
|
||||
.leaflet-container {
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
padding: 24px;
|
||||
padding-right: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 11px;
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
color: rgb(50, 48, 58);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
color: rgb(50, 48, 58);
|
||||
span {
|
||||
color: rgb(102, 102, 102);
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-top: 42px;
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
&__add-images {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
p {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: rgb(102, 102, 102);
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__upload {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: rgb(50, 48, 58);
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
color: rgb(176, 176, 176);
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__images {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
img {
|
||||
width: 187px;
|
||||
height: 187px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
button {
|
||||
width: fit-content;
|
||||
align-self: flex-end;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
text-decoration: underline;
|
||||
color: rgb(50, 48, 58);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
margin-top: 40px;
|
||||
padding: 15px;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border: 1px solid rgb(72, 159, 225), rgb(72, 159, 225);
|
||||
border-radius: 5px;
|
||||
background-color: rgb(72, 159, 225);
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
line-height: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.report-form {
|
||||
padding: 36px 20px;
|
||||
gap: 30px;
|
||||
|
||||
label {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
&__input {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
&__map {
|
||||
grid-template-columns: 1fr;
|
||||
height: fit-content;
|
||||
|
||||
.leaflet-container {
|
||||
height: 410px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.report-form {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
gap: 26px;
|
||||
|
||||
label {
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&__input {
|
||||
gap: 6px;
|
||||
|
||||
textarea {
|
||||
height: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
&__map {
|
||||
.leaflet-container {
|
||||
height: 370px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
padding: 20px 10px;
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
&__add-images {
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
"use client";
|
||||
|
||||
import "./ReportForm.scss";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import Image from "next/image";
|
||||
import {
|
||||
MapContainer,
|
||||
Marker,
|
||||
Popup,
|
||||
TileLayer,
|
||||
useMapEvents,
|
||||
} from "react-leaflet";
|
||||
import clip from "./icons/clip.svg";
|
||||
import arrow_right from "./icons/arrow-right.svg";
|
||||
import pin_image from "./icons/pin-image.svg";
|
||||
import { ChangeEventHandler, useState } from "react";
|
||||
import { Icon } from "leaflet";
|
||||
import pin_icon from "./icons/pin_icon.svg";
|
||||
import axios from "axios";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
interface ILatLng {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
|
||||
const ReportForm = () => {
|
||||
const session = useSession();
|
||||
const [latLng, setLatLng] = useState<ILatLng[]>([]);
|
||||
const [displayLatLng, setDisplayLatLng] = useState<string[]>([]);
|
||||
const [images, setImages] = useState<File[]>([]);
|
||||
const position = {
|
||||
lat: 42.8746,
|
||||
lng: 74.606,
|
||||
};
|
||||
|
||||
const customIcon = new Icon({
|
||||
iconUrl: pin_icon.src,
|
||||
iconSize: [32, 32],
|
||||
});
|
||||
|
||||
const handleImages: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
if (e.target.files) {
|
||||
const getArrayFromObject = Array.from(e.target.files);
|
||||
setImages(getArrayFromObject);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteImage = (name: string) => {
|
||||
setImages((prev) => prev.filter((img) => img.name !== name));
|
||||
};
|
||||
|
||||
const createReport: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const Authorization = `Bearer ${session.data?.access_token}`;
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization,
|
||||
},
|
||||
};
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
images.forEach((image) => {
|
||||
formData.append("image", image);
|
||||
});
|
||||
formData.append("latitude1", latLng[0].lat.toString());
|
||||
formData.append("longitude1", latLng[0].lng.toString());
|
||||
formData.append("latitude2", latLng[1].lat.toString());
|
||||
formData.append("longitude2", latLng[1].lng.toString());
|
||||
const res = await apiInstance.post(
|
||||
"/report/create/",
|
||||
formData,
|
||||
config
|
||||
);
|
||||
|
||||
console.log(res.status);
|
||||
};
|
||||
|
||||
const MapPins = (e: any) => {
|
||||
useMapEvents({
|
||||
click(e) {
|
||||
if (latLng.length < 2) {
|
||||
setLatLng([...latLng, e.latlng]);
|
||||
|
||||
axios
|
||||
.get(
|
||||
`https://nominatim.openstreetmap.org/reverse?lat=${e.latlng.lat}&lon=${e.latlng.lng}&format=json`
|
||||
)
|
||||
.then((res) =>
|
||||
setDisplayLatLng([
|
||||
...displayLatLng,
|
||||
res.data.display_name,
|
||||
])
|
||||
)
|
||||
.catch((error) => console.log(error));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return latLng.map((l) => (
|
||||
<Marker key={l.lat} icon={customIcon} position={l} />
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={createReport} className="report-form">
|
||||
<div className="report-form__input">
|
||||
<label>Адрес</label>
|
||||
<input
|
||||
value={displayLatLng.join("; ")}
|
||||
disabled
|
||||
placeholder="Отметьте точки на карте"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="report-form__map">
|
||||
<MapContainer
|
||||
center={position}
|
||||
zoom={14}
|
||||
scrollWheelZoom={false}
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
<MapPins />
|
||||
</MapContainer>
|
||||
|
||||
<div className="report-form__info">
|
||||
<h4>Как отметить участок дороги? </h4>
|
||||
<Image src={pin_image} alt="Pin Image" />
|
||||
<p>
|
||||
Поставьте булавку и начните рисовать участок дороги{" "}
|
||||
<span>
|
||||
(он может состоять из любого количества ломаных линий)
|
||||
</span>
|
||||
</p>
|
||||
<p>Чтобы удалить отрезок нажмите на точки повторно.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="report-form__input">
|
||||
<label>Добавьте описание проблемы</label>
|
||||
<textarea name="description" placeholder="Введите описание" />
|
||||
</div>
|
||||
|
||||
<div className="report-form__add-images">
|
||||
<label>Добавьте фотографии</label>
|
||||
<p>
|
||||
Загрузите до 5 фотографии, связанные с дорогой, которую Вы
|
||||
хотите отметить. Фотографии помогут лучше понять проблему.
|
||||
</p>
|
||||
<label
|
||||
className="report-form__upload"
|
||||
htmlFor="report-form-upload"
|
||||
>
|
||||
<Image src={clip} alt="Paper Clip Icon" />
|
||||
Прикрепить файл
|
||||
<span>(до 5 МБ)</span>
|
||||
</label>
|
||||
<input
|
||||
onChange={handleImages}
|
||||
multiple
|
||||
type="file"
|
||||
id="report-form-upload"
|
||||
/>
|
||||
{images.length ? (
|
||||
<div className="report-form__images">
|
||||
{images.map((image) => (
|
||||
<div key={image.name}>
|
||||
<img
|
||||
src={URL.createObjectURL(image)}
|
||||
alt="Report Image"
|
||||
/>
|
||||
<button onClick={() => deleteImage(image.name)}>
|
||||
удалить
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<button type="submit">
|
||||
Отправить на модерацию
|
||||
<Image src={arrow_right} alt="Arrow Right Icon" />
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportForm;
|
||||
@@ -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="clip345_6572">
|
||||
<rect id="arrow-right" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="arrow-right" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip345_6572)">
|
||||
<path id="Vector" d="M5 12L19 12" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M12 5L19 12L12 19" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 804 B |
@@ -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="clip345_6197">
|
||||
<rect id="paperclip" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="paperclip" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip345_6197)">
|
||||
<path id="Vector" d="M21.4404 11.0498L12.25 20.2402C11.124 21.3662 9.59766 21.998 8.00488 21.998C6.41309 21.998 4.88574 21.3662 3.76074 20.2402C2.63477 19.1143 2.00195 17.5869 2.00195 15.9951C2.00195 14.4023 2.63477 12.876 3.76074 11.75L12.9502 2.55957C13.7012 1.80957 14.7188 1.3877 15.7803 1.3877C16.8418 1.3877 17.8594 1.80957 18.6104 2.55957C19.3613 3.31055 19.7822 4.32812 19.7822 5.38965C19.7822 6.45117 19.3613 7.46973 18.6104 8.21973L9.41016 17.4102C9.03516 17.7852 8.52637 17.9961 7.99512 17.9961C7.46484 17.9961 6.95605 17.7852 6.58008 17.4102C6.20508 17.0342 5.99414 16.5254 5.99414 15.9951C5.99414 15.4639 6.20508 14.9551 6.58008 14.5801L15.0703 6.09961" stroke="#32303A" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg width="81.304688" height="64.551758" viewBox="0 0 81.3047 64.5518" 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="clip401_4465">
|
||||
<rect id="pin" width="50.000000" height="50.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="pin" width="50.000000" height="50.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip401_4465)">
|
||||
<path id="Vector" d="M32.8125 9.375C32.8125 7.89746 32.3926 6.44922 31.6035 5.2002C30.8125 3.9502 29.6836 2.95117 28.3496 2.31738C27.0137 1.68359 25.5254 1.44238 24.0586 1.62012C22.5898 1.79883 21.2031 2.38965 20.0586 3.32422C18.9141 4.25977 18.0566 5.5 17.5898 6.90234C17.1211 8.30469 17.0605 9.81055 17.4141 11.2461C17.7676 12.6816 18.5215 13.9863 19.5879 15.0107C20.6543 16.0342 21.9883 16.7354 23.4375 17.0303L23.4375 44.6836C23.4375 45.0986 23.5195 45.5098 23.6816 45.8926L24.6641 48.2363C24.6973 48.2969 24.7461 48.3477 24.8047 48.3828C24.8633 48.417 24.9316 48.4355 25 48.4355C25.0684 48.4355 25.1367 48.417 25.1953 48.3828C25.2539 48.3477 25.3027 48.2969 25.3359 48.2363L26.3203 45.8926C26.4805 45.5098 26.5625 45.0986 26.5625 44.6836L26.5625 17.0303C28.3262 16.668 29.9102 15.709 31.0488 14.3145C32.1875 12.9199 32.8105 11.1758 32.8125 9.375ZM27.3438 9.375C26.8809 9.375 26.4277 9.23828 26.041 8.98047C25.6562 8.72266 25.3555 8.35645 25.1777 7.92871C25.002 7.5 24.9551 7.0293 25.0449 6.57422C25.1348 6.12012 25.3594 5.70215 25.6855 5.37402C26.0137 5.04688 26.4316 4.82324 26.8867 4.73242C27.3418 4.64258 27.8125 4.68848 28.2402 4.86621C28.668 5.04395 29.0352 5.34375 29.293 5.72949C29.5508 6.11523 29.6875 6.56836 29.6875 7.03125C29.6875 7.65332 29.4414 8.24902 29.002 8.68848C28.5605 9.12891 27.9648 9.375 27.3438 9.375Z" fill="#E64452" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</g>
|
||||
<circle id="Ellipse 129" cx="25.000000" cy="51.000000" r="5.000000" fill="#FFFFFF" fill-opacity="1.000000"/>
|
||||
<circle id="Ellipse 129" cx="25.000000" cy="51.000000" r="4.750000" stroke="#000000" stroke-opacity="1.000000" stroke-width="0.500000"/>
|
||||
<line id="Line 12" x1="33.294922" y1="53.529297" x2="81.009766" y2="63.081055" stroke="#E64452" stroke-opacity="1.000000" stroke-width="3.000000" stroke-dasharray="6.000000,6.000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg width="50.000000" height="50.000000" viewBox="0 0 50 50" 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="clip345_6143">
|
||||
<rect id="pin" width="50.000000" height="50.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="pin" width="50.000000" height="50.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip345_6143)">
|
||||
<path id="Vector" d="M32.8125 9.375C32.8125 7.89746 32.3926 6.44922 31.6025 5.2002C30.8125 3.9502 29.6846 2.95117 28.3486 2.31738C27.0127 1.68359 25.5254 1.44238 24.0576 1.62012C22.5908 1.79883 21.2041 2.38965 20.0586 3.32422C18.9141 4.25977 18.0576 5.5 17.5898 6.90234C17.1211 8.30469 17.0605 9.81055 17.415 11.2461C17.7686 12.6816 18.5225 13.9863 19.5879 15.0107C20.6543 16.0342 21.9893 16.7354 23.4375 17.0303L23.4375 44.6836C23.4375 45.0986 23.5205 45.5098 23.6807 45.8926L24.6641 48.2363C24.6973 48.2969 24.7451 48.3477 24.8047 48.3828C24.8643 48.417 24.9316 48.4355 25 48.4355C25.0684 48.4355 25.1357 48.417 25.1953 48.3828C25.2549 48.3477 25.3027 48.2969 25.3359 48.2363L26.3193 45.8926C26.4795 45.5098 26.5625 45.0986 26.5625 44.6836L26.5625 17.0303C28.3262 16.668 29.9102 15.709 31.0488 14.3145C32.1875 12.9199 32.8105 11.1758 32.8125 9.375ZM27.3438 9.375C26.8799 9.375 26.4268 9.23828 26.042 8.98047C25.6562 8.72266 25.3555 8.35645 25.1787 7.92871C25.001 7.5 24.9551 7.0293 25.0449 6.57422C25.1357 6.12012 25.3584 5.70215 25.6865 5.37402C26.0146 5.04688 26.4316 4.82324 26.8867 4.73242C27.3408 4.64258 27.8125 4.68848 28.2402 4.86621C28.6689 5.04395 29.0352 5.34375 29.293 5.72949C29.5498 6.11523 29.6875 6.56836 29.6875 7.03125C29.6875 7.65332 29.4404 8.24902 29.001 8.68848C28.5615 9.12891 27.9658 9.375 27.3438 9.375Z" fill="#E64452" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,39 @@
|
||||
@import "@/shared/ui/variables.scss";
|
||||
|
||||
.reset-code-form {
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__inputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
&__error {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: $red-500;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
margin-top: 32px;
|
||||
padding: 10px 18px;
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
background-color: rgb(54, 149, 216);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.reset-code-form {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import AuthInput from "@/features/AuthInput/AuthInput";
|
||||
import "./ResetCodeForm.scss";
|
||||
import { useState } from "react";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { ITokens } from "@/shared/types/token-type";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
|
||||
const ResetCodeForm = () => {
|
||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
||||
const [passwordConfirmWarning, setPasswordConfirmWarning] =
|
||||
useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
if (!formData.get("new_password1")) {
|
||||
setError("");
|
||||
setPasswordConfirmWarning("");
|
||||
setPasswordWarning("Заполните поле с новым паролем");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.get("new_password1")) {
|
||||
setError("");
|
||||
setPasswordWarning("");
|
||||
setPasswordConfirmWarning(
|
||||
"Заполните поле с новым паролем потверждения"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setError("");
|
||||
setPasswordWarning("");
|
||||
setPasswordConfirmWarning("");
|
||||
setLoader(true);
|
||||
|
||||
const storage = localStorage.getItem("transitional");
|
||||
if (storage === null) return;
|
||||
const transitional: ITokens = JSON.parse(storage);
|
||||
|
||||
const Authorization = `Bearer ${transitional.access_token}`;
|
||||
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await apiInstance.put(
|
||||
"/users/password_reset/confirm/",
|
||||
formData,
|
||||
config
|
||||
);
|
||||
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
localStorage.removeItem("transitional");
|
||||
router.push("/sign-in");
|
||||
}
|
||||
} catch (error) {
|
||||
setError("An error ocured");
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="reset-code-form">
|
||||
<div className="reset-code-form__inputs">
|
||||
<AuthInput
|
||||
name="new_password1"
|
||||
label="Введите пароль"
|
||||
error={passwordWarning}
|
||||
isPassword
|
||||
placeholder="Введите новый пароль"
|
||||
/>
|
||||
<AuthInput
|
||||
name="new_password2"
|
||||
label="Повторите пароль"
|
||||
error={passwordConfirmWarning}
|
||||
isPassword
|
||||
placeholder="Потвердите новый пароль"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<p className="reset-code-form__error">{error}</p>
|
||||
) : null}
|
||||
<button className="reset-code-form__btn" type="submit">
|
||||
{loader ? <Loader /> : "Сохранить"}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResetCodeForm;
|
||||
@@ -0,0 +1,171 @@
|
||||
.review-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
line-height: 29px;
|
||||
color: rgb(62, 50, 50);
|
||||
|
||||
span {
|
||||
width: 4px;
|
||||
height: 10px;
|
||||
border-radius: 12px;
|
||||
background: rgb(57, 152, 232);
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 70px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
textarea {
|
||||
height: 258px;
|
||||
width: 100%;
|
||||
padding: 26px 18px;
|
||||
border: 1px solid rgb(197, 198, 197);
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: flex-end;
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
background: rgb(57, 152, 232);
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.review {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
border: 1px solid rgb(197, 198, 197);
|
||||
border-radius: 12px;
|
||||
|
||||
&__author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
#author-img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
&__author-name {
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
line-height: 19px;
|
||||
color: rgb(62, 50, 50);
|
||||
}
|
||||
|
||||
&__date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
|
||||
color: rgba(62, 50, 50, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.review-section {
|
||||
h3 {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 56px;
|
||||
}
|
||||
|
||||
&__list {
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.review {
|
||||
padding: 15px 20px;
|
||||
|
||||
&__description {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.review-section {
|
||||
h3 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 50px;
|
||||
gap: 12px;
|
||||
|
||||
textarea {
|
||||
height: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
ul {
|
||||
gap: 13px;
|
||||
}
|
||||
|
||||
.review {
|
||||
padding: 15px;
|
||||
|
||||
&__description {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
"use client";
|
||||
|
||||
import "./ReviewSection.scss";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { IReviewList } from "@/shared/types/review-type";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import calendar from "./icons/calendar.svg";
|
||||
import Image from "next/image";
|
||||
|
||||
interface IReviewsSectionProps {
|
||||
endpoint: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
const ReviewSection: React.FC<IReviewsSectionProps> = ({
|
||||
endpoint,
|
||||
id,
|
||||
}: IReviewsSectionProps) => {
|
||||
const [reviews, setReviews] = useState<IReviewList>();
|
||||
const session = useSession();
|
||||
const handleSubmit: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const Authorization = `Bearer ${session.data?.access_token}`;
|
||||
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization,
|
||||
},
|
||||
};
|
||||
|
||||
if (!formData.get("review")) {
|
||||
return;
|
||||
}
|
||||
|
||||
formData.append("news", id.toString());
|
||||
|
||||
try {
|
||||
const res = await apiInstance.post(
|
||||
`/${endpoint}/${id}/reviews/`,
|
||||
formData,
|
||||
config
|
||||
);
|
||||
getReviews();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const getReviews = async () => {
|
||||
const response = await apiInstance.get<IReviewList>(
|
||||
`/${endpoint}/${id}/reviews/`
|
||||
);
|
||||
|
||||
setReviews(response.data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getReviews();
|
||||
}, []);
|
||||
|
||||
const months: Record<string, string> = {
|
||||
"01": "Январь",
|
||||
"02": "Февраль",
|
||||
"03": "Март",
|
||||
"04": "Апрель",
|
||||
"05": "Май",
|
||||
"06": "Июнь",
|
||||
"07": "Июль",
|
||||
"08": "Август",
|
||||
"09": "Сентябрь",
|
||||
"10": "Октябрь",
|
||||
"11": "Ноябрь",
|
||||
"12": "Декабрь",
|
||||
};
|
||||
return (
|
||||
<section className="review-section">
|
||||
<h3>
|
||||
<span id="blue-point" /> Написать комментарий
|
||||
</h3>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<textarea name="review" />
|
||||
<button type="submit">Отправить</button>
|
||||
</form>
|
||||
|
||||
<div className="review-section__list">
|
||||
<h3>
|
||||
<span id="blue-point" /> Комментарии
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
{reviews?.results.map((review) => (
|
||||
<li key={review.id} className="review">
|
||||
<div className="review__author">
|
||||
<img
|
||||
id="author-img"
|
||||
src={review.author.image}
|
||||
alt="Author Image"
|
||||
/>
|
||||
<div className="review__header">
|
||||
<h5 className="review__author-name">
|
||||
{review.author.first_name}
|
||||
{review.author.last_name}
|
||||
</h5>
|
||||
<div className="review__date">
|
||||
<Image src={calendar} alt="Calendar Icon" />
|
||||
<p>
|
||||
{months[review.created_at.slice(5, 7)]}{" "}
|
||||
{review.created_at.slice(5, 7).slice(0, 1) ===
|
||||
"0"
|
||||
? review.created_at.slice(6, 7)
|
||||
: review.created_at.slice(5, 7)}
|
||||
, {review.created_at.slice(0, 4)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="review__description">{review.review}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReviewSection;
|
||||
@@ -0,0 +1,17 @@
|
||||
<svg width="16.000000" height="16.000000" viewBox="0 0 16 16" 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="clip2340_50999">
|
||||
<rect id="calendar" width="16.000000" height="16.000000" fill="white" fill-opacity="0"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="calendar" width="16.000000" height="16.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||
<g clip-path="url(#clip2340_50999)">
|
||||
<path id="Vector" d="M12.666 2.66699C13.4023 2.66699 14 3.26367 14 4L14 13.334C14 14.0703 13.4023 14.667 12.666 14.667L3.33398 14.667C2.59766 14.667 2 14.0703 2 13.334L2 4C2 3.26367 2.59766 2.66699 3.33398 2.66699L12.666 2.66699Z" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round"/>
|
||||
<path id="Vector" d="M10.666 1.33301L10.666 4" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M5.33398 1.33301L5.33398 4" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path id="Vector" d="M2 6.66699L14 6.66699" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,55 @@
|
||||
@import "@/shared/ui/variables.scss";
|
||||
|
||||
.sign-in-form {
|
||||
margin-bottom: 8px;
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__error {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: $red-500;
|
||||
}
|
||||
|
||||
&__forgot-password {
|
||||
margin: 36px 0;
|
||||
align-self: flex-end;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
&__inputs,
|
||||
&__btns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
&__btns {
|
||||
gap: 16px;
|
||||
|
||||
&_first {
|
||||
padding: 10px 18px;
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
background-color: $light-blue;
|
||||
border: 1px solid rgb(72, 159, 225);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
background: rgb(72, 159, 225);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.sign-in-form {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
"use client";
|
||||
|
||||
import "./SignInForm.scss";
|
||||
import AuthInput from "@/features/AuthInput/AuthInput";
|
||||
import GoogleButton from "@/features/GoogleButton/GoogleButton";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
const SignInForm = () => {
|
||||
const [emailWarning, setEmailWarning] = useState<string>("");
|
||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
if (!formData.get("email")?.toString()) {
|
||||
setError("");
|
||||
setPasswordWarning("");
|
||||
setEmailWarning("Заполните поле Email");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.get("password")?.toString()) {
|
||||
setError("");
|
||||
setEmailWarning("");
|
||||
setPasswordWarning("Заполните поле Пароль");
|
||||
return;
|
||||
}
|
||||
|
||||
setError("");
|
||||
setEmailWarning("");
|
||||
setPasswordWarning("");
|
||||
setLoader(true);
|
||||
|
||||
const res = await signIn("credentials", {
|
||||
email: formData.get("email"),
|
||||
password: formData.get("password"),
|
||||
redirect: false,
|
||||
});
|
||||
|
||||
setLoader(false);
|
||||
|
||||
if (res?.ok && !res.error) {
|
||||
router.push("/profile/personal");
|
||||
} else if (res?.status.toString().slice(0, 1) === "4") {
|
||||
setError("Неверный Email или Пароль");
|
||||
} else {
|
||||
setError("Произошла непредвиденная ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="sign-in-form" onSubmit={handleSubmit}>
|
||||
<div className="sign-in-form__inputs">
|
||||
<AuthInput
|
||||
type="email"
|
||||
label="Email"
|
||||
placeholder="Введите email"
|
||||
error={emailWarning}
|
||||
name="email"
|
||||
/>
|
||||
<AuthInput
|
||||
isPassword
|
||||
label="Пароль"
|
||||
placeholder="Введите пароль"
|
||||
error={passwordWarning}
|
||||
name="password"
|
||||
/>
|
||||
</div>
|
||||
{error ? <p className="sign-in-form__error">{error}</p> : null}
|
||||
<Link
|
||||
href="/sign-in/forgot-password"
|
||||
className="sign-in-form__forgot-password"
|
||||
>
|
||||
Забыли пароль?
|
||||
</Link>
|
||||
|
||||
<div className="sign-in-form__btns">
|
||||
<button className="sign-in-form__btns_first" type="submit">
|
||||
{loader ? <Loader /> : "Войти"}
|
||||
</button>
|
||||
<GoogleButton />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInForm;
|
||||
@@ -0,0 +1,56 @@
|
||||
@import "@/shared/ui/variables.scss";
|
||||
|
||||
.sign-up-form {
|
||||
margin-bottom: 8px;
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__error {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: $red-500;
|
||||
}
|
||||
|
||||
&__forgot-password {
|
||||
margin: 36px 0;
|
||||
align-self: flex-end;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
&__inputs,
|
||||
&__btns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
&__btns {
|
||||
margin-top: 36px;
|
||||
gap: 16px;
|
||||
|
||||
&_first {
|
||||
padding: 10px 18px;
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
background-color: $light-blue;
|
||||
border: 1px solid rgb(72, 159, 225);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
background: rgb(72, 159, 225);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.sign-up-form {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import "./SignUpForm.scss";
|
||||
import { useState } from "react";
|
||||
import AuthInput from "@/features/AuthInput/AuthInput";
|
||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||
import GoogleButton from "@/features/GoogleButton/GoogleButton";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
const SignUpForm = () => {
|
||||
const [emailWarning, setEmailWarning] = useState<string>("");
|
||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
||||
const [passwordConfirmWarning, setPasswordConfirmWarning] =
|
||||
useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loader, setLoader] = useState<boolean>(false);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit: React.MouseEventHandler<
|
||||
HTMLFormElement
|
||||
> = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
if (!formData.get("email")?.toString()) {
|
||||
setError("");
|
||||
setPasswordWarning("");
|
||||
setPasswordConfirmWarning("");
|
||||
setEmailWarning("Заполните поле Email");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.get("password")?.toString()) {
|
||||
setError("");
|
||||
setEmailWarning("");
|
||||
setPasswordConfirmWarning("");
|
||||
setPasswordWarning("Заполните поле Пароль");
|
||||
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;
|
||||
}
|
||||
|
||||
try {
|
||||
setError("");
|
||||
setEmailWarning("");
|
||||
setPasswordWarning("");
|
||||
setPasswordConfirmWarning("");
|
||||
setLoader(true);
|
||||
|
||||
const res = await apiInstance.post(
|
||||
"/users/register/",
|
||||
formData
|
||||
);
|
||||
|
||||
if (res.status === 200 || res.status === 201) {
|
||||
router.push(
|
||||
`/sign-up/confirm-email/?email=${formData.get("email")}`
|
||||
);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
setError("Произошла непредвиденная ошибка");
|
||||
} else {
|
||||
setError("An error ocured");
|
||||
}
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="sign-up-form" onSubmit={handleSubmit}>
|
||||
<div className="sign-up-form__inputs">
|
||||
<AuthInput
|
||||
type="email"
|
||||
label="Email"
|
||||
placeholder="Введите email"
|
||||
error={emailWarning}
|
||||
name="email"
|
||||
/>
|
||||
<AuthInput
|
||||
isPassword
|
||||
label="Пароль"
|
||||
placeholder="Введите пароль"
|
||||
error={passwordWarning}
|
||||
name="password"
|
||||
/>
|
||||
<AuthInput
|
||||
isPassword
|
||||
label="Пароль потверждения"
|
||||
placeholder="Повторите пароль"
|
||||
error={passwordConfirmWarning}
|
||||
name="password2"
|
||||
/>
|
||||
</div>
|
||||
{error ? <p className="sign-up-form__error">{error}</p> : null}
|
||||
|
||||
<div className="sign-up-form__btns">
|
||||
<button className="sign-up-form__btns_first" type="submit">
|
||||
{loader ? <Loader /> : "Войти"}
|
||||
</button>
|
||||
<GoogleButton />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUpForm;
|
||||
@@ -0,0 +1,56 @@
|
||||
@import "../../shared/ui/variables.scss";
|
||||
|
||||
.statistics-section {
|
||||
padding: 120px 90px 110px 90px;
|
||||
|
||||
&__list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 32px;
|
||||
|
||||
li {
|
||||
padding: 20px;
|
||||
padding-right: 0px;
|
||||
height: 128px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgb(213, 213, 213);
|
||||
border-radius: 10px;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
line-height: 29px;
|
||||
color: $light-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.statistics-section {
|
||||
padding: 120px 30px 110px 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
.statistics-section {
|
||||
padding: 120px 16px 110px 16px;
|
||||
|
||||
&__list {
|
||||
gap: 16px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
li {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ROAD_TYPES_STATS } from "@/shared/variables/road-types";
|
||||
import "./StatisticsSection.scss";
|
||||
import { statiscsSectionStore } from "./statisticsSection.store";
|
||||
|
||||
const StatisticsSection = async () => {
|
||||
// const { getStatsCount, getErrorMessage } = statiscsSectionStore();
|
||||
// const statistics = await getStatsCount();
|
||||
// const error = getErrorMessage();
|
||||
|
||||
const mockArray = [
|
||||
{ category: 1, count: 132 },
|
||||
{ category: 2, count: 12 },
|
||||
{ category: 3, count: 1432 },
|
||||
{ category: 4, count: 18 },
|
||||
{ category: 5, count: 95 },
|
||||
{ category: 6, count: 54 },
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="statistics-section">
|
||||
<ul className="statistics-section__list">
|
||||
{mockArray.map((cat) => (
|
||||
<li key={cat.category}>
|
||||
<p>{ROAD_TYPES_STATS[cat.category]}</p>
|
||||
<span>{cat.count}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatisticsSection;
|
||||
@@ -0,0 +1,35 @@
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
interface IStatsCount {
|
||||
category: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export const statiscsSectionStore = () => {
|
||||
let errorMessage = "";
|
||||
async function getStatsCount() {
|
||||
try {
|
||||
const response = await apiInstance.get<IStatsCount[]>(
|
||||
"/report/category_count/"
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
errorMessage = error.message;
|
||||
} else {
|
||||
errorMessage = "An error ocured";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
return {
|
||||
getStatsCount,
|
||||
getErrorMessage,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
@import "@/shared/ui/variables.scss";
|
||||
|
||||
.statistics-table {
|
||||
position: relative;
|
||||
|
||||
&__popup {
|
||||
width: 123px;
|
||||
position: absolute;
|
||||
top: 58%;
|
||||
left: 2%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgb(255, 255, 255);
|
||||
box-shadow: 0px 4px 6px -2px rgba(16, 24, 40, 0.03),
|
||||
0px 12px 16px -4px rgba(16, 24, 40, 0.08);
|
||||
button {
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
color: $gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
border: 1px solid rgb(213, 213, 213);
|
||||
border-radius: 12px;
|
||||
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
thead {
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
height: 76px;
|
||||
display: flex;
|
||||
background-color: rgb(244, 244, 244);
|
||||
align-items: center;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 100px 167px 186px 188px 263px 170px 151px;
|
||||
align-items: center;
|
||||
|
||||
td {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-color: rgb(244, 244, 244);
|
||||
}
|
||||
|
||||
td,
|
||||
button {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
padding: 0 20px;
|
||||
tr {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 167px 186px 188px 263px 170px 151px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
border-bottom: 1px solid rgb(241, 244, 249);
|
||||
|
||||
td {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
|
||||
#statistics-table-stat-name {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
|
||||
import { IStatistics } from "@/shared/types/statistics-type";
|
||||
import "./StatisticsTable.scss";
|
||||
import { useStatisticsStore } from "./statistics.store";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import SearchForm from "@/features/SearchForm/SearchForm";
|
||||
import chevron_down from "./icons/chevron-down.svg";
|
||||
import arrows from "@/shared/icons/arrows.svg";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface IStatisticsTableProps {
|
||||
firstData: IStatistics[] | string;
|
||||
}
|
||||
|
||||
const StatisticsTable: React.FC<IStatisticsTableProps> = ({
|
||||
firstData,
|
||||
}: IStatisticsTableProps) => {
|
||||
const { data, error, isLoading, getStatistics } =
|
||||
useStatisticsStore(useShallow((state) => state));
|
||||
|
||||
const [statistics, setStatistics] = useState<IStatistics[]>(
|
||||
typeof firstData === "string" ? [] : firstData
|
||||
);
|
||||
const [openPopup, setOpenPopup] = useState<boolean>(false);
|
||||
|
||||
const handleSubmit = () => {};
|
||||
|
||||
const params = [
|
||||
{ param: "Добавлено дорог", handleSubmit() {} },
|
||||
{ param: "Локальных дефектов", handleSubmit() {} },
|
||||
{ param: "Очагов аварийности", handleSubmit() {} },
|
||||
{ param: "Локальных дефектов исправлено", handleSubmit() {} },
|
||||
{ param: "В планах ремонта", handleSubmit() {} },
|
||||
{ param: "Отремонтировано", handleSubmit() {} },
|
||||
];
|
||||
|
||||
const getStatsByLocation = async (location: string) => {
|
||||
try {
|
||||
const data: IStatistics[] | undefined = await getStatistics(
|
||||
location
|
||||
);
|
||||
|
||||
if (data === undefined)
|
||||
throw new Error("Ошибка на стороне сервера");
|
||||
setStatistics(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching statistics:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="statistics-table">
|
||||
<SearchForm
|
||||
style={{ width: "100%" }}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
|
||||
{openPopup && (
|
||||
<div className="statistics-table__popup">
|
||||
<button onClick={() => getStatsByLocation("state")}>
|
||||
Область
|
||||
</button>
|
||||
<button onClick={() => getStatsByLocation("city")}>
|
||||
Город
|
||||
</button>
|
||||
<button onClick={() => getStatsByLocation("village")}>
|
||||
Деревня
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="statistics-table__wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<button onClick={() => setOpenPopup((prev) => !prev)}>
|
||||
Город
|
||||
<Image src={chevron_down} alt=" Chevron Icon" />
|
||||
</button>
|
||||
</td>
|
||||
{params.map((param) => (
|
||||
<td key={param.param}>
|
||||
<button>
|
||||
{param.param}
|
||||
<Image src={arrows} alt=" Arrows Icon" />
|
||||
</button>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{statistics.map((stat) => (
|
||||
<tr key={stat.name}>
|
||||
<td id="statistics-table-stat-name">{stat.name}</td>
|
||||
<td>{stat.broken_road_1}</td>
|
||||
<td>{stat.local_defect_3}</td>
|
||||
<td>{stat.hotbed_of_accidents_2}</td>
|
||||
<td>{stat.local_defect_fixed_6}</td>
|
||||
<td>{stat.repair_plans_4}</td>
|
||||
<td>{stat.repaired_5}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatisticsTable;
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg width="9.000000" height="6.000000" viewBox="0 0 9 6" 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="Vector" d="M8.25 0L0.75 0C0.609375 0 0.46875 0.0410156 0.347656 0.117188C0.226562 0.193359 0.132812 0.302734 0.0703125 0.431641C0.0117188 0.561523 -0.0117188 0.705078 0.0078125 0.84668C0.0234375 0.988281 0.0820312 1.12109 0.175781 1.23047L3.92578 5.73047C3.99609 5.81543 4.08203 5.88281 4.18359 5.92969C4.28125 5.97656 4.39062 6 4.5 6C4.60938 6 4.71875 5.97656 4.81641 5.92969C4.91797 5.88281 5.00781 5.81543 5.07812 5.73047L8.82812 1.23047C8.91797 1.12109 8.97656 0.988281 8.99609 0.84668C9.01172 0.705078 8.99219 0.561523 8.92969 0.431641C8.87109 0.302734 8.77344 0.193359 8.65234 0.117188C8.53125 0.0410156 8.39453 0 8.25 0ZM4.5 4.07812L2.35156 1.5L6.64844 1.5L4.5 4.07812Z" fill="#667085" fill-opacity="1.000000" fill-rule="nonzero"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 955 B |
@@ -0,0 +1,37 @@
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { IFetch } from "@/shared/types/fetch-type";
|
||||
import { IStatistics } from "@/shared/types/statistics-type";
|
||||
import { AxiosError } from "axios";
|
||||
import { create } from "zustand";
|
||||
|
||||
interface IStatisticsStore extends IFetch {
|
||||
getStatistics: (
|
||||
endpoint: string
|
||||
) => Promise<IStatistics[] | undefined>;
|
||||
}
|
||||
|
||||
export const useStatisticsStore = create<IStatisticsStore>((set) => ({
|
||||
isLoading: false,
|
||||
error: "",
|
||||
|
||||
getStatistics: async (
|
||||
endpoint: string
|
||||
): Promise<IStatistics[] | undefined> => {
|
||||
try {
|
||||
set({ isLoading: true });
|
||||
const response = await apiInstance.get<IStatistics[]>(
|
||||
`/report/${endpoint}/stats`
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
set({ error: error.message });
|
||||
} else {
|
||||
set({ error: "an error ocured" });
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,73 @@
|
||||
.volunteers-table {
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
border: 1px solid rgb(213, 213, 213);
|
||||
border-radius: 6px;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
thead {
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
height: 76px;
|
||||
display: flex;
|
||||
background-color: rgb(244, 244, 244);
|
||||
align-items: center;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 100px 320px 210px 213px 222px 92px;
|
||||
align-items: center;
|
||||
|
||||
td {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-color: rgb(244, 244, 244);
|
||||
}
|
||||
|
||||
td,
|
||||
button {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
color: rgb(102, 112, 133);
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
padding: 0 20px;
|
||||
tr {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 320px 210px 213px 222px 92px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
border-bottom: 1px solid rgb(241, 244, 249);
|
||||
|
||||
td {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#volunteers-user-cell {
|
||||
color: rgb(72, 159, 225);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { IUserRatings } from "@/shared/types/user-rating-type";
|
||||
import "./VolunteersTable.scss";
|
||||
import Image from "next/image";
|
||||
import arrows from "@/shared/icons/arrows.svg";
|
||||
|
||||
interface IVolunteersTableProps {
|
||||
firstData: IUserRatings[] | string;
|
||||
}
|
||||
|
||||
const VolunteersTable: React.FC<IVolunteersTableProps> = ({
|
||||
firstData,
|
||||
}: IVolunteersTableProps) => {
|
||||
const params = [
|
||||
{ param: "№" },
|
||||
{ param: "Активист" },
|
||||
{ param: "Добавлено дорог", handleClick() {} },
|
||||
{ param: "Получено голосов", handleClick() {} },
|
||||
{ param: "Оставлено голосов", handleClick() {} },
|
||||
{ param: "Рейтинг", handleClick() {} },
|
||||
];
|
||||
return (
|
||||
<div className="volunteers-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{params.map((param) => (
|
||||
<td key={param.param}>
|
||||
{param.handleClick ? (
|
||||
<button>
|
||||
{param.param}
|
||||
<Image src={arrows} alt="Arrows Icon" />
|
||||
</button>
|
||||
) : (
|
||||
<>{param.param}</>
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{typeof firstData === "string" ? (
|
||||
<h3>{firstData}</h3>
|
||||
) : (
|
||||
firstData.map((user, index) => (
|
||||
<tr key={user.user_id}>
|
||||
<td>{index + 1}</td>
|
||||
<td id="volunteers-user-cell">{user.username}</td>
|
||||
<td>{user.report_count}</td>
|
||||
<td>{user.likes_received_count}</td>
|
||||
<td>{user.likes_given_count}</td>
|
||||
<td>{user.average_rating}</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VolunteersTable;
|
||||