From fb9f1f36ec97b7d79a72342c183acd07c0cc0e61 Mon Sep 17 00:00:00 2001 From: Konstantin <“rudnevaketi@gmail.com”> Date: Tue, 1 Aug 2023 16:43:06 +0300 Subject: [PATCH 001/153] added profile layout --- src/components/Card/Card.module.scss | 255 +++++++++--------- .../userProfile/card-partner-avatar.png | Bin 0 -> 58409 bytes src/images/userProfile/edit.png | Bin 0 -> 445 bytes src/images/userProfile/flag.png | Bin 0 -> 563 bytes src/images/userProfile/help.png | Bin 0 -> 890 bytes src/images/userProfile/settings.png | Bin 0 -> 1063 bytes src/pages/Routing.tsx | 2 + src/pages/UserProfile/About/About.jsx | 21 ++ src/pages/UserProfile/About/About.module.scss | 35 +++ .../Buttons/EditButton/EditButton.jsx | 20 ++ .../Buttons/EditButton/EditButton.module.scss | 21 ++ .../Buttons/IconButton/IconButton.jsx | 19 ++ .../Buttons/IconButton/IconButton.module.scss | 11 + src/pages/UserProfile/Reviews/Reviews.jsx | 10 + src/pages/UserProfile/Topics/Topics.jsx | 21 ++ .../UserProfile/Topics/Topics.module.scss | 40 +++ src/pages/UserProfile/UserCard/UserCard.jsx | 50 ++++ .../UserProfile/UserCard/UserCard.module.scss | 99 +++++++ .../UserLanguages/UserLanguages.jsx | 23 ++ .../UserLanguages/UserLanguages.module.scss | 36 +++ src/pages/UserProfile/UserProfile.jsx | 15 ++ src/pages/UserProfile/UserProfile.module.scss | 7 + src/vendor/root.css | 73 ++--- 23 files changed, 592 insertions(+), 166 deletions(-) create mode 100644 src/images/userProfile/card-partner-avatar.png create mode 100644 src/images/userProfile/edit.png create mode 100644 src/images/userProfile/flag.png create mode 100644 src/images/userProfile/help.png create mode 100644 src/images/userProfile/settings.png create mode 100644 src/pages/UserProfile/About/About.jsx create mode 100644 src/pages/UserProfile/About/About.module.scss create mode 100644 src/pages/UserProfile/Buttons/EditButton/EditButton.jsx create mode 100644 src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss create mode 100644 src/pages/UserProfile/Buttons/IconButton/IconButton.jsx create mode 100644 src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss create mode 100644 src/pages/UserProfile/Reviews/Reviews.jsx create mode 100644 src/pages/UserProfile/Topics/Topics.jsx create mode 100644 src/pages/UserProfile/Topics/Topics.module.scss create mode 100644 src/pages/UserProfile/UserCard/UserCard.jsx create mode 100644 src/pages/UserProfile/UserCard/UserCard.module.scss create mode 100644 src/pages/UserProfile/UserLanguages/UserLanguages.jsx create mode 100644 src/pages/UserProfile/UserLanguages/UserLanguages.module.scss create mode 100644 src/pages/UserProfile/UserProfile.jsx create mode 100644 src/pages/UserProfile/UserProfile.module.scss diff --git a/src/components/Card/Card.module.scss b/src/components/Card/Card.module.scss index 911845e..dd99ea7 100644 --- a/src/components/Card/Card.module.scss +++ b/src/components/Card/Card.module.scss @@ -1,147 +1,142 @@ .card { - display: flex; - width: 336px; - padding: 16px; - flex-direction: column; - border-radius: 20px; - background: var(--color-base-white); - box-shadow: var(--card-shadow); - - &__countryAndStatusTag { - display: flex; - justify-content: space-between; - align-items: center; - } - - &__tag { - display: flex; - align-items: center; - } - - &__text { - font: var(--font-text-13-108-normal); - color: var(--grey-color-90); - } - - &__flag { - width: 16px; - height: 16px; - margin-right: 8px; - } - - &__indicator { - width: 6px; - height: 6px; - margin-right: 6px; - } - - &__partnerAbout { - display: flex; - align-items: center; - padding: 16px 0; - } - - &__partnerInfo { - display: flex; - flex-direction: column; - justify-content: space-between; - } - - &__partnerAvatar { - width: 80px; - height: 80px; - border-radius: 20px; - background-color: lightgray; - background-repeat: no-repeat; - } - - &__partnerGreeting { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - height: 40px; - overflow: hidden; - text-overflow: ellipsis; - font: var(--font-text-14-140-normal); - color: #9A98A8; - } - - &__partnerPersonalInfo { - height: 80px; - display: flex; - flex-direction: column; - justify-content: space-between; - margin-left: 12px; - - &_firstName { - font: var(--font-text-16-120-normal); - color: var(--grey-color-500); - } - - &_genderAndAge { - width: max-content; - display: flex; - padding: 3px 8px; - border-radius: 20px; - background: var(--grey-color-50); - margin-top: 4px; - } - - &_partnerGender { - width: 16px; - height: 16px; - } - - &_partnerAge { - margin-left: 8px; - font: var(--font-text-14-100-normal); - color: var(--soft-grey); - } - - &_languagesTag { - display: flex; - } - - &_languages { - display: flex; - } - - &_arrows { - width: 16px; - height: 16px; - margin: 0 8px; - } - } + display: flex; + width: 336px; + padding: 16px; + flex-direction: column; + border-radius: 20px; + background: var(--color-base-white); + box-shadow: var(--card-shadow); + + &__countryAndStatusTag { + display: flex; + justify-content: space-between; + align-items: center; + } + + &__tag { + display: flex; + align-items: center; + } + + &__text { + font: var(--font-text-13-108-normal); + color: var(--grey-color-90); + } + + &__flag { + width: 16px; + height: 16px; + margin-right: 8px; + } + + &__indicator { + width: 6px; + height: 6px; + margin-right: 6px; + } + + &__partnerAbout { + display: flex; + align-items: center; + padding: 16px 0; + } + + &__partnerInfo { + display: flex; + flex-direction: column; + justify-content: space-between; + } + + &__partnerAvatar { + width: 80px; + height: 80px; + border-radius: 20px; + background-color: lightgray; + background-repeat: no-repeat; + } + + &__partnerGreeting { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + height: 40px; + overflow: hidden; + text-overflow: ellipsis; + font: var(--font-text-14-140-normal); + color: #9a98a8; + } + + &__partnerPersonalInfo { + height: 80px; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-left: 12px; + + &_firstName { + font: var(--font-text-16-120-normal); + color: var(--grey-color-500); + } + + &_genderAndAge { + width: max-content; + display: flex; + padding: 3px 8px; + border-radius: 20px; + background: var(--grey-color-50); + margin-top: 4px; + } + + &_partnerGender { + width: 16px; + height: 16px; + } + + &_partnerAge { + margin-left: 8px; + font: var(--font-text-14-100-normal); + color: var(--soft-grey); + } + + &_languagesTag { + display: flex; + } + + &_languages { + display: flex; + } + + &_arrows { + width: 16px; + height: 16px; + margin: 0 8px; + } + } } .languages { - display: flex; - padding: 0; + display: flex; + padding: 0; } .languageItem { - display: flex; - align-items: flex-end; - margin-left: 4px; - list-style-type: none; + display: flex; + align-items: flex-end; + margin-left: 4px; + list-style-type: none; } .languageItem:first-child { - margin-left: 0; + margin-left: 0; } .skillLevel { - width: 3px; - height: 19px; + width: 3px; + height: 19px; } .language { - margin-left: 2px; - font: var(--font-text-14-100-normal); - color: var(--grey-color-500); + margin-left: 2px; + font: var(--font-text-14-100-normal); + color: var(--grey-color-500); } - - - - - diff --git a/src/images/userProfile/card-partner-avatar.png b/src/images/userProfile/card-partner-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..1e05b897af03fe078f730aa0b1714ebfa2c5c977 GIT binary patch literal 58409 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�c}>{^7#9jui}Q0e;E@a!-mHOJ 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 literal 0 HcmV?d00001 diff --git a/src/images/userProfile/flag.png b/src/images/userProfile/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..096de83bcd7d5c251c51f76e4163f6393d00cdc3 GIT binary patch literal 563 zcmV-30?hr1P)Dd4iOVM96%ay# zBuSG7gTWi0PQ?n>*qx*6IYzH_^|s_&7Y3_2Mn^+<|A-j>v3EEWIXJ$&onD-C1IMam zNG;A|bgptXi}zdw!B~(DOfC)N`D}Fzeu9M%ggaZnD}F1Ww6^*(9Y0_y1~S!{p98~P ze53EA(KhWnjzw0#8jANP`(c|0YiK zlIiMJk{@CWB#I)2e?Y#17cxhn9~=6dNJEc822gp7fKH*d0|9nH24Fyy%AfeSy*IBZ zDfp9$=>4;!sJq>&H1PZVtbAFB3jUY%0>-#gzE~nsJ*3I&gu@;=IZvxZ+f?JwPmS71 zF?mOc3G$h6@?u9IHOpaDkykdSyyx{gINOcS{{knlpEXrDjZpvq002ovPDHLkV1l7_ B?r#79 literal 0 HcmV?d00001 diff --git a/src/images/userProfile/help.png b/src/images/userProfile/help.png new file mode 100644 index 0000000000000000000000000000000000000000..62393a27ec9bed693f0b97419638e3a876673d10 GIT binary patch literal 890 zcmV-=1BLvFP)?6YCB!kh^$qBD5W4(`UO(F5R<@g$n^z7`qj!Gn*tjC z7X>k91yEo)ff+>X8B>&5ImZBZabcep0;_1V32sA2v$t~QkAQ)$NHJ5j`{eu_L+ zj=hF77U+r=qaykcA=f1=x8CCZv;nZQGn$p33-{Hom0zd$ZFxCR55UWWV|gT)^xf@^UI77NI{F?0 z-%H^Sf`QS?a!%LxS*pdoG*>S3oxn)~G6r>k7;g5u<2mW?AIYcSizxW@?`Gy1`JjLs zD8!}yI}h_^OydQa5;TZ$>l(m;fwAqt!~u-Kl+?=^uS;lD% zs#2>-K*sE}dX%i>cd+OANmYTTRGgQVuo`?mx1ESd4C2C10ID|oNZf4&m7;XOpzs<5 z7JM@oYimi7h&1GO5s+wMX=y$QsCz5F5&X2{;E8m2_VVIQFjuanGE44-$|*|MWK&1= zViGXElMp@h(u4ltlJ$#N_SQO{mC>-?FB^fBP{kc5l#ERbEQo~(kVLC?8$Lr@vyyWu znVM9W;hWN!n$b(Cn8;XAhLXfmgZ%zTfreUy++P*YJdNioW+9D*f@39C%;}~y<#8Kp zQ~P;CbprwFQwMGn^)g}{CI*i6CHbkzNH3B)BqwY2Gfn%b~Orid!OvVQHR7i_$X01BCHqm-9a1pH6?2{SiX=`n-b QCIA2c07*qoM6N<$g6)cy0{{R3 literal 0 HcmV?d00001 diff --git a/src/images/userProfile/settings.png b/src/images/userProfile/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..14c6f05a6f0a1b73e87070cd86e4c54c558a93a4 GIT binary patch literal 1063 zcmV+?1laqDP)V^E}Bw6ukmE}GUf z#vfEr(5MT+EEE-gxT0p_4=C!wh2WDJFt`dt!6a|q^}F{ygQDcUOco-{2k+f`?$0^j zJ@?#u4^T%Pb=2`{AutcP`N9fV*u|=28Wbv2xmK12PM}7lFdqo);eVV={s?8!38gf_ z7SxEEJ>-7g-Hfz-p9FekY4Cs3*4ne4r1~*keOF%DHijCI*h6kxVIzyxT*GY$b%NW| zL)nK2&dm%G)){)sN7_2>wio)K#5a^bX3rrxvgO|-b`p7r%DENT&*}w!i}}69&w~mI zvb6Q+b7NcAbpX~bYHo(vPTb=61&MtxM+W!D*@`9Px<4$yWYb-GWsC}!xo&qOCao+Q z-o7K{@4lPwa$u)&RFy*2z9>igJu_$O%@2|BJt?<6q<2eHl|{y0B4z*hZ1&=}Y^tfl ztX7CsTGR$u+}1|j*1~4?b$)5){Kq-zw$Jm!c=tyrNgtCvk(WN zkFrtPX;%IelTH7yuZMBc4^)={(@R+2VcK+G1T#nA+epWFoH;vgfLFd;aawY3Q0WE) znFW+jx%plPmO7vb@XlH9m9LC><*N_8%B5pYHs7d8ISbqNd6m&|XW^HcT#+&K7f<8v zID7FPB6vc6W-&|x9pN&n?WPpi`odu04?%~5?dOcFpyv!T{(eCMBqH<-!dOv3H5C1q z#9+#gh@_(iA9Fw=LT-EUZx$=*k?@EI=}}`yr8-|2tb%pnnA8PR3Z>_)jC8~zHipc5 zC9sn#9znS?HcR7rcL)nFmo&n8A0(b ziZvI9jD4E_iW2HiE$WNWZ7VJ|#!6cX2<-RCb`7^?HG=7nOdA|uk1?01A)>aE%osg< zG|pVIr=zWB4TtW-vrmUMfMD%QpAKPd2aGr7{B*>|kOuet24m$Bw`3{Bvs}i1H1H`6 z7oC>;5wCpdK}fuNDTK`j7JPugt3m{NoI$T+GwL7QL3$aK|Yh(i1pH83T? hkEo-LI_h|h@dTN import('./SignInPage/SignInPage')); const SignUpPage = lazy(() => import('./SignUpPage/SignUpPage')); const MainPage = lazy(() => import('./MainPage/MainPage')); +const UserProfile = lazy(() => import('../pages/UserProfile/UserProfile')); const NotFoundPage = lazy(() => import('./NotFoundPage/NotFoundPage')); export const Routing = () => { @@ -21,6 +22,7 @@ export const Routing = () => { } /> } /> } /> + } /> } /> ); diff --git a/src/pages/UserProfile/About/About.jsx b/src/pages/UserProfile/About/About.jsx new file mode 100644 index 0000000..f94c9f8 --- /dev/null +++ b/src/pages/UserProfile/About/About.jsx @@ -0,0 +1,21 @@ +import styles from "./About.module.scss"; + + +const About = () => { + return( + <> +
+
+

Обо мне

+

Hi! I work as an interior designer and often meet with foreign customers, I want to improve my English to communicate fluently. I am looking for a person who could guide me and correct pronunciation mistakes.

+
+
+

Я изучаю язык, чтобы

+

improve conversational level

+
+
+ + ); +}; + +export default About; \ No newline at end of file diff --git a/src/pages/UserProfile/About/About.module.scss b/src/pages/UserProfile/About/About.module.scss new file mode 100644 index 0000000..668eed0 --- /dev/null +++ b/src/pages/UserProfile/About/About.module.scss @@ -0,0 +1,35 @@ +.about { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; + background-color: var(--color-base-white); + border-radius: 20px; + margin: 16px 16px 16px 0; + padding: 20px; +} + +.about__section { + margin-bottom: 34px; +} + +.about__title { + font-family: var(--font-sans); + font-size: 18px; + font-weight: 500; + line-height: 25px; + letter-spacing: 0em; + text-align: left; + margin-bottom: 16px; + color: var(--dark-violet); +} + +.about__subtitle { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 22px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet-gradient); +} diff --git a/src/pages/UserProfile/Buttons/EditButton/EditButton.jsx b/src/pages/UserProfile/Buttons/EditButton/EditButton.jsx new file mode 100644 index 0000000..b166ad9 --- /dev/null +++ b/src/pages/UserProfile/Buttons/EditButton/EditButton.jsx @@ -0,0 +1,20 @@ +import icon from '../../../../images/userProfile/edit.png'; +import styles from "./EditButton.module.scss"; + +const EditButton = () => { + + const handleButtonClick = () => { + // Действия + }; + + return( + <> + + + ); +}; + +export default EditButton; \ No newline at end of file diff --git a/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss b/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss new file mode 100644 index 0000000..3deddb7 --- /dev/null +++ b/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss @@ -0,0 +1,21 @@ +.profile__button { + display: flex; + align-items: center; + border: none; + background-color: var(--color-base-white); + color: var(--dark-violet); +} + +.profile__icon { + margin-right: 8px; +} + +.profile__text { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 19px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet); +} diff --git a/src/pages/UserProfile/Buttons/IconButton/IconButton.jsx b/src/pages/UserProfile/Buttons/IconButton/IconButton.jsx new file mode 100644 index 0000000..ac7ef0b --- /dev/null +++ b/src/pages/UserProfile/Buttons/IconButton/IconButton.jsx @@ -0,0 +1,19 @@ +import icon from '../../../../images/userProfile/settings.png'; +import styles from "./IconButton.module.scss"; + +const IconButton = () => { + + const handleButtonClick = () => { + // Действия + }; + + return( + <> + + + ) +} + +export default IconButton; \ No newline at end of file diff --git a/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss b/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss new file mode 100644 index 0000000..fac9a6c --- /dev/null +++ b/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss @@ -0,0 +1,11 @@ +.profile__button { + display: flex; + border: none; + background-color: var(--color-base-white); + color: var(--dark-violet); +} + +.profile__icon { + width: 36px; + height: 36px; +} diff --git a/src/pages/UserProfile/Reviews/Reviews.jsx b/src/pages/UserProfile/Reviews/Reviews.jsx new file mode 100644 index 0000000..9bc1fe0 --- /dev/null +++ b/src/pages/UserProfile/Reviews/Reviews.jsx @@ -0,0 +1,10 @@ +const Reviews = () => { + return( +
+

Отзывы

+

У тебя пока нет отзывов, начни общаться и через неделю твой собеседник сможет оставить тут отзыв.

+
+ ); +}; + +export default Reviews; \ No newline at end of file diff --git a/src/pages/UserProfile/Topics/Topics.jsx b/src/pages/UserProfile/Topics/Topics.jsx new file mode 100644 index 0000000..506cb72 --- /dev/null +++ b/src/pages/UserProfile/Topics/Topics.jsx @@ -0,0 +1,21 @@ +import styles from "./Topics.module.scss"; + +const Topics = () => { + + const array = ['Русская культура', 'Путешествия', 'Дизайн', 'Кулинария', 'Книги', 'Языки', 'Спорт']; + + return( + <> +
+

Темы для общения

+
+ {array.map((item, index) => ( + {item} + ))} +
+
+ + ); +}; + +export default Topics; \ No newline at end of file diff --git a/src/pages/UserProfile/Topics/Topics.module.scss b/src/pages/UserProfile/Topics/Topics.module.scss new file mode 100644 index 0000000..819837e --- /dev/null +++ b/src/pages/UserProfile/Topics/Topics.module.scss @@ -0,0 +1,40 @@ +.topics { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 454px; + background-color: var(--color-base-white); + border-radius: 20px; + margin: 16px 16px 16px 0; + padding: 20px; +} + +.title { + font-family: var(--font-sans); + font-size: 18px; + font-weight: 500; + line-height: 25px; + letter-spacing: 0em; + text-align: left; + margin-bottom: 16px; + color: var(--dark-violet); +} + +.lystThemes { + display: flex; + flex-wrap: wrap; +} + +.themes { + border: 1px solid #c4cad4; + border-radius: 20px; + padding: 8px 12px; + font-family: var(--font-sans); + font-size: 15px; + font-weight: 400; + line-height: 18px; + letter-spacing: 0em; + text-align: left; + margin: 0 12px 12px 0; + color: var(--dark-violet); +} diff --git a/src/pages/UserProfile/UserCard/UserCard.jsx b/src/pages/UserProfile/UserCard/UserCard.jsx new file mode 100644 index 0000000..c7f4f91 --- /dev/null +++ b/src/pages/UserProfile/UserCard/UserCard.jsx @@ -0,0 +1,50 @@ +import EditButton from '../Buttons/EditButton/EditButton'; +import IconButton from '../Buttons/IconButton/IconButton'; +import UserLanguages from '../UserLanguages/UserLanguages'; +import Topics from '../Topics/Topics'; +import About from '../About/About'; +import Reviews from '../Reviews/Reviews'; +import cardPartnerAvatar from '../../../images/userProfile/card-partner-avatar.png'; +import cardPartnerFlag from '../../../images/userProfile/flag.png'; +import styles from "./UserCard.module.scss"; + +const UserCard = () => { + return( +
+
+
+
+
+ Аватар пользователя +
+
+

Светлана

+

женщина, 33

+
+
+ Флаг страны пользователя +

Россия, Москва

+
+
+
+
+ + +
+
+
+
+
+ + +
+
+ +
+
+ +
+ ); +}; + +export default UserCard; \ No newline at end of file diff --git a/src/pages/UserProfile/UserCard/UserCard.module.scss b/src/pages/UserProfile/UserCard/UserCard.module.scss new file mode 100644 index 0000000..abced1b --- /dev/null +++ b/src/pages/UserProfile/UserCard/UserCard.module.scss @@ -0,0 +1,99 @@ +.profile__card { + width: 1158px; + max-height: 698px; + margin: 0 auto; +} + +.profile__accent { + height: 77px; + background-color: #6d47d1; + border-top-left-radius: 20px; + border-top-right-radius: 20px; +} + +.profile__generalInfo { + display: flex; + align-items: start; + justify-content: space-between; + height: 228px; + background-color: var(--color-base-white); + border-top-left-radius: 20px; + border-top-right-radius: 20px; + border-bottom-left-radius: 20px; + border-bottom-right-radius: 20px; + margin-top: -20px; +} + +.profile__partnerAbout { + display: flex; + align-items: center; + height: 100%; + padding: 16px 0; +} + +.profile__partnerAvatar { + width: 180px; + height: 100%; + border-radius: 20px; + margin: 24px 24px 24px; + background-repeat: no-repeat; +} + +.profile__partnerInfo { + display: flex; + flex-direction: column; + justify-content: flex-end; + height: 100%; +} + +.profile__name { + font-family: var(--font-sans); + font-size: 32px; + font-weight: 400; + line-height: 39px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet); +} + +.profile__sex { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 19px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet-gradient); +} + +.profile__country { + display: flex; + align-items: center; + padding-top: 24px; +} + +.profile__flag { + width: 16px; + height: 16px; + margin-right: 8px; +} + +.profile__city { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 22px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet-gradient); +} + +.profile__buttons { + display: flex; + align-items: center; + margin-top: 24px; +} + +.profile__moreAbout { + display: flex; +} diff --git a/src/pages/UserProfile/UserLanguages/UserLanguages.jsx b/src/pages/UserProfile/UserLanguages/UserLanguages.jsx new file mode 100644 index 0000000..a501143 --- /dev/null +++ b/src/pages/UserProfile/UserLanguages/UserLanguages.jsx @@ -0,0 +1,23 @@ +import help from "../../../images/userProfile/help.png"; +import styles from "./UserLanguages.module.scss"; + + +const UserLanguages = () => { + return( + <> +
+
+

Свободный

+

Русский

+
+
+

Изучаю

+

Английский

+
+ Флаг страны пользователя +
+ + ) +} + +export default UserLanguages; \ No newline at end of file diff --git a/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss b/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss new file mode 100644 index 0000000..8f4a6c5 --- /dev/null +++ b/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss @@ -0,0 +1,36 @@ +.profile__languages { + display: flex; + justify-content: space-between; + width: 454px; + height: 107px; + background-color: var(--color-base-white); + border-radius: 20px; + margin: 16px 16px 16px 0; + padding: 20px; +} + +.profile__title { + font-family: var(--font-sans); + font-size: 18px; + font-weight: 500; + line-height: 25px; + letter-spacing: 0em; + text-align: left; + margin-bottom: 16px; + color: var(--dark-violet); +} + +.profile__subtitle { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 14px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet-gradient); +} + +.profile__help { + width: 20px; + height: 20px; +} diff --git a/src/pages/UserProfile/UserProfile.jsx b/src/pages/UserProfile/UserProfile.jsx new file mode 100644 index 0000000..80fac7d --- /dev/null +++ b/src/pages/UserProfile/UserProfile.jsx @@ -0,0 +1,15 @@ +import Header from '../../components/Header/Header'; +import Footer from "../../components/Footer/Footer"; +import UserCard from './UserCard/UserCard'; +import styles from "./UserProfile.module.scss"; + + +const UserProfile = () => { + return( +
+ +
+ ); +}; + +export default UserProfile; \ No newline at end of file diff --git a/src/pages/UserProfile/UserProfile.module.scss b/src/pages/UserProfile/UserProfile.module.scss new file mode 100644 index 0000000..f524463 --- /dev/null +++ b/src/pages/UserProfile/UserProfile.module.scss @@ -0,0 +1,7 @@ +.profile { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + background-color: var(--grey-color-50); +} diff --git a/src/vendor/root.css b/src/vendor/root.css index 0f4a0ed..b7f1ac7 100644 --- a/src/vendor/root.css +++ b/src/vendor/root.css @@ -1,38 +1,39 @@ :root { - /* Typography */ - --font-sans: "Inter", sans-serif; - - /* Color scheme */ - - /* Colors */ - /*Старые для путей пользователя, потом убрать*/ - --color-primary: #1172CB; - --color-background-header: #9CCAF4; - --color-background-content: #C4DCF2; - --color-input-placeholder: #8FA1B2; - - /*По макету*/ - --soft-grey: #9A98A8; - --grey-color-50: #F0F2F4; - --grey-color-90: #909090; - --grey-color-500: #323944; - - /* Base */ - --color-base-black: #000; - --color-base-white: #fff; - - /* Shadows */ - --card-shadow: 0px 0px 8px 0px rgba(17, 17, 26, 0.10), 0px 1px 0px 0px rgba(17, 17, 26, 0.05); - - /* Typography */ - /*Старые для путей пользователя, потом убрать*/ - --font-text-xl-regular: 400 36px/43.57px var(--font-sans); - --font-text-lg-regular: 400 24px/29.05px var(--font-sans); - - /*По макету*/ - --font-text-13-108-normal: 400 13px/14px var(--font-sans); - --font-text-14-100-normal: 400 14px/14px var(--font-sans); - --font-text-14-140-normal: 400 14px/1.4 var(--font-sans); - --font-text-16-120-normal: 400 16px/1.2 var(--font-sans); - + /* Typography */ + --font-sans: 'Inter', sans-serif; + + /* Color scheme */ + + /* Colors */ + /*Старые для путей пользователя, потом убрать*/ + --color-primary: #1172cb; + --color-background-header: #9ccaf4; + --color-background-content: #c4dcf2; + --color-input-placeholder: #8fa1b2; + + /*По макету*/ + --soft-grey: #9a98a8; + --grey-color-50: #f0f2f4; + --grey-color-90: #909090; + --grey-color-500: #323944; + --dark-violet: #403a6d; + --dark-violet-gradient: #66618a; + + /* Base */ + --color-base-black: #000; + --color-base-white: #fff; + + /* Shadows */ + --card-shadow: 0px 0px 8px 0px rgba(17, 17, 26, 0.1), 0px 1px 0px 0px rgba(17, 17, 26, 0.05); + + /* Typography */ + /*Старые для путей пользователя, потом убрать*/ + --font-text-xl-regular: 400 36px/43.57px var(--font-sans); + --font-text-lg-regular: 400 24px/29.05px var(--font-sans); + + /*По макету*/ + --font-text-13-108-normal: 400 13px/14px var(--font-sans); + --font-text-14-100-normal: 400 14px/14px var(--font-sans); + --font-text-14-140-normal: 400 14px/1.4 var(--font-sans); + --font-text-16-120-normal: 400 16px/1.2 var(--font-sans); } From 29dd830af4d3ac7cdf76399b0ce5267bc362d072 Mon Sep 17 00:00:00 2001 From: Keti <“rudnevaketi@gmail.com”> Date: Wed, 2 Aug 2023 03:28:44 +0300 Subject: [PATCH 002/153] added styles for reviews --- src/pages/UserProfile/Reviews/Reviews.jsx | 8 ++++--- .../UserProfile/Reviews/Reviews.module.scss | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/pages/UserProfile/Reviews/Reviews.module.scss diff --git a/src/pages/UserProfile/Reviews/Reviews.jsx b/src/pages/UserProfile/Reviews/Reviews.jsx index 9bc1fe0..88443e5 100644 --- a/src/pages/UserProfile/Reviews/Reviews.jsx +++ b/src/pages/UserProfile/Reviews/Reviews.jsx @@ -1,8 +1,10 @@ +import styles from "./Reviews.module.scss"; + const Reviews = () => { return( -
-

Отзывы

-

У тебя пока нет отзывов, начни общаться и через неделю твой собеседник сможет оставить тут отзыв.

+
+

Отзывы

+

У тебя пока нет отзывов, начни общаться и через неделю твой собеседник сможет оставить тут отзыв.

); }; diff --git a/src/pages/UserProfile/Reviews/Reviews.module.scss b/src/pages/UserProfile/Reviews/Reviews.module.scss new file mode 100644 index 0000000..625e908 --- /dev/null +++ b/src/pages/UserProfile/Reviews/Reviews.module.scss @@ -0,0 +1,24 @@ +.review { + width: 100%; + padding-top: 32px; + + &__title { + font-family: var(--font-sans); + font-size: 18px; + font-weight: 500; + line-height: 22px; + letter-spacing: 0em; + text-align: left; + padding-bottom: 16px; + color: var(--dark-violet); + } + &__text { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 26px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet-gradient); + } +} From b1cf571e06610725f61eb8e6f8a1d540d1d6eacb Mon Sep 17 00:00:00 2001 From: Keti <“rudnevaketi@gmail.com”> Date: Wed, 2 Aug 2023 04:39:30 +0300 Subject: [PATCH 003/153] added LevelLanguage --- .../Buttons/EditButton/EditButton.module.scss | 29 +++++++++++-------- .../Buttons/IconButton/IconButton.module.scss | 13 ++++++--- .../LevelLanguage/LevelLanguage.jsx | 9 ++++++ .../LevelLanguage/LevelLanguage.module.scss | 8 +++++ .../UserLanguages/UserLanguages.jsx | 22 ++++++++++++-- .../UserLanguages/UserLanguages.module.scss | 12 ++++++++ src/vendor/root.css | 1 + 7 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 src/pages/UserProfile/LevelLanguage/LevelLanguage.jsx create mode 100644 src/pages/UserProfile/LevelLanguage/LevelLanguage.module.scss diff --git a/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss b/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss index 3deddb7..edf82c0 100644 --- a/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss +++ b/src/pages/UserProfile/Buttons/EditButton/EditButton.module.scss @@ -4,18 +4,23 @@ border: none; background-color: var(--color-base-white); color: var(--dark-violet); -} -.profile__icon { - margin-right: 8px; -} + &:hover { + cursor: pointer; + opacity: 0.7; + } -.profile__text { - font-family: var(--font-sans); - font-size: 16px; - font-weight: 400; - line-height: 19px; - letter-spacing: 0em; - text-align: left; - color: var(--dark-violet); + &__icon { + margin-right: 8px; + } + + &__text { + font-family: var(--font-sans); + font-size: 16px; + font-weight: 400; + line-height: 19px; + letter-spacing: 0em; + text-align: left; + color: var(--dark-violet); + } } diff --git a/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss b/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss index fac9a6c..fac3ded 100644 --- a/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss +++ b/src/pages/UserProfile/Buttons/IconButton/IconButton.module.scss @@ -3,9 +3,14 @@ border: none; background-color: var(--color-base-white); color: var(--dark-violet); -} -.profile__icon { - width: 36px; - height: 36px; + &:hover { + cursor: pointer; + opacity: 0.7; + } + + &__icon { + width: 36px; + height: 36px; + } } diff --git a/src/pages/UserProfile/LevelLanguage/LevelLanguage.jsx b/src/pages/UserProfile/LevelLanguage/LevelLanguage.jsx new file mode 100644 index 0000000..af80feb --- /dev/null +++ b/src/pages/UserProfile/LevelLanguage/LevelLanguage.jsx @@ -0,0 +1,9 @@ +import styles from './LevelLanguage.module.scss'; + +const LevelLanguage = () => { + return( +
+ ); +}; + +export default LevelLanguage; \ No newline at end of file diff --git a/src/pages/UserProfile/LevelLanguage/LevelLanguage.module.scss b/src/pages/UserProfile/LevelLanguage/LevelLanguage.module.scss new file mode 100644 index 0000000..2cda8e4 --- /dev/null +++ b/src/pages/UserProfile/LevelLanguage/LevelLanguage.module.scss @@ -0,0 +1,8 @@ +.level { + width: 4px; + height: 26px; + border-radius: 4px; + margin-right: 3px; + background-color: var(--rose-violet); + border: 2px solid violet; +} diff --git a/src/pages/UserProfile/UserLanguages/UserLanguages.jsx b/src/pages/UserProfile/UserLanguages/UserLanguages.jsx index a501143..77aba99 100644 --- a/src/pages/UserProfile/UserLanguages/UserLanguages.jsx +++ b/src/pages/UserProfile/UserLanguages/UserLanguages.jsx @@ -1,18 +1,36 @@ +import LevelLanguage from "../LevelLanguage/LevelLanguage"; import help from "../../../images/userProfile/help.png"; import styles from "./UserLanguages.module.scss"; const UserLanguages = () => { + + const levelsArray = Array.from({ length: 6 }); + return( <>

Свободный

-

Русский

+
+

Русский

+
+ {levelsArray.map((_, index) => { + return ; + })} +
+

Изучаю

-

Английский

+
+

Английский

+
+ {levelsArray.map((_, index) => { + return ; + })} +
+
Флаг страны пользователя
diff --git a/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss b/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss index 8f4a6c5..484c5a8 100644 --- a/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss +++ b/src/pages/UserProfile/UserLanguages/UserLanguages.module.scss @@ -20,6 +20,18 @@ color: var(--dark-violet); } +.profile__language { + min-width: 161px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.profile__levels { + display: flex; + justify-content: space-between; +} + .profile__subtitle { font-family: var(--font-sans); font-size: 16px; diff --git a/src/vendor/root.css b/src/vendor/root.css index b7f1ac7..ce319b0 100644 --- a/src/vendor/root.css +++ b/src/vendor/root.css @@ -16,6 +16,7 @@ --grey-color-50: #f0f2f4; --grey-color-90: #909090; --grey-color-500: #323944; + --rose-violet: #ca60d3; --dark-violet: #403a6d; --dark-violet-gradient: #66618a; From 4456e1574708ca775561311c4fb574ea70f5ae45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Wed, 2 Aug 2023 11:19:46 +0300 Subject: [PATCH 004/153] =?UTF-8?q?Fix=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=B3=D0=BE=20=D1=8F?= =?UTF-8?q?=D0=B7=D1=8B=D0=BA=D0=B0=20=D0=B8=20=D1=83=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=BD=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Sort/Sort.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 3af91e5..a521566 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -15,12 +15,18 @@ interface SortProps { //const [open, setOpen] = useState(false); const [leftValue, setLeftValue] = useState(18); const [rightValue, setRightValue] = useState(40); + const [isLanguageMenuOpen, setLanguageMenuOpen] = useState(false); const handleSliderChange = (left: number, right: number) => { setLeftValue(left); setRightValue(right); }; + const handleOpenLanguageMenu = () => { + setLanguageMenuOpen(true); + }; + + return (
@@ -36,8 +42,10 @@ interface SortProps {

Язык партнера

+ {isLanguageMenuOpen && }

Язык партнера

- - {isLanguageMenuOpen && } + handleAddLanguage(language)} + onRemove={(language) => handleRemoveLanguage(language)} + /> + {selectedLanguages.map((language, index) => ( + handleAddLanguage(language)} + onRemove={(language) => handleRemoveLanguage(language)} + /> + ))} + {isLanguageMenuOpen && ( + handleAddLanguage(name)} /> + )}
+ - ); +

Язык партнера

+ + {selectedLanguages.map((language, index) => ( + + ))} + {isLanguageMenuOpen && ( + + )} +
+
+
+

О партнере

+
+

Пол

+
+
+
+

Возраст

+ +
+ +
Date: Thu, 3 Aug 2023 19:52:35 +0300 Subject: [PATCH 013/153] fix: modal component --- src/components/InfoTooltip/InfoTooltip.scss | 57 ------- src/components/InfoTooltip/InfoTooltip.tsx | 63 -------- src/components/Modal/Modal.module.scss | 53 +++++++ src/components/Modal/Modal.tsx | 139 +++++++++--------- src/components/Modal/modal.css | 46 ------ .../ModalOverlay/ModalOverlay.module.scss | 60 -------- src/components/ModalOverlay/ModalOverlay.tsx | 14 -- src/components/SignupSigninForm/model.ts | 7 +- src/pages/MainPage/MainPage.module.scss | 102 +++---------- src/pages/MainPage/MainPage.tsx | 77 ++-------- 10 files changed, 161 insertions(+), 457 deletions(-) delete mode 100644 src/components/InfoTooltip/InfoTooltip.scss delete mode 100644 src/components/InfoTooltip/InfoTooltip.tsx create mode 100644 src/components/Modal/Modal.module.scss delete mode 100644 src/components/Modal/modal.css delete mode 100644 src/components/ModalOverlay/ModalOverlay.module.scss delete mode 100644 src/components/ModalOverlay/ModalOverlay.tsx diff --git a/src/components/InfoTooltip/InfoTooltip.scss b/src/components/InfoTooltip/InfoTooltip.scss deleted file mode 100644 index 6328793..0000000 --- a/src/components/InfoTooltip/InfoTooltip.scss +++ /dev/null @@ -1,57 +0,0 @@ -.infoTooltip { - width: 100%; - - &__container { - width: 501px; - display: flex; - flex-direction: column; - position: relative; - background-color: var(--color-base-white); - border-radius: 20px; - padding: 60px 40px; - margin: 0; - } - - &__closeButton { - padding: 0; - width: 32px; - height: 32px; - position: absolute; - right: -16px; - top: -16px; - border: none; - background-image: url(../../images/svg/modal-close-button.svg); - background-repeat: no-repeat; - background-size: 100% 100%; - background-position: center; - cursor: pointer; - opacity: 1; - - &:hover { - opacity: 0.6; - transition: opacity 0.5s ease; - } - } -} - -.popup { - visibility: hidden; - opacity: 0; - z-index: -100; - display: flex; - background-color: rgba(0,0,0,0.5); - position: fixed; - min-width: 100vw; - min-height: 100%; - top: 0; - left: 0; - justify-content: center; - align-items: center; - - &_opened { - visibility: visible; - opacity: 1; - z-index: 100; - transition: visibility 0.5s ease, opacity 0.5s ease; - } -} diff --git a/src/components/InfoTooltip/InfoTooltip.tsx b/src/components/InfoTooltip/InfoTooltip.tsx deleted file mode 100644 index e7cf674..0000000 --- a/src/components/InfoTooltip/InfoTooltip.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, {useRef, useEffect, FC, ReactNode} from "react"; -import ReactDOM from 'react-dom' - -import ModalOverlay from "../ModalOverlay/ModalOverlay"; - -import styles from "./InfoTooltip.scss" -import cn from "classnames"; - -interface IInfoTooltipProps { - className?: string; - isOpen: boolean; - onClose: any; - children: ReactNode; -} - -const InfoTooltip: FC = ({ className, isOpen, onClose, children }) => { - const popupRef = useRef() as React.MutableRefObject; - - // useEffect(() => { - // popupRef.current.addEventListener("mousedown", (event: MouseEvent) => { - // const targetClasses = (event.target as Element).classList; - // if (targetClasses.contains("popup_opened")) { - // onClose(); - // } - // }); - // }, []); - - //Закрытие popup при нажатии на Esc - const handleCloseByEsc = (event: KeyboardEvent) => { - if (event.key === "Escape") { - onClose(); - } - }; - - useEffect(() => { - if (isOpen) { - // Список действий внутри одного хука - document.addEventListener("keydown", handleCloseByEsc); - // Возвращаем функцию, которая удаляет эффекты - return () => { - document.removeEventListener("keydown", handleCloseByEsc); - }; - } - }, [isOpen]); - - return ( -
-
- - {children} -
-
- ); -} - -export default InfoTooltip; diff --git a/src/components/Modal/Modal.module.scss b/src/components/Modal/Modal.module.scss new file mode 100644 index 0000000..4857223 --- /dev/null +++ b/src/components/Modal/Modal.module.scss @@ -0,0 +1,53 @@ +.modal { + visibility: hidden; + opacity: 0; + z-index: -100; + display: flex; + background-color: rgba(0, 0, 0, 0.5); + position: fixed; + min-width: 100vw; + min-height: 100%; + top: 0; + left: 0; + justify-content: center; + align-items: center; + + &_opened { + visibility: visible; + opacity: 1; + z-index: 100; + transition: visibility 0.5s ease, opacity 0.5s ease; + } + + &__container { + width: 501px; + display: flex; + flex-direction: column; + position: relative; + background-color: var(--color-base-white); + border-radius: 20px; + padding: 60px 40px; + margin: 0; + } + + &__closeButton { + padding: 0; + width: 32px; + height: 32px; + position: absolute; + right: 16px; + top: 16px; + border: none; + background-image: url(../../images/svg/modal-close-button.svg); + background-repeat: no-repeat; + background-size: 100% 100%; + background-position: center; + cursor: pointer; + opacity: 1; + + &:hover { + opacity: 0.6; + transition: opacity 0.5s ease; + } + } +} diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 28e239f..48bb56c 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -1,68 +1,71 @@ -import React, {FC, useRef} from "react"; -import ReactDOM from 'react-dom' -import ModalOverlay from '../ModalOverlay/ModalOverlay'; -import "./modal.css"; - -type ScriptEvent = () => void; - -const modalRoot = (document.getElementById("react-modals") as Element); - -interface IModalProps { - isOpen: boolean; - onClose: ScriptEvent; - children: React.ReactNode; -} - -const Modal: FC = ({ isOpen, onClose, children }) => { - - const overlay = useRef() as React.MutableRefObject; - - React.useEffect(() => { - const closeByClick = (event: MouseEvent) => { - if (event !== null && event.target) {if ((event.target as Element).classList.contains('modalOverlay')) { - handleClose(); - }} - }; - const element = overlay.current; - if (element && overlay && overlay.current) { - element.addEventListener('click', closeByClick); - return () => { - element.removeEventListener('click', closeByClick); - }; - } - }, []); - - function handleClose() { - onClose() - } - - React.useEffect(() => { - const escFunction = (event: KeyboardEvent) => { - if (event.key === "Escape") { - handleClose(); - } - }; - document.addEventListener("keydown", escFunction); - return () => { - document.removeEventListener("keydown", escFunction); - } - }, []) - - return ReactDOM.createPortal ( -
-
- -
-
-
-
-
- {children} -
-
, modalRoot - ); -} - -export default Modal; +import React, {useRef, useEffect, FC, ReactNode} from "react"; +import {observer} from "mobx-react-lite"; + +import styles from "./Modal.module.scss" +import cn from "classnames"; + +import {useModel} from "../SignupSigninForm/model"; + +interface ModalProps { + className?: string; + children?: ReactNode; +} + +const Modal: FC = ({ + className, + children + }) => { + const model = useModel(); + + const modalRef = useRef() as React.MutableRefObject; + + const setCloseByOverlayListener = (modal: any) => { + modal.addEventListener("mousedown", (event: MouseEvent) => { + const targetClasses = (event.target as Element).classList; + const regExp = /^(Modal_modal_opened__)[\w]?/; + for (let i = 0; i < targetClasses.length; i++) { + if (regExp.test(targetClasses[i])) { + model.handleCloseModal(); + } + } + }); + } + + const handleCloseByEsc = (event: KeyboardEvent) => { + if (event.key === "Escape") { + model.handleCloseModal(); + } + }; + + useEffect(() => { + setCloseByOverlayListener(modalRef.current); + }, []); + + // Закрытие popup при нажатии на Esc + useEffect(() => { + if (model.isModalOpen) { + // Список действий внутри одного хука + document.addEventListener("keydown", handleCloseByEsc); + // Возвращаем функцию, которая удаляет эффекты + return () => { + document.removeEventListener("keydown", handleCloseByEsc); + }; + } + }, [model.isModalOpen]); + + return ( +
+
+ + {children} +
+
+ ); +} + +export default observer(Modal); diff --git a/src/components/Modal/modal.css b/src/components/Modal/modal.css deleted file mode 100644 index 86ed382..0000000 --- a/src/components/Modal/modal.css +++ /dev/null @@ -1,46 +0,0 @@ -.modal__container { - min-width: 720px; - min-height: 539px; - background-color: #1C1C21; - display: flex; - align-items: center; - text-align: center; - padding: 40px 40px 40px 40px; - box-shadow: 0px 0px 25px rgba(0, 0, 0, 0.15); - border-radius: 40px; - flex-direction: column; - flex-basis: auto; - align-self: center; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 12; - box-sizing: border-box; -} - -@media screen and (max-width: 500px) { - .modal__container { - min-width: 282px; - min-height: 322px; - padding: 25px 22px; - } -} - -.modal__button-container svg { - justify-self: flex-end; - position: absolute; - right: 40px; - top: 60px; - z-index: 11; -} - -.modal { - visibility: hidden; - z-index: 11; -} - -.modal_opened { - visibility: visible; - z-index: 11; -} \ No newline at end of file diff --git a/src/components/ModalOverlay/ModalOverlay.module.scss b/src/components/ModalOverlay/ModalOverlay.module.scss deleted file mode 100644 index ed474af..0000000 --- a/src/components/ModalOverlay/ModalOverlay.module.scss +++ /dev/null @@ -1,60 +0,0 @@ -.modalOverlay { - display: flex; - width: 100%; - height: calc(100% + 270px); - position: absolute; - top: -119px; - left: 0; - justify-content: center; - align-items: flex-start; - background-color: 0, 0, 0, 0.6; - visibility: hidden; - transition: visibility, 400ms linear; - z-index: 11; -} - -.modalOverlay:before { - content: ''; - width: 100%; - height: 100%; - background-color: #e5e5e5; - opacity: 0.5; - align-self: center; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 11; -} - -@media screen and (max-width: 920px) { - .modalOverlay { - height: calc(100% + 270px); - } -} - -@media screen and (max-width: 770px) { - .modalOverlay { - height: calc(100% + 220px); - } -} - -@media screen and (max-width: 610px) { - .modalOverlay { - height: calc(100% + 170px); - } -} - -@media screen and (max-width: 610px) { - .modalOverlay { - top: -84px; - } -} - -.modalOverlay_opened { - display: flex; - opacity: 1; - z-index: 2; - visibility: visible; - z-index: 11; -} diff --git a/src/components/ModalOverlay/ModalOverlay.tsx b/src/components/ModalOverlay/ModalOverlay.tsx deleted file mode 100644 index 636f6e3..0000000 --- a/src/components/ModalOverlay/ModalOverlay.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import './ModalOverlay.module.scss'; - -interface IModalOverlayProps { - isOpen: boolean; -} - -function ModalOverlay(props: IModalOverlayProps) { - const { isOpen } = props; - - return
; -} - -export default ModalOverlay; diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 1d52f22..7a1ebcc 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -16,7 +16,7 @@ export const useModel = () => { email: "", password: "", confirmPassword: "", - isModalOpen: false, + isModalOpen: true, isLoggedIn: false, handleUsernameChange({value}: { value: string }) { @@ -39,6 +39,11 @@ export const useModel = () => { model.isModalOpen = false; }, + handleOpenModal() { + model.isModalOpen = true; + console.log(model.isModalOpen); + }, + async handleRegister(event: FormEvent) { event.preventDefault(); try { diff --git a/src/pages/MainPage/MainPage.module.scss b/src/pages/MainPage/MainPage.module.scss index e002769..1ef8f61 100644 --- a/src/pages/MainPage/MainPage.module.scss +++ b/src/pages/MainPage/MainPage.module.scss @@ -58,50 +58,12 @@ border: none; cursor: pointer; } - - &__modal { - &_container { - width: 501px; - display: flex; - flex-direction: column; - position: relative; - background-color: var(--color-base-white); - border-radius: 20px; - padding: 60px 40px; - margin: 0; - } - - &_header { - font: var(--font-header-24-120-600); - color: var(--dark-violet); - letter-spacing: 0.48px; - text-align: center; - } - - &_text { - &_main { - font: var(--font-text-18-140-400); - color: var(--dark-violet-gradient-400); - letter-spacing: 0.36px; - margin: 20px 0; - text-align: center; - } - - &_additional { - font: var(--font-text-16-120-400); - color: var(--grey-gradient-400); - font-style: italic; - letter-spacing: 0.32px; - text-align: center; - } - } - } } -.infoTooltip { +.modal { width: 100%; - &__container { + &_container { width: 501px; display: flex; flex-direction: column; @@ -112,48 +74,28 @@ margin: 0; } - &__closeButton { - padding: 0; - width: 32px; - height: 32px; - position: absolute; - right: 16px; - top: 16px; - border: none; - background-image: url(../../images/svg/modal-close-button.svg); - background-color: var(--color-base-white); - background-repeat: no-repeat; - background-size: 100% 100%; - background-position: center; - cursor: pointer; - opacity: 1; - - &:hover { - opacity: 0.6; - transition: opacity 0.5s ease; - } + &_header { + font: var(--font-header-24-120-600); + color: var(--dark-violet); + letter-spacing: 0.48px; + text-align: center; } -} -.popup { - visibility: hidden; - opacity: 0; - z-index: -100; - display: flex; - background-color: rgba(0,0,0,0.5); - position: fixed; - min-width: 100vw; - min-height: 100%; - top: 0; - left: 0; - justify-content: center; - align-items: center; + &_text { + &_main { + font: var(--font-text-18-140-400); + color: var(--dark-violet-gradient-400); + letter-spacing: 0.36px; + margin: 20px 0; + text-align: center; + } - &_opened { - visibility: visible; - opacity: 1; - z-index: 100; - transition: visibility 0.5s ease, opacity 0.5s ease; + &_additional { + font: var(--font-text-16-120-400); + color: var(--grey-gradient-400); + font-style: italic; + letter-spacing: 0.32px; + text-align: center; + } } } - diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 430dad2..417a10b 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {api} from '../../utils/constants'; @@ -8,31 +8,24 @@ import Categories from "../../components/Categories/Categories"; import Sort from "../../components/Sort/Sort"; import Footer from '../../components/Footer/Footer'; import {Button} from '../../components/UI/Button/Button'; +import Modal from "../../components/Modal/Modal"; import styles from './MainPage.module.scss'; import cn from "classnames"; import {useModel} from "../../components/SignupSigninForm/model"; -import {useNavigate, useParams} from "react-router-dom"; const MainPage = () => { - const model = useModel(); - - const [usersList, setUsersList] = useState([]); const [isUsersList, setIsUsersList] = useState(false); const [category, setCategory] = useState({name: 'Все', path: ''}); const [sortType, setSortType] = useState({}); const [isSortPopupOpen, setSortPopupOpen] = useState(true); - const isModalOpen = model.isModalOpen; - const handleOpenSortPopup = () => { setSortPopupOpen(!isSortPopupOpen); - console.log(isSortPopupOpen); } - const getUsersList = async () => { try { console.log('отправка запроса ---'); @@ -57,41 +50,6 @@ const MainPage = () => { getUsersList(); }, [category, sortType]); - const popupRef = useRef() as React.MutableRefObject; - - useEffect(() => { - popupRef.current.addEventListener("mousedown", (event: MouseEvent) => { - const targetClasses = (event.target as Element).classList; - if (targetClasses.contains("popup_opened")) { - model.handleCloseModal(); - console.log(targetClasses); - console.log(model.isModalOpen); - } - }); - }, [model.isModalOpen]); - - // Закрытие popup при нажатии на Esc - const handleCloseByEsc = (event: KeyboardEvent) => { - if (event.key === "Escape") { - model.handleCloseModal(); - console.log(popupRef); - const targetClasses = (event.target as Element).classList; - console.log(targetClasses); - console.log(model.isModalOpen); - } - }; - - useEffect(() => { - if (isModalOpen) { - // Список действий внутри одного хука - document.addEventListener("keydown", handleCloseByEsc); - // Возвращаем функцию, которая удаляет эффекты - return () => { - document.removeEventListener("keydown", handleCloseByEsc); - }; - } - }, [model.isModalOpen]); - return ( <>
@@ -134,31 +92,14 @@ const MainPage = () => {
-
-
- -

Подтвердите адрес электронной почты

-

Пожалуйста, проверьте электронную почту, которую - указали - при регистрации, и перейдите по ссылке для подтверждения

-

Ссылка будет активна в течении 24 часов

-
-
+ +

Подтвердите адрес электронной почты

+

Пожалуйста, проверьте электронную почту, которую + указали + при регистрации, и перейдите по ссылке для подтверждения

+

Ссылка будет активна в течении 24 часов

+
- {/**/} - {/*

Подтвердите адрес электронной почты

*/} - {/*

Пожалуйста, проверьте электронную почту, которую указали*/} - {/* при регистрации, и перейдите по ссылке для подтверждения

*/} - {/*

Ссылка будет активна в течении 24 часов

*/} - {/*
*/} ); }; From 720c9d1f5802b1a904d896fe03eabeb7a5803ca8 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Thu, 3 Aug 2023 21:52:08 +0300 Subject: [PATCH 014/153] fix: modal and main components --- src/components/Modal/Modal.module.scss | 20 ------ src/components/Modal/Modal.tsx | 64 ++++--------------- .../ModalOverlay/ModalOverlay.module.scss | 21 ++++++ src/components/ModalOverlay/ModalOverlay.tsx | 56 ++++++++++++++++ src/pages/MainPage/MainPage.tsx | 22 ++++--- 5 files changed, 102 insertions(+), 81 deletions(-) create mode 100644 src/components/ModalOverlay/ModalOverlay.module.scss create mode 100644 src/components/ModalOverlay/ModalOverlay.tsx diff --git a/src/components/Modal/Modal.module.scss b/src/components/Modal/Modal.module.scss index 4857223..442760e 100644 --- a/src/components/Modal/Modal.module.scss +++ b/src/components/Modal/Modal.module.scss @@ -1,24 +1,4 @@ .modal { - visibility: hidden; - opacity: 0; - z-index: -100; - display: flex; - background-color: rgba(0, 0, 0, 0.5); - position: fixed; - min-width: 100vw; - min-height: 100%; - top: 0; - left: 0; - justify-content: center; - align-items: center; - - &_opened { - visibility: visible; - opacity: 1; - z-index: 100; - transition: visibility 0.5s ease, opacity 0.5s ease; - } - &__container { width: 501px; display: flex; diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 48bb56c..4edfbe3 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -1,71 +1,29 @@ -import React, {useRef, useEffect, FC, ReactNode} from "react"; -import {observer} from "mobx-react-lite"; +import React, {FC, ReactNode} from "react"; -import styles from "./Modal.module.scss" -import cn from "classnames"; +import ModalOverlay from "../ModalOverlay/ModalOverlay"; -import {useModel} from "../SignupSigninForm/model"; +import styles from "./Modal.module.scss" -interface ModalProps { - className?: string; +interface IModalProps { + isOpen: boolean; + onClose: () => void; children?: ReactNode; } -const Modal: FC = ({ - className, - children - }) => { - const model = useModel(); - - const modalRef = useRef() as React.MutableRefObject; - - const setCloseByOverlayListener = (modal: any) => { - modal.addEventListener("mousedown", (event: MouseEvent) => { - const targetClasses = (event.target as Element).classList; - const regExp = /^(Modal_modal_opened__)[\w]?/; - for (let i = 0; i < targetClasses.length; i++) { - if (regExp.test(targetClasses[i])) { - model.handleCloseModal(); - } - } - }); - } - - const handleCloseByEsc = (event: KeyboardEvent) => { - if (event.key === "Escape") { - model.handleCloseModal(); - } - }; - - useEffect(() => { - setCloseByOverlayListener(modalRef.current); - }, []); - - // Закрытие popup при нажатии на Esc - useEffect(() => { - if (model.isModalOpen) { - // Список действий внутри одного хука - document.addEventListener("keydown", handleCloseByEsc); - // Возвращаем функцию, которая удаляет эффекты - return () => { - document.removeEventListener("keydown", handleCloseByEsc); - }; - } - }, [model.isModalOpen]); - +const Modal: FC = ({isOpen, onClose, children}) => { return ( -
+
{children}
-
+ ); } -export default observer(Modal); +export default Modal; diff --git a/src/components/ModalOverlay/ModalOverlay.module.scss b/src/components/ModalOverlay/ModalOverlay.module.scss new file mode 100644 index 0000000..b9637b5 --- /dev/null +++ b/src/components/ModalOverlay/ModalOverlay.module.scss @@ -0,0 +1,21 @@ +.modalOverlay { + visibility: hidden; + opacity: 0; + z-index: -100; + display: flex; + background-color: #403A6D4D; + position: fixed; + min-width: 100%; + min-height: 100%; + top: 0; + left: 0; + justify-content: center; + align-items: center; + + &_opened { + visibility: visible; + opacity: 1; + z-index: 100; + transition: visibility 0.5s ease, opacity 0.5s ease; + } +} diff --git a/src/components/ModalOverlay/ModalOverlay.tsx b/src/components/ModalOverlay/ModalOverlay.tsx new file mode 100644 index 0000000..c365250 --- /dev/null +++ b/src/components/ModalOverlay/ModalOverlay.tsx @@ -0,0 +1,56 @@ +import React, {FC, ReactNode, useEffect, useRef} from 'react'; + +import styles from './ModalOverlay.module.scss'; +import cn from "classnames"; + +interface IModalOverlayProps { + isOpen: boolean; + onClose: () => void; + children?: ReactNode; +} + +const ModalOverlay: FC = ({isOpen, onClose, children}) => { + const modalRef = useRef() as React.MutableRefObject; + + // Закрытие по overlay + const setCloseByOverlayListener = (modal: any) => { + modal.addEventListener("mousedown", (event: MouseEvent) => { + const targetClasses = (event.target as Element).classList; + const regExp = /^(ModalOverlay_modalOverlay_opened__)[\w]?/; + for (let i = 0; i < targetClasses.length; i++) { + if (regExp.test(targetClasses[i])) { + onClose(); + } + } + }); + } + + // Закрытие при нажатии на Esc + const handleCloseByEsc = (event: KeyboardEvent) => { + if (event.key === "Escape") { + onClose(); + } + }; + + useEffect(() => { + setCloseByOverlayListener(modalRef.current); + }, []); + + useEffect(() => { + if (isOpen) { + // Список действий внутри одного хука + document.addEventListener("keydown", handleCloseByEsc); + // Возвращаем функцию, которая удаляет эффекты + return () => { + document.removeEventListener("keydown", handleCloseByEsc); + }; + } + }, [isOpen]); + + + return
+ {children} +
; +} + +export default ModalOverlay; diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 417a10b..35bde71 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -1,4 +1,5 @@ import React, {useEffect, useState} from 'react'; +import {observer} from "mobx-react-lite"; import {api} from '../../utils/constants'; @@ -16,6 +17,8 @@ import cn from "classnames"; import {useModel} from "../../components/SignupSigninForm/model"; const MainPage = () => { + const model = useModel(); + const [usersList, setUsersList] = useState([]); const [isUsersList, setIsUsersList] = useState(false); const [category, setCategory] = useState({name: 'Все', path: ''}); @@ -89,19 +92,22 @@ const MainPage = () => { + + +

Подтвердите адрес электронной почты

+

Пожалуйста, проверьте электронную почту, которую + указали + при регистрации, и перейдите по ссылке для подтверждения

+

Ссылка будет активна в течении 24 часов

+
+
- -

Подтвердите адрес электронной почты

-

Пожалуйста, проверьте электронную почту, которую - указали - при регистрации, и перейдите по ссылке для подтверждения

-

Ссылка будет активна в течении 24 часов

-
+ ); }; -export default MainPage; +export default observer(MainPage); From ae20f3bd59c72758ff78f3dc91746e39ad04ab52 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Thu, 3 Aug 2023 22:02:18 +0300 Subject: [PATCH 015/153] fix: modal close button --- src/components/Modal/Modal.module.scss | 1 + src/images/svg/modal-close-button.svg | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Modal/Modal.module.scss b/src/components/Modal/Modal.module.scss index 442760e..8e31853 100644 --- a/src/components/Modal/Modal.module.scss +++ b/src/components/Modal/Modal.module.scss @@ -18,6 +18,7 @@ right: 16px; top: 16px; border: none; + background-color: var(--color-base-white); background-image: url(../../images/svg/modal-close-button.svg); background-repeat: no-repeat; background-size: 100% 100%; diff --git a/src/images/svg/modal-close-button.svg b/src/images/svg/modal-close-button.svg index 0d8f452..825298e 100644 --- a/src/images/svg/modal-close-button.svg +++ b/src/images/svg/modal-close-button.svg @@ -1,9 +1,9 @@ - - + + - + - + From a55a37024f0c88ac0832bd77e1e1b954b05ee075 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Thu, 3 Aug 2023 22:31:19 +0300 Subject: [PATCH 016/153] fix: ModalOverlay component --- src/components/ModalOverlay/ModalOverlay.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/ModalOverlay/ModalOverlay.tsx b/src/components/ModalOverlay/ModalOverlay.tsx index c365250..9977cf3 100644 --- a/src/components/ModalOverlay/ModalOverlay.tsx +++ b/src/components/ModalOverlay/ModalOverlay.tsx @@ -17,10 +17,16 @@ const ModalOverlay: FC = ({isOpen, onClose, children}) => { modal.addEventListener("mousedown", (event: MouseEvent) => { const targetClasses = (event.target as Element).classList; const regExp = /^(ModalOverlay_modalOverlay_opened__)[\w]?/; - for (let i = 0; i < targetClasses.length; i++) { - if (regExp.test(targetClasses[i])) { - onClose(); - } + const getClassName = () => { + let isClassName = false; + Array.from(targetClasses).filter((className) => { + isClassName = regExp.test(className); + }); + return isClassName; + } + + if (getClassName()) { + onClose(); } }); } From 7843a8c5038ae4b8ed0600ab2bd56d7500944cb7 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Thu, 3 Aug 2023 22:36:05 +0300 Subject: [PATCH 017/153] fix: delete modal and sort popups from start MainPage --- src/components/SignupSigninForm/model.ts | 2 +- src/pages/MainPage/MainPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 7a1ebcc..78fe5db 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -16,7 +16,7 @@ export const useModel = () => { email: "", password: "", confirmPassword: "", - isModalOpen: true, + isModalOpen: false, isLoggedIn: false, handleUsernameChange({value}: { value: string }) { diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 35bde71..700533a 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -23,7 +23,7 @@ const MainPage = () => { const [isUsersList, setIsUsersList] = useState(false); const [category, setCategory] = useState({name: 'Все', path: ''}); const [sortType, setSortType] = useState({}); - const [isSortPopupOpen, setSortPopupOpen] = useState(true); + const [isSortPopupOpen, setSortPopupOpen] = useState(false); const handleOpenSortPopup = () => { setSortPopupOpen(!isSortPopupOpen); From 3e020611e8193a92d798ed98080fe4d52c4fc909 Mon Sep 17 00:00:00 2001 From: Keti <“rudnevaketi@gmail.com”> Date: Fri, 4 Aug 2023 09:37:12 +0300 Subject: [PATCH 018/153] added media --- src/images/userProfile/clock.png | Bin 0 -> 801 bytes src/images/userProfile/plus.svg | 14 ++++++++++++++ src/images/userProfile/russia.svg | 12 ++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 src/images/userProfile/clock.png create mode 100644 src/images/userProfile/plus.svg create mode 100644 src/images/userProfile/russia.svg diff --git a/src/images/userProfile/clock.png b/src/images/userProfile/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..d45be89ecd3133d47318e2c959f965d51d6f2a39 GIT binary patch literal 801 zcmV++1K#|JP)Tdxz^|8wOr>h4Z(oolPLc6(XoaB(<7NoR5=8G$dookGfwr{KyGxYl@Ue zOFsmlI$j(kw4Q?@X4MvexPZN2f(He9fneN1v!gF~Waf{+HqV<13F-;YF6ub7-g$QT zBV>s-PozSUc^?pqEGgZ60q2+Gnn(^hePaa->*1Y6i$BYg>9A{!?^>>&m^hGp?+ZF- zigzeC4;Srf{M@2-z@M>gKHhMx1y-|v%4w`A1L1~HSf4)UA95Pnq*l8%OLo6AffC^y z_a%_olVn(0i_AH@3gSN7hqngU<$YBMM2ApN2sRi3O%;$?Z3P{~!*tm*)A4<-zwVvyBiOkCjvmTf7rD(o6 zTs@l&)^U1jKgHt%Jlv(gJky<~-SwBls-M|{P|Ljz5a_39!YAO%!9v`p2#yipdAl0+ z@uhShX9e0}``TnHvNC-dp_+9@ztc_o@DTM~yG8yH_e$osIWO3D?{p!+w=#AfuE6rb fK(HYSn;E|V&*d08(fc|P00000NkvXXu0mjff7WF? literal 0 HcmV?d00001 diff --git a/src/images/userProfile/plus.svg b/src/images/userProfile/plus.svg new file mode 100644 index 0000000..c9f3a90 --- /dev/null +++ b/src/images/userProfile/plus.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/images/userProfile/russia.svg b/src/images/userProfile/russia.svg new file mode 100644 index 0000000..d9a864c --- /dev/null +++ b/src/images/userProfile/russia.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + From 7757b7d0fe90402008f8db6cb11fafd89f3aec9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 4 Aug 2023 11:06:13 +0300 Subject: [PATCH 019/153] Fix: sort cantry --- .../LanguageLevel/LanguageLevel.tsx | 8 +- src/components/Sort/Sort.module.scss | 35 +++- src/components/Sort/Sort.tsx | 149 ++++++++++++++++-- src/images/svg/16px.svg | 8 + ...1\213\320\272\320\260\320\274\320\270.zip" | Bin 0 -> 629 bytes src/pages/MainPage/MainPage.tsx | 17 +- 6 files changed, 186 insertions(+), 31 deletions(-) create mode 100644 src/images/svg/16px.svg create mode 100644 "src/images/svg/\320\237\320\273\320\260\321\202\321\204\320\276\321\200\320\274\320\260 \320\264\320\273\321\217 \320\276\320\261\320\274\320\265\320\275\320\260 \321\217\320\267\321\213\320\272\320\276\320\262\321\213\320\274\320\270 \320\275\320\260\320\262\321\213\320\272\320\260\320\274\320\270.zip" diff --git a/src/components/LanguageLevel/LanguageLevel.tsx b/src/components/LanguageLevel/LanguageLevel.tsx index 63a3297..597044c 100644 --- a/src/components/LanguageLevel/LanguageLevel.tsx +++ b/src/components/LanguageLevel/LanguageLevel.tsx @@ -1,10 +1,6 @@ import React, {useState} from "react"; import styles from "../../components/LanguageLevel/LanguageLevel.module.scss" - -export interface Language { - id: number; - name: string; -} +import { Language } from '../../utils/openapi'; interface LanguageLevelProps { languages: Language[]; @@ -38,7 +34,7 @@ const LanguageLevel: React.FC = ({ languages, onAdd, onRemov
+ {hasError ? ( + {error} + ) : ( + hint && {hint} + )} + + ) : ( + <> + + {hasError ? ( + {error} + ) : ( + hint && {hint} + )} + + ); +}; diff --git a/src/images/close-icon-16px.png b/src/images/close-icon-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..cdadfca2a7aa0a49062fcd849c27ded404d7a5b0 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{y$F_$B+ufvq3ur4;cu=%uMIdJXqpzVuFI# z0%oa%0!=v&kChEV0*pM8QyvI$9Bkn8tW&M_;gHT)a{2DNSAGZ9AH2ix+);jGOp_pc zqk80Tlu_#Wq4dquga#_wpQa4O^ZW!XB{K0GY_c5JElf#cnkfopW7 zZ?CZY-4XBD%CKPRQq{eYaT#+Q_x|*B!z`CaTjWAyI0WxG ztX)~Y+N>3QAq1^L!PyXQ++FcaAf39H^GTIz>r|@boFyt=akR{ E01#=1o&W#< literal 0 HcmV?d00001 diff --git a/src/images/fill-out-profile-export-avatar.png b/src/images/fill-out-profile-export-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..60ff9f127f19a7ff2fb2323d3e7bf54209667829 GIT binary patch literal 3532 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 V8gZAMCO!ZF002ovPDHLkV1ib^jJ5y( literal 0 HcmV?d00001 diff --git a/src/images/goals/handshake-logo.png b/src/images/goals/handshake-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e1dcdefdf041e3e75ef91967e4c9182963949b67 GIT binary patch literal 3684 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 p&lS6QQh002ovPDHLkV1k{Q<%|FT literal 0 HcmV?d00001 diff --git a/src/images/svg/back-arrow-32px.svg b/src/images/svg/back-arrow-32px.svg new file mode 100644 index 0000000..e97f88d --- /dev/null +++ b/src/images/svg/back-arrow-32px.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx index 57b5f3e..234ba08 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx @@ -58,6 +58,7 @@ const FillOutProfilePage1 = () => { type="text" name="firstName" value={model.firstName} + fontSize="16" label="Укажите имя" labelStyles="label18" isLabelHintHidden={true} @@ -76,6 +77,7 @@ const FillOutProfilePage1 = () => { type="date" name="birthdate" value={model.birthdate} + fontSize="16" label="Дату рождения" labelStyles="label18" isLabelHintHidden={true} diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx new file mode 100644 index 0000000..aa9038e --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx @@ -0,0 +1,95 @@ +import React, {FormEvent, useEffect, useMemo, useState} from "react"; +import {useNavigate} from "react-router-dom"; + +import Header from "../../components/Header/Header"; +import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; +import LanguageModule from "../../components/LanguageModule/LanguageModule"; +import {Button} from "../../components/UI/Button/Button"; + +import {Language, SkillLevelEnum} from "../../utils/openapi"; + +import styles from "./FillOutProfilePages.module.scss"; +import cn from "classnames"; +import LanguageLevelModal from "../../components/LanguageLevelModal/LanguageLevelModal"; + +const FillOutProfilePage1 = () => { + const navigate = useNavigate(); + + const [isModalOpen, setModalOpen] = useState(false); + const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); + + const initialLanguageAndLevels = useMemo(() => { + return {language: null, skillLevels: []}; + }, []); + + const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< + { language: Language | null; skillLevels: SkillLevelEnum[] }[] + >([initialLanguageAndLevels]); + + const handleSubmitButtonDisabled = () => { + for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { + selectedLanguagesAndLevels[i].language === null || selectedLanguagesAndLevels[i].skillLevels.toString() === [].toString() + ? setSubmitButtonDisabled(true) + : setSubmitButtonDisabled(false) + } + } + + useEffect(() => { + handleSubmitButtonDisabled(); + }, [selectedLanguagesAndLevels]); + + const handleReturnButtonClick = () => { + navigate("/fill-out-2"); + } + + const handleHelpButtonClick = () => { + setModalOpen(true); + } + + const handleFillOutPage3 = (event: FormEvent) => { + event.preventDefault(); + navigate("/fill-out-4"); + console.log("FillOutPage3"); + }; + + return ( + <> +
+
+ +
+ +

Выберите изучаемые языки

+
+
+ + +
+ +
+
+ + +
+ + ); +}; + +export default FillOutProfilePage1; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx new file mode 100644 index 0000000..c45ce1d --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx @@ -0,0 +1,102 @@ +import {ChangeEvent, FormEvent, MouseEventHandler, useEffect, useMemo, useState} from "react"; +import {useNavigate} from "react-router-dom"; + +import Header from "../../components/Header/Header"; +import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; +import {Button} from "../../components/UI/Button/Button"; +import {Goals} from "./Goals"; + +import styles from "./FillOutProfilePages.module.scss"; + +const FillOutProfilePage4 = () => { + const navigate = useNavigate(); + + const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); + + const [selectedGoals, setSelectedGoals] = useState<{ + image: string, name: string, description: string, active: boolean + }[]>([]); + + console.log(selectedGoals); + console.log(isSubmitButtonDisabled); + + const handleSubmitButtonDisabled = () => { + selectedGoals.length === 0 + ? setSubmitButtonDisabled(true) + : setSubmitButtonDisabled(false) + } + + useEffect(() => { + handleSubmitButtonDisabled(); + }, [selectedGoals]); + + const handleReturnButtonClick = () => { + navigate("/fill-out-3"); + }; + + const handleGoalButtonClick = (event, goal, index) => { + const updatedGoals = [...selectedGoals]; + + console.log(goal.active); + + if (!goal.active) { + goal.active = true; + updatedGoals.push(goal); + setSelectedGoals(updatedGoals); + event.currentTarget.classList.add(styles.container__goals_goal_active); + } else { + goal.active = false; + const updatedGoals = selectedGoals.filter((selectedGoal) => selectedGoal.active); + setSelectedGoals(updatedGoals); + event.currentTarget.classList.remove(styles.container__goals_goal_active); + } + } + const handleFillOutPage4 = (event: FormEvent) => { + event.preventDefault(); + navigate("/fill-out-5"); + console.log("FillOutPage4"); + }; + + return ( + <> +
+
+ +
+ +

Укажите ваши цели

+
+
+ {Goals.map((goal, index) => ( + + ))} +
+ +
+
+
+ + ); + } +; + +export default FillOutProfilePage4; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx new file mode 100644 index 0000000..d06864c --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx @@ -0,0 +1,94 @@ +import {FormEvent, useEffect, useState} from "react"; +import {useNavigate} from "react-router-dom"; + +import Header from "../../components/Header/Header"; +import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; +import {Input} from "../../components/UI/Input/Input"; +import {Button} from "../../components/UI/Button/Button"; +import {api} from "../../utils/constants"; +import {Interest, PaginatedInterestList} from "../../utils/openapi"; + +import {useModel} from "./model"; + +import styles from "./FillOutProfilePages.module.scss"; + +const FillOutProfilePage5 = () => { + const model = useModel(); + const navigate = useNavigate(); + + const [interestsList, setInterestsList] = useState([]); + + const getInterestsList = async () => { + try { + console.log('отправка запроса ---'); + const response = await api.api.interestsList(); + console.log('ответ получен -', response); + + if (response.data && response.data.results) { + setInterestsList(response.data.results); + console.log(response.data.results); + } + } catch (error) { + console.error('Ошибка при получении данных -', error); + } + }; + + useEffect(() => { + getInterestsList(); + }, []); + + const handleReturnButtonClick = () => { + navigate("/fill-out-4"); + } + + const handleFillOutPage5 = (event: FormEvent) => { + event.preventDefault(); + navigate("/fill-out-6"); + console.log("FillOutPage5"); + }; + + return ( + <> +
+
+ +
+ +

Укажите ваши интересы

+
+ +
+ {interestsList && + interestsList.map((interest, index) => ( + + )) + } +
+ +
+
+
+ + ); +}; + +export default FillOutProfilePage5; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx new file mode 100644 index 0000000..38e2bfc --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx @@ -0,0 +1,63 @@ +import {FormEvent} from "react"; +import {useNavigate} from "react-router-dom"; + +import Header from "../../components/Header/Header"; +import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; +import {Button} from "../../components/UI/Button/Button"; +import {Textarea} from "../../components/UI/Textarea/Textarea"; + +import {useModel} from "./model"; + +import styles from "./FillOutProfilePages.module.scss"; + +const FillOutProfilePage6 = () => { + const model = useModel(); + const navigate = useNavigate(); + + const handleReturnButtonClick = () => { + navigate("/fill-out-5"); + } + + const handleFillOutPage6 = (event: FormEvent) => { + event.preventDefault(); + navigate("/"); + console.log("FillOutPage6"); + }; + + return ( + <> +
+
+ +
+ +

Расскажите о себе

+
+ + +
+
+
+ + ); +}; + +export default FillOutProfilePage6; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePages.module.scss b/src/pages/FillOutProfilePages/FillOutProfilePages.module.scss index 86e8539..7e6e0bf 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePages.module.scss +++ b/src/pages/FillOutProfilePages/FillOutProfilePages.module.scss @@ -133,6 +133,10 @@ border: none; cursor: pointer; + &_active { + background-color: var(--accent-color-salad); + } + & img { margin-bottom: 12px; } @@ -153,48 +157,31 @@ width: 454px; margin-top: 40px; } -} -.modal { - align-items: center; - text-align: center; - - &__item { - width: 318px; - padding: 19px 0 18px; - margin: 0 auto; - border-bottom: 1px solid var(--grey-gradient-100); + &__input { + &_fullWidth { + min-width: 668px; + margin: 32px auto; + } } - &__item:last-child { - width: 318px; - padding-bottom: 0; - margin: 0 auto; - border-bottom: none; + &__textarea { + margin-top: 32px; } +} +.modal { &__title { letter-spacing: 0.48px; margin: 0 0 24px; } - &__subtitle_languages { - color: var(--dark-violet); - font: var(--font-header-14-120-600); - margin: 0 0 8px; - } - &__text { color: var(--dark-violet); font: var(--font-text-16-160-400); max-width: 375px; } - &__text_languages { - color: var(--dark-violet-gradient-400); - font: var(--font-text-14-120-400); - } - &__hint { color: var(--dark-violet); font: var(--font-text-14-140-400); @@ -224,6 +211,27 @@ display: none; } } +} +.interests { + display: flex; + flex-wrap: wrap; + width: 100%; + height: 88px; + overflow: hidden; + gap: 16px; + + &__interest { + border-radius: 20px; + border: 1px solid var(--accent-color-violet); + height: 36px; + padding: 4px 30px 4px 12px; + background: url("../../images/close-icon-16px.png") transparent no-repeat right 6px center; + background-size: 16px 16px; + cursor: pointer; + color: var(--dark-violet); + font: var(--font-text-16-100-400); + text-align: left; + } } diff --git a/src/pages/FillOutProfilePages/Goals.ts b/src/pages/FillOutProfilePages/Goals.ts new file mode 100644 index 0000000..92372df --- /dev/null +++ b/src/pages/FillOutProfilePages/Goals.ts @@ -0,0 +1,15 @@ +import graduationCap from "../../images/goals/graduation-cap.png"; +import business from "../../images/goals/business.png"; +import geography from "../../images/goals/geography.png"; +import product from "../../images/goals/product.png"; +import handshakeLogo from "../../images/goals/handshake-logo.png"; +import brain from "../../images/goals/brain.png"; + +export const Goals = [ + {image: graduationCap, name: "Академическая шапочка", description: "Образование", active: false}, + {image: business, name: "Портфель бизнесмена", description: "Карьера и бизнес", active: false}, + {image: geography, name: "Модель планеты", description: "Путешествие", active: false}, + {image: product, name: "Иконка чемодана", description: "Переезд", active: false}, + {image: handshakeLogo, name: "Иконка рукопожатия", description: "Сообщество", active: false}, + {image: brain, name: "Иконка человеческого мозга", description: "Развитие", active: false}, +]; diff --git a/src/pages/FillOutProfilePages/model.ts b/src/pages/FillOutProfilePages/model.ts index 9c5b8e8..5f08f73 100644 --- a/src/pages/FillOutProfilePages/model.ts +++ b/src/pages/FillOutProfilePages/model.ts @@ -8,15 +8,17 @@ export const useModel = () => { firstName: "", birthdate: "", country: "", + interest: "", + about: "", isLoading: false, - handleValue({name, value}: { name: "firstName" | "country" ; value: string }) { + handleValue({name, value}: { name: "firstName" | "country" | "interest" | "about"; value: string }) { model[name] = value; }, - handleBirthdate({name, value}: { name: "birthdate"; value: string }) { - model[name] = value; - }, + // handleBirthdate({name, value}: { name: "birthdate"; value: string }) { + // model[name] = value; + // }, }; }); diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index c785d80..498dcb7 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -13,25 +13,47 @@ export interface AgeVisibility { age_is_hidden: boolean; } -/** Сериализатор для создания чата. */ +/** Сериализатор для просмотра чата. */ export interface Chat { - /** @pattern ^[-a-zA-Z0-9_]+$ */ - companion: string; + id: number; + initiator: UserShort; + receiver: UserShort; + messages: Message[]; } -/** Сериализатор для списка чатов. */ +/** Сериализатор для просмотра списка чатов. */ export interface ChatList { id: number; - companion: string; + initiator: UserShort; + receiver: UserShort; + last_message: string; + unread: string; +} + +/** Сериализатор для создания личного чата. */ +export interface ChatStart { + /** + * Слаг + * Слаг + */ + receiver: string | null; + /** @maxLength 10000 */ + message: string; } -/** Сериализатор для создания чата. */ -export interface ChatRequest { +/** Сериализатор для создания личного чата. */ +export interface ChatStartRequest { /** + * Слаг + * Слаг * @minLength 1 - * @pattern ^[-a-zA-Z0-9_]+$ */ - companion: string; + receiver: string | null; + /** + * @minLength 1 + * @maxLength 10000 + */ + message: string; } /** Сериализатор модели страны. */ @@ -101,6 +123,59 @@ export interface Language { sorting: number; } +/** Сериализатор модели Message. */ +export interface Message { + id: number; + /** + * Слаг + * Слаг + */ + sender: string | null; + /** @maxLength 10000 */ + text: string | null; + /** @format uri */ + file_to_send?: string; + /** @format uri */ + photo_to_send?: string; + /** + * Ответ на другое сообщение + * Ответ на другое сообщение + */ + responding_to?: number | null; + /** + * Сообщение отправлено + * Сообщение отправлено + */ + sender_keep: boolean; + is_read: string; + /** + * Сообщение закреплено + * Сообщение закреплено + */ + is_pinned: boolean; + read_by: UserShort[]; + /** @format date-time */ + timestamp: string; +} + +/** Сериализатор модели Message. */ +export interface MessageRequest { + /** + * @minLength 1 + * @maxLength 10000 + */ + text: string | null; + /** @format binary */ + file_to_send?: File; + /** @format binary */ + photo_to_send?: File; + /** + * Ответ на другое сообщение + * Ответ на другое сообщение + */ + responding_to?: number | null; +} + export type NullEnum = null; export interface PaginatedChatListList { @@ -409,6 +484,28 @@ export interface UserRepr { role: string; } +export interface UserShort { + /** + * Слаг + * Слаг + * @pattern ^[-a-zA-Z0-9_]+$ + */ + slug: string | null; + /** Логин */ + username: string; + /** + * Имя + * Имя пользователя + */ + first_name: string; + /** + * Изображение + * Аватар пользователя + * @format uri + */ + avatar: string | null; +} + export type QueryParamsType = Record; export type ResponseFormat = keyof Omit; @@ -678,10 +775,11 @@ export class Api extends HttpClient extends HttpClient - this.request({ - path: `/api/v1/chats/`, - method: "POST", - body: data, - secure: true, - type: ContentType.Json, - format: "json", - ...params, - }), - - /** - * @description Просмотреть чат + * @description Просмотреть чат и историю сообщений * * @tags chats * @name ChatsRetrieve + * @summary Просмотреть чат * @request GET:/api/v1/chats/{id}/ * @secure */ @@ -744,16 +824,17 @@ export class Api extends HttpClient - this.request({ - path: `/api/v1/chats/${id}/clear/`, + chatsSendMessageCreate: (id: number, data: MessageRequest, params: RequestParams = {}) => + this.request({ + path: `/api/v1/chats/${id}/send-message/`, method: "POST", body: data, secure: true, @@ -763,16 +844,17 @@ export class Api extends HttpClient - this.request({ - path: `/api/v1/chats/${id}/send_message/`, + chatsStartPersonalChatCreate: (data: ChatStartRequest, params: RequestParams = {}) => + this.request({ + path: `/api/v1/chats/start-personal-chat/`, method: "POST", body: data, secure: true, diff --git a/src/vendor/_variables.scss b/src/vendor/_variables.scss index b875fe5..532d403 100644 --- a/src/vendor/_variables.scss +++ b/src/vendor/_variables.scss @@ -45,7 +45,9 @@ --font-text-14-120-400: 400 14px/1.2 var(--font-sans); --font-text-14-140-400: 400 14px/1.4 var(--font-sans); --font-text-15-120-400: 400 15px/1.2 var(--font-sans); + --font-text-16-100-400: 400 16px/1 var(--font-sans); --font-text-16-120-400: 400 16px/1.2 var(--font-sans); + --font-text-16-140-400: 400 16px/1.4 var(--font-sans); --font-text-16-160-400: 400 16px/1.6 var(--font-sans); --font-text-18-100-400: 400 18px/1 var(--font-sans); --font-text-18-140-400: 400 18px/1.4 var(--font-sans); From d669743f66a377452c0aed3d6ede52afc46b1c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 25 Aug 2023 01:37:03 +0300 Subject: [PATCH 102/153] =?UTF-8?q?Fix=20=D0=94=D0=BE=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=20LanguageLevelModal=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=D0=B0=20Sort=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=BE=D0=B3=D0=BD=D0=B0=D0=BD=D1=8B?= =?UTF-8?q?=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8=20=D0=B8=20=D0=BA=D0=B0=D1=80?= =?UTF-8?q?=D1=82=D0=B8=D0=BD=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LanguageLevelModal.module.scss | 73 +++++++++ .../LanguageLevelModal/LanguageLevelModal.tsx | 142 ++++++++++++++++++ src/components/Sort/Sort.tsx | 14 +- src/images/SkillLevel/Level1.svg | 8 + src/images/SkillLevel/Level2.svg | 8 + src/images/SkillLevel/Level3.svg | 8 + src/images/SkillLevel/Level4.svg | 8 + src/images/SkillLevel/Level5.svg | 8 + src/images/SkillLevel/Level6.svg | 8 + src/vendor/_variables.scss | 1 + 10 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 src/components/LanguageLevelModal/LanguageLevelModal.module.scss create mode 100644 src/components/LanguageLevelModal/LanguageLevelModal.tsx create mode 100644 src/images/SkillLevel/Level1.svg create mode 100644 src/images/SkillLevel/Level2.svg create mode 100644 src/images/SkillLevel/Level3.svg create mode 100644 src/images/SkillLevel/Level4.svg create mode 100644 src/images/SkillLevel/Level5.svg create mode 100644 src/images/SkillLevel/Level6.svg diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.module.scss b/src/components/LanguageLevelModal/LanguageLevelModal.module.scss new file mode 100644 index 0000000..14f1041 --- /dev/null +++ b/src/components/LanguageLevelModal/LanguageLevelModal.module.scss @@ -0,0 +1,73 @@ +.sortModal { + width: 409px; + padding: 60px 45px 40px; + + &__item { + width: 318px; + padding: 19px 0 19px; + margin: 0 auto; + border-bottom: 1px solid var(--grey-gradient-100); + display: flex; + align-items: center; + justify-content: center; + } + + &__item:last-child { + width: 318px; + padding-bottom: 0; + margin: 0 auto; + border-bottom: none; + } + + &__languages { + flex: 1; + margin-right: 20px; + + &_subtitle { + color: var(--dark-violet); + font: var(--font-header-14-120-600); + margin: 0 0 8px; + } + + &_text { + font: var(--font-text-14-120-400); + color: var(--dark-violet-gradient-400); + } + } + + &__image { + width: 39px; + height: 26px; + margin-left: 20px; + } +} + +.modal { + align-items: center; + text-align: center; + + &__item { + width: 318px; + padding: 19px 0 18px; + margin: 0 auto; + border-bottom: 1px solid var(--grey-gradient-100); + } + + &__item:last-child { + width: 318px; + padding-bottom: 0; + margin: 0 auto; + border-bottom: none; + } + + &__subtitle_languages { + color: var(--dark-violet); + font: var(--font-header-14-120-600); + margin: 0 0 8px; + } + + &__text_languages { + color: var(--dark-violet-gradient-400); + font: var(--font-text-14-120-600); + } + } \ No newline at end of file diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.tsx b/src/components/LanguageLevelModal/LanguageLevelModal.tsx new file mode 100644 index 0000000..7aa6e27 --- /dev/null +++ b/src/components/LanguageLevelModal/LanguageLevelModal.tsx @@ -0,0 +1,142 @@ +import React from "react"; +import Modal from "../Modal/Modal"; + +import level_1 from '../../images/SkillLevel/Level1.svg'; +import level_2 from '../../images/SkillLevel/Level2.svg'; +import level_3 from '../../images/SkillLevel/Level3.svg'; +import level_4 from '../../images/SkillLevel/Level4.svg'; +import level_5 from '../../images/SkillLevel/Level5.svg'; +import level_6 from '../../images/SkillLevel/Level6.svg'; + +import styles from "./LanguageLevelModal.module.scss"; + +interface LanguageLevelModalProps { + isModalOpen: boolean; + setModalOpen: React.Dispatch>; + pageName: string; +} + +interface ModalStyles { + [key: string]: string; +} +const LanguageLevelModal: React.FC = ({ isModalOpen, setModalOpen, pageName }) => { + const handleCloseModal = () => { + setModalOpen(false); + }; + + const modalStyles: ModalStyles = { + Sort: styles.sortModal, + }; + + const renderModalContent = () => { + if (pageName === "Sort") { + return ( + <> +
+
+

Новичок

+

+ Может представиться, простые тексты и базовые выражения +

+
+ Новичок +
+
+
+

Любитель

+

+ Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы. +

+
+ Любитель +
+
+
+

Профессионал

+

+ Может выразить личное мнение с аргументацией, поддержание диалога +

+
+ Профессионал +
+
+
+

Эксперт

+

+ Может выражаться естественно и без усилий, вспоминать интересные обороты речи +

+
+ Эксперт +
+
+
+

Гуру

+

+ Может поддержать дискуссии на узконаправленные темы +

+
+ Гуру +
+
+
+

Носитель

+

+ Вырос в среде языка +

+
+ Носитель +
+ + ); + } else { + return ( + <> +
+

Новичок

+

+ Может представиться, простые тексты и базовые выражения +

+
+
+

Любитель

+

+ Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы. +

+
+
+

Профессионал

+

+ Может выразить личное мнение с аргументацией, поддержание диалога +

+
+
+

Эксперт

+

+ Может выражаться естественно и без усилий, вспоминать интересные обороты речи +

+
+
+

Гуру

+

+ Может поддержать дискуссии на узконаправленные темы +

+
+
+

Носитель

+

+ Вырос в среде языка +

+
+ + ); + }; + }; + + return ( + + {renderModalContent()} + + ); +}; + +export default LanguageLevelModal; \ No newline at end of file diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 6f4c639..82b0921 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -5,6 +5,7 @@ import CountrySelection from "../CountrySelection/CountrySelection"; import LanguageModule from "../LanguageModule/LanguageModule"; import Gender from "../Gender/Gender"; import MultiRangeSlider from "../MultiRangeSlider/MultiRangeSlider"; +import LanguageLevelModal from "../LanguageLevelModal/LanguageLevelModal" import { Button } from "../UI/Button/Button"; import { Country, Language, SkillLevelEnum } from '../../utils/openapi'; @@ -27,12 +28,15 @@ interface SortProps { const Sort: React.FC = ({ onChangeSort, isOpen }) => { const [selectedCountries, setSelectedCountries] = useState([]); - const [leftValue, setLeftValue] = useState(18); - const [rightValue, setRightValue] = useState(40); - const [selectedGender, setSelectedGender] = useState(null); const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< { language: Language | null; skillLevels: SkillLevelEnum[] }[] >([]); + const [isModalOpen, setModalOpen] = useState(false); + const [showDefaultLanguageModule, setShowDefaultLanguageModule] = useState(true); + const [selectedGender, setSelectedGender] = useState(null); + const [leftValue, setLeftValue] = useState(18); + const [rightValue, setRightValue] = useState(40); + @@ -76,14 +80,17 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => {

Язык партнера

+ {showDefaultLanguageModule && ( + )}

О партнере

@@ -120,6 +127,7 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { children={'Очистить фильтр'} onClick={handleClearFilter} /> +
); }; diff --git a/src/images/SkillLevel/Level1.svg b/src/images/SkillLevel/Level1.svg new file mode 100644 index 0000000..457920c --- /dev/null +++ b/src/images/SkillLevel/Level1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level2.svg b/src/images/SkillLevel/Level2.svg new file mode 100644 index 0000000..5902469 --- /dev/null +++ b/src/images/SkillLevel/Level2.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level3.svg b/src/images/SkillLevel/Level3.svg new file mode 100644 index 0000000..861569c --- /dev/null +++ b/src/images/SkillLevel/Level3.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level4.svg b/src/images/SkillLevel/Level4.svg new file mode 100644 index 0000000..ad1a706 --- /dev/null +++ b/src/images/SkillLevel/Level4.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level5.svg b/src/images/SkillLevel/Level5.svg new file mode 100644 index 0000000..da3f2d2 --- /dev/null +++ b/src/images/SkillLevel/Level5.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level6.svg b/src/images/SkillLevel/Level6.svg new file mode 100644 index 0000000..b0e1b12 --- /dev/null +++ b/src/images/SkillLevel/Level6.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/vendor/_variables.scss b/src/vendor/_variables.scss index 13ce828..f755455 100644 --- a/src/vendor/_variables.scss +++ b/src/vendor/_variables.scss @@ -48,6 +48,7 @@ --font-text-24-120-400: 400 24px/1.2 var(--font-sans); --font-text-16-125-500: 500 16px/1.25 var(--font-sans); --font-title-18-120-500: 500 18px/1.2 var(--font-sans); + --font-header-14-120-600: 600 14px/1.2 var(--font-sans); --font-header-18-120-600: 600 18px/1.2 var(--font-sans); --font-header-24-120-600: 600 24px/1.2 var(--font-sans); } From 9c54aad21dc75295cfaaf9ae5ee59f555a848ece Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Fri, 25 Aug 2023 11:20:58 +0300 Subject: [PATCH 103/153] fix: LanguageModule component --- .../LanguageLevelModal.module.scss | 36 +++++++---- .../LanguageLevelModal/LanguageLevelModal.tsx | 63 +++++++------------ src/components/LanguageLevelModal/Levels.ts | 15 +++++ src/images/SkillLevel/Level1.svg | 8 +++ src/images/SkillLevel/Level2.svg | 8 +++ src/images/SkillLevel/Level3.svg | 8 +++ src/images/SkillLevel/Level4.svg | 8 +++ src/images/SkillLevel/Level5.svg | 8 +++ src/images/SkillLevel/Level6.svg | 8 +++ .../FillOutProfilePage3.tsx | 2 +- 10 files changed, 113 insertions(+), 51 deletions(-) create mode 100644 src/components/LanguageLevelModal/Levels.ts create mode 100644 src/images/SkillLevel/Level1.svg create mode 100644 src/images/SkillLevel/Level2.svg create mode 100644 src/images/SkillLevel/Level3.svg create mode 100644 src/images/SkillLevel/Level4.svg create mode 100644 src/images/SkillLevel/Level5.svg create mode 100644 src/images/SkillLevel/Level6.svg diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.module.scss b/src/components/LanguageLevelModal/LanguageLevelModal.module.scss index 1cd1566..16a384e 100644 --- a/src/components/LanguageLevelModal/LanguageLevelModal.module.scss +++ b/src/components/LanguageLevelModal/LanguageLevelModal.module.scss @@ -1,29 +1,43 @@ .modal { - align-items: center; - text-align: center; + text-align: left; &__item { width: 318px; padding: 19px 0 18px; margin: 0 auto; border-bottom: 1px solid var(--grey-gradient-100); + display: flex; + align-items: center; } &__item:last-child { - width: 318px; padding-bottom: 0; - margin: 0 auto; border-bottom: none; } - &__subtitle_languages { - color: var(--dark-violet); - font: var(--font-header-14-120-600); - margin: 0 0 8px; + &__languages { + flex: 1; + margin-right: 20px; + + &_title { + color: var(--dark-violet); + font: var(--font-header-14-120-600); + margin: 0 0 8px; + } + + &_text { + font: var(--font-text-14-120-400); + color: var(--dark-violet-gradient-400); + } } - &__text_languages { - color: var(--dark-violet-gradient-400); - font: var(--font-text-14-120-400); + &__image { + width: 39px; + height: 26px; + margin-left: 20px; + + &_hidden { + display: none; + } } } diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.tsx b/src/components/LanguageLevelModal/LanguageLevelModal.tsx index 7eb4410..c4381e4 100644 --- a/src/components/LanguageLevelModal/LanguageLevelModal.tsx +++ b/src/components/LanguageLevelModal/LanguageLevelModal.tsx @@ -1,50 +1,35 @@ +import React from "react"; import Modal from "../Modal/Modal"; +import {Levels} from "./Levels"; + import styles from "./LanguageLevelModal.module.scss"; -const LanguageLevelModal = ({isModalOpen, setModalOpen}) => { +interface LanguageLevelModalProps { + isModalOpen: boolean; + setModalOpen: React.Dispatch>; + pageName: string; +} + +const LanguageLevelModal: React.FC = ({isModalOpen, setModalOpen, pageName}) => { const handleCloseModal = () => { setModalOpen(false); }; - return( - -
-

Новичок

-

- Может представиться, простые тексты и базовые выражения -

-
-
-

Любитель

-

- Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы. -

-
-
-

Профессионал

-

- Может выразить личное мнение с аргументацией, поддержание диалога -

-
-
-

Эксперт

-

- Может выражаться естественно и без усилий, вспоминать интересные обороты речи -

-
-
-

Гуру

-

- Может поддержать дискуссии на узконаправленные темы -

-
-
-

Носитель

-

- Вырос в среде языка -

-
+ return ( + + {Levels.map((level) => ( +
+
+

{level.name}

+

+ {level.description} +

+
+ {level.name} +
+ ))}
); }; diff --git a/src/components/LanguageLevelModal/Levels.ts b/src/components/LanguageLevelModal/Levels.ts new file mode 100644 index 0000000..cba62fa --- /dev/null +++ b/src/components/LanguageLevelModal/Levels.ts @@ -0,0 +1,15 @@ +import level_1 from '../../images/SkillLevel/Level1.svg'; +import level_2 from '../../images/SkillLevel/Level2.svg'; +import level_3 from '../../images/SkillLevel/Level3.svg'; +import level_4 from '../../images/SkillLevel/Level4.svg'; +import level_5 from '../../images/SkillLevel/Level5.svg'; +import level_6 from '../../images/SkillLevel/Level6.svg'; + +export const Levels = [ + {level: level_1, name: "Новичок", description: "Может представиться, простые тексты и базовые выражения"}, + {level: level_2, name: "Любитель", description: "Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы."}, + {level: level_3, name: "Профессионал", description: "Может выразить личное мнение с аргументацией, поддержание диалога"}, + {level: level_4, name: "Эксперт", description: "Может выражаться естественно и без усилий, вспоминать интересные обороты речи"}, + {level: level_5, name: "Гуру", description: "Может поддержать дискуссии на узконаправленные темы"}, + {level: level_6, name: "Носитель", description: "Вырос в среде языка"}, +] diff --git a/src/images/SkillLevel/Level1.svg b/src/images/SkillLevel/Level1.svg new file mode 100644 index 0000000..457920c --- /dev/null +++ b/src/images/SkillLevel/Level1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level2.svg b/src/images/SkillLevel/Level2.svg new file mode 100644 index 0000000..5902469 --- /dev/null +++ b/src/images/SkillLevel/Level2.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level3.svg b/src/images/SkillLevel/Level3.svg new file mode 100644 index 0000000..861569c --- /dev/null +++ b/src/images/SkillLevel/Level3.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level4.svg b/src/images/SkillLevel/Level4.svg new file mode 100644 index 0000000..ad1a706 --- /dev/null +++ b/src/images/SkillLevel/Level4.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level5.svg b/src/images/SkillLevel/Level5.svg new file mode 100644 index 0000000..da3f2d2 --- /dev/null +++ b/src/images/SkillLevel/Level5.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/images/SkillLevel/Level6.svg b/src/images/SkillLevel/Level6.svg new file mode 100644 index 0000000..b0e1b12 --- /dev/null +++ b/src/images/SkillLevel/Level6.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx index aa9038e..f495917 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx @@ -86,7 +86,7 @@ const FillOutProfilePage1 = () => {
- + ); From 4de63980b2a42966ec7da2367b93dd043beed059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 25 Aug 2023 13:06:50 +0300 Subject: [PATCH 104/153] =?UTF-8?q?Fix=20=D0=A1=D1=83=D0=BF=D0=B5=D1=80=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=20LanguageLeve?= =?UTF-8?q?lModal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LanguageLevelModal.module.scss | 98 +++++-------- .../LanguageLevelModal/LanguageLevelModal.tsx | 135 ++---------------- src/components/LanguageLevelModal/Levels.ts | 15 ++ src/components/UI/Button/Button.module.scss | 13 +- src/components/UI/Button/Button.tsx | 2 +- 5 files changed, 74 insertions(+), 189 deletions(-) create mode 100644 src/components/LanguageLevelModal/Levels.ts diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.module.scss b/src/components/LanguageLevelModal/LanguageLevelModal.module.scss index 14f1041..76a5ef1 100644 --- a/src/components/LanguageLevelModal/LanguageLevelModal.module.scss +++ b/src/components/LanguageLevelModal/LanguageLevelModal.module.scss @@ -1,73 +1,43 @@ -.sortModal { - width: 409px; - padding: 60px 45px 40px; +.modal { + text-align: left; - &__item { - width: 318px; - padding: 19px 0 19px; - margin: 0 auto; - border-bottom: 1px solid var(--grey-gradient-100); - display: flex; - align-items: center; - justify-content: center; - } + &__item { + width: 318px; + padding: 19px 0 18px; + margin: 0 auto; + border-bottom: 1px solid var(--grey-gradient-100); + display: flex; + align-items: center; + } - &__item:last-child { - width: 318px; - padding-bottom: 0; - margin: 0 auto; - border-bottom: none; - } - - &__languages { - flex: 1; - margin-right: 20px; - - &_subtitle { - color: var(--dark-violet); - font: var(--font-header-14-120-600); - margin: 0 0 8px; - } - - &_text { - font: var(--font-text-14-120-400); - color: var(--dark-violet-gradient-400); - } - } - - &__image { - width: 39px; - height: 26px; - margin-left: 20px; - } -} + &__item:last-child { + padding-bottom: 0; + border-bottom: none; + } -.modal { - align-items: center; - text-align: center; - - &__item { - width: 318px; - padding: 19px 0 18px; - margin: 0 auto; - border-bottom: 1px solid var(--grey-gradient-100); - } - - &__item:last-child { - width: 318px; - padding-bottom: 0; - margin: 0 auto; - border-bottom: none; - } - - &__subtitle_languages { + &__languages { + flex: 1; + margin-right: 20px; + + &_title { color: var(--dark-violet); font: var(--font-header-14-120-600); margin: 0 0 8px; } - - &__text_languages { + + &_text { + font: var(--font-text-14-120-400); color: var(--dark-violet-gradient-400); - font: var(--font-text-14-120-600); } - } \ No newline at end of file + } + + &__image { + width: 39px; + height: 26px; + margin-left: 20px; + + &_hidden { + display: none; + } + } +} \ No newline at end of file diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.tsx b/src/components/LanguageLevelModal/LanguageLevelModal.tsx index 7aa6e27..9aad2e8 100644 --- a/src/components/LanguageLevelModal/LanguageLevelModal.tsx +++ b/src/components/LanguageLevelModal/LanguageLevelModal.tsx @@ -1,12 +1,7 @@ import React from "react"; import Modal from "../Modal/Modal"; -import level_1 from '../../images/SkillLevel/Level1.svg'; -import level_2 from '../../images/SkillLevel/Level2.svg'; -import level_3 from '../../images/SkillLevel/Level3.svg'; -import level_4 from '../../images/SkillLevel/Level4.svg'; -import level_5 from '../../images/SkillLevel/Level5.svg'; -import level_6 from '../../images/SkillLevel/Level6.svg'; +import {Levels} from "./Levels"; import styles from "./LanguageLevelModal.module.scss"; @@ -16,127 +11,27 @@ interface LanguageLevelModalProps { pageName: string; } -interface ModalStyles { - [key: string]: string; -} -const LanguageLevelModal: React.FC = ({ isModalOpen, setModalOpen, pageName }) => { +const LanguageLevelModal: React.FC = ({isModalOpen, setModalOpen, pageName}) => { const handleCloseModal = () => { - setModalOpen(false); + setModalOpen(false); }; - const modalStyles: ModalStyles = { - Sort: styles.sortModal, - }; - - const renderModalContent = () => { - if (pageName === "Sort") { - return ( - <> -
-
-

Новичок

-

- Может представиться, простые тексты и базовые выражения -

-
- Новичок -
-
-
-

Любитель

-

- Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы. -

-
- Любитель -
-
-
-

Профессионал

-

- Может выразить личное мнение с аргументацией, поддержание диалога -

-
- Профессионал -
-
-
-

Эксперт

-

- Может выражаться естественно и без усилий, вспоминать интересные обороты речи -

-
- Эксперт -
-
-
-

Гуру

-

- Может поддержать дискуссии на узконаправленные темы -

-
- Гуру -
-
-
-

Носитель

-

- Вырос в среде языка + return ( + + {Levels.map((level) => ( +

+
+

{level.name}

+

+ {level.description}

- Носитель -
- - ); - } else { - return ( - <> -
-

Новичок

-

- Может представиться, простые тексты и базовые выражения -

-
-
-

Любитель

-

- Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы. -

-
-
-

Профессионал

-

- Может выразить личное мнение с аргументацией, поддержание диалога -

-
-
-

Эксперт

-

- Может выражаться естественно и без усилий, вспоминать интересные обороты речи -

-
-
-

Гуру

-

- Может поддержать дискуссии на узконаправленные темы -

-
-
-

Носитель

-

- Вырос в среде языка -

+ {level.name}
- - ); - }; - }; - - return ( - - {renderModalContent()} + ))} - ); + ); }; export default LanguageLevelModal; \ No newline at end of file diff --git a/src/components/LanguageLevelModal/Levels.ts b/src/components/LanguageLevelModal/Levels.ts new file mode 100644 index 0000000..df65f14 --- /dev/null +++ b/src/components/LanguageLevelModal/Levels.ts @@ -0,0 +1,15 @@ +import level_1 from '../../images/SkillLevel/Level1.svg'; +import level_2 from '../../images/SkillLevel/Level2.svg'; +import level_3 from '../../images/SkillLevel/Level3.svg'; +import level_4 from '../../images/SkillLevel/Level4.svg'; +import level_5 from '../../images/SkillLevel/Level5.svg'; +import level_6 from '../../images/SkillLevel/Level6.svg'; + +export const Levels = [ + {level: level_1, name: "Новичок", description: "Может представиться, простые тексты и базовые выражения"}, + {level: level_2, name: "Любитель", description: "Может рассказать о себе, своих увлечениях, поддержать диалог на простые темы."}, + {level: level_3, name: "Профессионал", description: "Может выразить личное мнение с аргументацией, поддержание диалога"}, + {level: level_4, name: "Эксперт", description: "Может выражаться естественно и без усилий, вспоминать интересные обороты речи"}, + {level: level_5, name: "Гуру", description: "Может поддержать дискуссии на узконаправленные темы"}, + {level: level_6, name: "Носитель", description: "Вырос в среде языка"}, +] \ No newline at end of file diff --git a/src/components/UI/Button/Button.module.scss b/src/components/UI/Button/Button.module.scss index 1231b66..52baa70 100644 --- a/src/components/UI/Button/Button.module.scss +++ b/src/components/UI/Button/Button.module.scss @@ -17,7 +17,7 @@ } .fontSize-16 { - font: var(--font-text-16-120-400); + font: var(--font-text-16-100-400); } .size-xs { @@ -46,6 +46,11 @@ background-color: var(--primory-button-hover); transition: 0.3s background-color ease; } + + &:disabled { + color: var(--blue-grey); + background: var(--grey-gradient-100); + } } .variant-transparent { @@ -61,11 +66,11 @@ .variant-gray { color: var(--dark-violet); - background-color: var(--grey-gradient-100); - border: 1px solid var(--grey-gradient-100); + background-color: #F0F2F6; + border: 1px solid #F0F2F6; &:active { - border: 1px solid var(--grey-gradient-100); + border: 1px solid #F0F2F6; background-color: var(--accent-color-salad); transition: 0.3s background-color ease; } diff --git a/src/components/UI/Button/Button.tsx b/src/components/UI/Button/Button.tsx index 3b76659..ecf2364 100644 --- a/src/components/UI/Button/Button.tsx +++ b/src/components/UI/Button/Button.tsx @@ -15,7 +15,7 @@ interface Props { children?: ReactNode; } -export const Button: FC = ({ className, type, variant = "primary", size = "big", fontSize = "16", disabled, onClick, children, }) => { +export const Button: FC = ({ className, type="button", variant = "primary", size = "big", fontSize = "16", disabled, onClick, children, }) => { return ( - ))} -
- )} -
-
- {pageName === "Sort" && - Object.entries(skillLevelNames).map(([key, level]) => ( - - ))} -
- - ); + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedSuggestionIndex !== null) { + handleLanguageSelect(sortedLanguages[selectedSuggestionIndex]); + } else if (inputValue.trim() !== '') { + const matchedLanguage = sortedLanguages.find(language => + language.name.toLowerCase() === inputValue.toLowerCase() + ); + if (matchedLanguage) { + handleLanguageSelect(matchedLanguage); + } + } + setIsOpen(false); + } else if (e.key === 'Backspace' || e.key === 'Delete') { + if (inputValue === '' && selectedSuggestionIndex === null) { + onLanguageChange(null); + } + setInputValue(''); + setSelectedSuggestionIndex(null); + setIsOpen(false); + } + }; + + const handleReset = () => { + onReset(); + setInputValue(''); + setSelectedSuggestionIndex(null); + setIsOpen(false); + setLanguage(initialLanguageAndLevels.language); + setSkillLevels(initialLanguageAndLevels.skillLevels); + }; + + const handleClearFilter = () => { + handleReset(); + onLanguageChange(null); + onSkillLevelsChange([]); + }; + + return ( + <> +
+ setIsOpen(!isOpen)} + onChange={(e) => setInputValue(e.target.value)} + onKeyDown={handleKeyDown} + /> + {isOpen && ( +
+ {sortedLanguages.map((language, index) => ( + + ))} +
+ )} +
+
+ {pageName === "Sort" && + Object.entries(skillLevelNames).map(([key, level]) => ( + + ))} +
+ + ); }; -export default LanguageLevel; \ No newline at end of file +export default LanguageLevel; diff --git a/src/components/LanguageModule/LanguageModule.tsx b/src/components/LanguageModule/LanguageModule.tsx index 2680b6f..524cf61 100644 --- a/src/components/LanguageModule/LanguageModule.tsx +++ b/src/components/LanguageModule/LanguageModule.tsx @@ -1,119 +1,145 @@ -import {FC, useEffect, useState} from "react"; - +import React, { FC, useEffect, useState } from "react"; import LanguageLevel from "../LanguageLevel/LanguageLevel"; -import {Button} from "../UI/Button/Button"; - -import {Language, SkillLevelEnum} from "../../utils/openapi"; -import {api} from "../../utils/constants"; - +import { Button } from "../UI/Button/Button"; +import { Language, SkillLevelEnum } from "../../utils/openapi"; +import { api } from "../../utils/constants"; import styles from "./LanguageModule.module.scss"; import cn from "classnames"; interface LanguageModuleProps { - pageName: string; - initialLanguageAndLevels: { language: Language | null; skillLevels: SkillLevelEnum[] }; - selectedLanguagesAndLevels: { language: Language | null; skillLevels: SkillLevelEnum[] }[]; // Вот этот пропс - setSelectedLanguagesAndLevels: (languagesAndLevels: { language: Language | null; skillLevels: SkillLevelEnum[] }[]) => void; - } + pageName: string; + initialLanguageAndLevels: { language: Language | null; skillLevels: SkillLevelEnum[] }; + selectedLanguagesAndLevels: { + language: Language | null; + skillLevels: SkillLevelEnum[]; + }[]; + setSelectedLanguagesAndLevels: ( + languagesAndLevels: { language: Language | null; skillLevels: SkillLevelEnum[] }[] + ) => void; +} + const LanguageModule: FC = ({ - pageName, - initialLanguageAndLevels, - selectedLanguagesAndLevels, - setSelectedLanguagesAndLevels + pageName, + initialLanguageAndLevels, + selectedLanguagesAndLevels, + setSelectedLanguagesAndLevels, }) => { - const [languagesData, setLanguagesData] = useState([]); + const [languagesData, setLanguagesData] = useState([]); + const [selectedLanguage, setSelectedLanguage] = useState(null); + const [selectedLanguages, setSelectedLanguages] = useState([]); + + useEffect(() => { + const languagesInUse = selectedLanguagesAndLevels.map(item => item.language); + setSelectedLanguages(languagesInUse); + }, [selectedLanguagesAndLevels]); + + const fetchLanguagesData = async () => { + try { + const response = await api.api.languagesList(); + const languages = response.data; + setLanguagesData(languages); + } catch (error) { + console.error("Ошибка при получении данных о языках:", error); + } + }; + + useEffect(() => { + fetchLanguagesData(); + }, []); - const [selectedLanguage, setSelectedLanguage] = useState(null); - const [selectedSkillLevels, setSelectedSkillLevels] = useState([]); + const handleLanguageChange = (language: Language | null, index: number) => { + if (index >= 0 && index < selectedLanguagesAndLevels.length) { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels[index].language = language; + updatedLanguagesAndLevels[index].skillLevels = []; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + } + }; + const handleSkillLevelsChange = (skillLevels: SkillLevelEnum[], index: number) => { + if (index >= 0 && index < selectedLanguagesAndLevels.length) { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels[index].skillLevels = skillLevels; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + } + }; - //Запрос массива языков - const fetchLanguagesData = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.languagesList(); - console.log('ответ получен -', response); - const languages = response.data; - setLanguagesData(languages); - } catch (error) { - console.error("Ошибка при получении данных о языках:", error); - } - }; + const handleReset = (index: number) => { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels[index].language = null; + updatedLanguagesAndLevels[index].skillLevels = []; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + }; - useEffect(() => { - fetchLanguagesData(); - }, []); + const handleClearFilter = () => { + const clearedLanguagesAndLevels = selectedLanguagesAndLevels.map((item) => ({ + language: null, + skillLevels: [], + })); + setSelectedLanguagesAndLevels(clearedLanguagesAndLevels); + }; - const handleAddLanguageAndLevel = (language: Language, skillLevels: SkillLevelEnum[]) => { - const existingLanguageIndex = selectedLanguagesAndLevels.findIndex( - lang => lang.language?.isocode === language.isocode - ); - - if (existingLanguageIndex === -1) { - setSelectedLanguagesAndLevels([...selectedLanguagesAndLevels, { language, skillLevels }]); - } else { - const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; - updatedLanguagesAndLevels[existingLanguageIndex].skillLevels = skillLevels; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - } - }; + const handleAddLanguage = () => { + if (selectedLanguagesAndLevels.length < 3) { + const updatedLanguagesAndLevels = [ + ...selectedLanguagesAndLevels, + { language: null, skillLevels: [] }, + ]; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + handleSkillLevelsChange([], updatedLanguagesAndLevels.length - 1); + } + }; - const handleRemoveLanguage = (index: number) => { - setSelectedLanguagesAndLevels((prevLanguages) => { - const updatedLanguages = [...prevLanguages]; - updatedLanguages.splice(index, 1); - return updatedLanguages; - }); - }; + const handleRemoveLanguage = (index: number) => { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels.splice(index, 1); + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + }; - return ( - <> - {selectedLanguagesAndLevels.map((item, index) => ( - { - const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; - updatedLanguagesAndLevels[index].language = language; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - }} - onSkillLevelsChange={(skillLevels) => { - const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; - updatedLanguagesAndLevels[index].skillLevels = skillLevels; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - }} - onReset={() => { - const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; - updatedLanguagesAndLevels[index].language = null; - updatedLanguagesAndLevels[index].skillLevels = []; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - }} - onRemoveLanguage={() => handleRemoveLanguage(index)} - /> - ))} - {selectedLanguagesAndLevels.length < 3 && ( -
- -
- )} - - ); + return ( + <> + {selectedLanguagesAndLevels.length === 0 && ( + handleLanguageChange(language, 0)} + onSkillLevelsChange={(skillLevels) => handleSkillLevelsChange(skillLevels, 0)} + onReset={() => handleReset(0)} + onRemoveLanguage={() => handleRemoveLanguage(0)} + /> + )} + {selectedLanguagesAndLevels.map((item, index) => ( + !selectedLanguages.includes(lang))} + selectedLanguage={item.language || selectedLanguage} + initialLanguageAndLevels={initialLanguageAndLevels} + selectedSkillLevels={item.skillLevels} + onLanguageChange={(language) => handleLanguageChange(language, index)} + onSkillLevelsChange={(skillLevels) => handleSkillLevelsChange(skillLevels, index)} + onReset={() => handleReset(index)} + onRemoveLanguage={() => handleRemoveLanguage(index)} + /> + ))} + {selectedLanguagesAndLevels.length < 3 && ( +
+ +
+ )} + + ); }; -export default LanguageModule; \ No newline at end of file +export default LanguageModule; diff --git a/src/components/Modal/Modal.module.scss b/src/components/Modal/Modal.module.scss index 41bdfa0..f1bfd02 100644 --- a/src/components/Modal/Modal.module.scss +++ b/src/components/Modal/Modal.module.scss @@ -1,6 +1,6 @@ .modal { &__container { - width: 540px; + width: max-content; display: flex; flex-direction: column; position: relative; @@ -31,4 +31,4 @@ transition: opacity 0.5s ease; } } -} +} \ No newline at end of file diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 82b0921..5ef0964 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -1,25 +1,22 @@ import React, { useState, useMemo } from 'react'; - import styles from "../Sort/Sort.module.scss"; import CountrySelection from "../CountrySelection/CountrySelection"; import LanguageModule from "../LanguageModule/LanguageModule"; import Gender from "../Gender/Gender"; import MultiRangeSlider from "../MultiRangeSlider/MultiRangeSlider"; -import LanguageLevelModal from "../LanguageLevelModal/LanguageLevelModal" - +import LanguageLevelModal from "../LanguageLevelModal/LanguageLevelModal"; import { Button } from "../UI/Button/Button"; import { Country, Language, SkillLevelEnum } from '../../utils/openapi'; -// type Filters = { -// native_languages: string; -// foreign_languages: { -// language: string; -// skill_level: SkillLevelEnum; -// }[]; -// gender: string | null; -// age: string; -// country: string; -// }; +interface Filters { + country: string; + languages: { + language: string; + skill_level: string; + }[]; + gender: string | null; + age: string; +} interface SortProps { onChangeSort: (sortType: any) => void; @@ -37,9 +34,6 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { const [leftValue, setLeftValue] = useState(18); const [rightValue, setRightValue] = useState(40); - - - const initialLanguageAndLevels = useMemo(() => { return { language: null, skillLevels: [] }; }, []); @@ -47,26 +41,36 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { const handleSliderChange = (left: number, right: number) => { setLeftValue(left); setRightValue(right); - }; - + }; - const handleClearFilter = () => { + const handleClearFilter = () => { + setSelectedCountries([]); setSelectedLanguagesAndLevels([initialLanguageAndLevels]); - selectedLanguagesAndLevels.forEach(item => { - item.language = null; - item.skillLevels = []; - }); - setSelectedLanguagesAndLevels([...selectedLanguagesAndLevels]); + setSelectedGender(null); setLeftValue(18); setRightValue(40); }; const handleFindButtonClick = () => { - - //onChangeSort(filters); - + const ageRange = `${leftValue},${rightValue}`; + const languageFilters = selectedLanguagesAndLevels + .filter(item => item.language !== null && item.skillLevels.length > 0) + .map(item => { + const skillLevels = item.skillLevels.join(', '); // Соединяем уровни владения через запятую и пробел + return `${item.language!.name} ${skillLevels}`; + }) + .join('; '); // Соединяем языки с разделителем ; + + const filters = { + age: ageRange, + country: selectedCountries.length > 0 ? selectedCountries[0].name : "", + gender: selectedGender || null, + languages: languageFilters, + }; + + onChangeSort(filters); }; - + return (
@@ -74,6 +78,7 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => {
@@ -84,21 +89,21 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { />
{showDefaultLanguageModule && ( - + )}

О партнере

Пол

@@ -131,4 +136,5 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => {
); }; + export default Sort; diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 4c84f75..faa60fc 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -35,10 +35,9 @@ const MainPage = () => { const [cardsListLength, setCardsListLength] = useState(0); const [isUsersList, setIsUsersList] = useState(false); const [category, setCategory] = useState({ name: 'Все', path: '' }); - const [sortType, setSortType] = useState({}); + const [filters, setFilters] = useState({}); const [isSortPopupOpen, setSortPopupOpen] = useState(false); - const [languagesData, setLanguagesData] = useState([]); - const [countriesData, setCountriesData] = useState([]); + const handleOpenSortPopup = () => { setSortPopupOpen(!isSortPopupOpen); @@ -47,9 +46,16 @@ const MainPage = () => { const getUsersList = async (filters: any) => { try { console.log('отправка запроса ---'); + const languageFilters = filters.languages.map((languageFilter: any) => { + return `${languageFilter.language},${languageFilter.skill_level}`; + }); + const response = await api.api.usersList({ ordering: `${category.path}`, - ...filters, + age: filters.age, + country: filters.country, + gender: filters.gender, + languages: languageFilters.join(';'), }); console.log('ответ получен -', response); setIsUsersList(true); @@ -65,8 +71,8 @@ const MainPage = () => { }; useEffect(() => { - getUsersList(sortType); - }, [category, sortType]); + getUsersList(filters); + }, [category, filters]); return ( <> @@ -110,7 +116,7 @@ const MainPage = () => {
diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 08ac44c..3eb6d6b 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -16,30 +16,44 @@ export interface AgeVisibility { /** Сериализатор для просмотра чата. */ export interface Chat { id: number; - /** - * Название - * @maxLength 128 - */ - name?: string; + initiator: UserShort; + receiver: UserShort; messages: Message[]; } /** Сериализатор для просмотра списка чатов. */ export interface ChatList { id: number; - /** Название */ - name: string; + initiator: UserShort; + receiver: UserShort; last_message: string; + unread: string; } -/** Сериализатор для просмотра чата. */ -export interface ChatRequest { +/** Сериализатор для создания личного чата. */ +export interface ChatStart { /** - * Название - * @maxLength 128 + * Слаг + * Слаг + */ + receiver: string | null; + /** @maxLength 10000 */ + message: string; +} + +/** Сериализатор для создания личного чата. */ +export interface ChatStartRequest { + /** + * Слаг + * Слаг + * @minLength 1 */ - name?: string; - messages: MessageRequest[]; + receiver: string | null; + /** + * @minLength 1 + * @maxLength 10000 + */ + message: string; } /** Сериализатор модели страны. */ @@ -85,26 +99,6 @@ export interface Goal { icon: string; } -/** Сериализатор для создания группового чата. */ -export interface GroupChatCreate { - /** - * Название - * @maxLength 128 - */ - name?: string; - members: (string | null)[]; -} - -/** Сериализатор для создания группового чата. */ -export interface GroupChatCreateRequest { - /** - * Название - * @maxLength 128 - */ - name?: string; - members: (string | null)[]; -} - export interface Interest { /** Название */ name: string; @@ -133,12 +127,16 @@ export interface Language { export interface Message { id: number; /** - * Отправитель сообщения - * Отправитель сообщения + * Слаг + * Слаг */ - sender?: number | null; + sender: string | null; /** @maxLength 10000 */ text: string | null; + /** @format uri */ + file_to_send?: string; + /** @format uri */ + photo_to_send?: string; /** * Ответ на другое сообщение * Ответ на другое сообщение @@ -148,28 +146,20 @@ export interface Message { * Сообщение отправлено * Сообщение отправлено */ - sender_keep?: boolean; + sender_keep: boolean; is_read: string; /** * Сообщение закреплено * Сообщение закреплено */ - is_pinned?: boolean; + is_pinned: boolean; read_by: UserShort[]; + /** @format date-time */ + timestamp: string; } /** Сериализатор модели Message. */ export interface MessageRequest { - /** - * Отправитель сообщения - * Отправитель сообщения - */ - sender?: number | null; - /** - * Чат - * Чат, к которому относится сообщение - */ - chat: number; /** * @minLength 1 * @maxLength 10000 @@ -184,16 +174,6 @@ export interface MessageRequest { * Ответ на другое сообщение */ responding_to?: number | null; - /** - * Сообщение отправлено - * Сообщение отправлено - */ - sender_keep?: boolean; - /** - * Сообщение закреплено - * Сообщение закреплено - */ - is_pinned?: boolean; } export type NullEnum = null; @@ -844,10 +824,11 @@ export class Api< }), /** - * @description Просмотреть свои чаты + * @description Просмотреть список всех своих личных чатов * * @tags chats * @name ChatsList + * @summary Просмотреть список чатов * @request GET:/api/v1/chats/ * @secure */ @@ -874,29 +855,11 @@ export class Api< }), /** - * @description Создать групповой чат - * - * @tags chats - * @name ChatsCreate - * @request POST:/api/v1/chats/ - * @secure - */ - chatsCreate: (data: GroupChatCreateRequest, params: RequestParams = {}) => - this.request({ - path: `/api/v1/chats/`, - method: 'POST', - body: data, - secure: true, - type: ContentType.Json, - format: 'json', - ...params, - }), - - /** - * @description Просмотреть чат + * @description Просмотреть чат и историю сообщений * * @tags chats * @name ChatsRetrieve + * @summary Просмотреть чат * @request GET:/api/v1/chats/{id}/ * @secure */ @@ -914,16 +877,40 @@ export class Api< * * @tags chats * @name ChatsSendMessageCreate - * @request POST:/api/v1/chats/{id}/send_message/ + * @summary Отправить сообщение + * @request POST:/api/v1/chats/{id}/send-message/ * @secure */ chatsSendMessageCreate: ( id: number, - data: ChatRequest, + data: MessageRequest, params: RequestParams = {}, ) => - this.request({ - path: `/api/v1/chats/${id}/send_message/`, + this.request({ + path: `/api/v1/chats/${id}/send-message/`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * @description Начать чат с пользователем, отправить первое сообщение + * + * @tags chats + * @name ChatsStartPersonalChatCreate + * @summary Начать чат с пользователем + * @request POST:/api/v1/chats/start-personal-chat/ + * @secure + */ + chatsStartPersonalChatCreate: ( + data: ChatStartRequest, + params: RequestParams = {}, + ) => + this.request({ + path: `/api/v1/chats/start-personal-chat/`, method: 'POST', body: data, secure: true, @@ -1229,22 +1216,6 @@ export class Api< ...params, }), - /** - * @description Отправка запроса на личный чат. - * - * @tags users - * @name UsersStartChatCreate - * @request POST:/api/v1/users/{slug}/start_chat/ - * @secure - */ - usersStartChatCreate: (slug: string, params: RequestParams = {}) => - this.request({ - path: `/api/v1/users/${slug}/start_chat/`, - method: 'POST', - secure: true, - ...params, - }), - /** * @description Разблокировать пользователя * From da04926b47eccb4a1299ed82da06a7c852eaa5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Sun, 27 Aug 2023 18:43:03 +0300 Subject: [PATCH 106/153] fix update --- src/components/Sort/Sort.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 5ef0964..021493c 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -55,22 +55,23 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { const ageRange = `${leftValue},${rightValue}`; const languageFilters = selectedLanguagesAndLevels .filter(item => item.language !== null && item.skillLevels.length > 0) - .map(item => { - const skillLevels = item.skillLevels.join(', '); // Соединяем уровни владения через запятую и пробел - return `${item.language!.name} ${skillLevels}`; - }) - .join('; '); // Соединяем языки с разделителем ; - - const filters = { + .map(item => ({ + language: item.language!.name, + skill_level: item.skillLevels.join(','), + })); + + const countryCodes = selectedCountries.map(country => country.code).join(','); + + const filters: Filters = { age: ageRange, - country: selectedCountries.length > 0 ? selectedCountries[0].name : "", + country: countryCodes, gender: selectedGender || null, languages: languageFilters, }; - + onChangeSort(filters); }; - + return (
From a6674b799e76df4133b51b5a1d32aa4f845276d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Sun, 27 Aug 2023 23:31:20 +0300 Subject: [PATCH 107/153] fix update red --- src/components/Sort/Sort.tsx | 18 ++- src/utils/openapi.ts | 268 +++++++++++++---------------------- 2 files changed, 113 insertions(+), 173 deletions(-) diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 021493c..3d2ab4c 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -53,24 +53,32 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { const handleFindButtonClick = () => { const ageRange = `${leftValue},${rightValue}`; + + // Фильтруем выбранные языки и уровни навыков const languageFilters = selectedLanguagesAndLevels .filter(item => item.language !== null && item.skillLevels.length > 0) .map(item => ({ language: item.language!.name, skill_level: item.skillLevels.join(','), })); - - const countryCodes = selectedCountries.map(country => country.code).join(','); - + + // Фильтруем выбранные страны и формируем строку с кодами стран + const countryCodes = selectedCountries + .filter(country => country.code !== null) + .map(country => country.code!.toUpperCase()) // Используем toUpperCase() для кодов в верхнем регистре + .join(','); + + // Фильтры для запроса const filters: Filters = { age: ageRange, - country: countryCodes, + country: countryCodes, // Строка с кодами стран в верхнем регистре, например: 'DE,AR' gender: selectedGender || null, languages: languageFilters, }; - + onChangeSort(filters); }; + return (
diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 3eb6d6b..a35a6d9 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -81,8 +81,8 @@ export interface Country { * * `Female` - Женский */ export enum GenderEnum { - Male = 'Male', - Female = 'Female', + Male = "Male", + Female = "Female", } export interface GenderVisibility { @@ -304,12 +304,12 @@ export interface SetPasswordRequest { * * `Native` - Носитель */ export enum SkillLevelEnum { - Newbie = 'Newbie', - Amateur = 'Amateur', - Profi = 'Profi', - Expert = 'Expert', - Guru = 'Guru', - Native = 'Native', + Newbie = "Newbie", + Amateur = "Amateur", + Profi = "Profi", + Expert = "Expert", + Guru = "Guru", + Native = "Native", } export interface TokenObtainPair { @@ -507,9 +507,9 @@ export interface UserShort { } export type QueryParamsType = Record; -export type ResponseFormat = keyof Omit; +export type ResponseFormat = keyof Omit; -export interface FullRequestParams extends Omit { +export interface FullRequestParams extends Omit { /** set parameter to `true` for call `securityWorker` for this request */ secure?: boolean; /** request path */ @@ -528,22 +528,16 @@ export interface FullRequestParams extends Omit { cancelToken?: CancelToken; } -export type RequestParams = Omit< - FullRequestParams, - 'body' | 'method' | 'query' | 'path' ->; +export type RequestParams = Omit; export interface ApiConfig { baseUrl?: string; - baseApiParams?: Omit; - securityWorker?: ( - securityData: SecurityDataType | null, - ) => Promise | RequestParams | void; + baseApiParams?: Omit; + securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; customFetch?: typeof fetch; } -export interface HttpResponse - extends Response { +export interface HttpResponse extends Response { data: D; error: E; } @@ -551,25 +545,24 @@ export interface HttpResponse type CancelToken = Symbol | string | number; export enum ContentType { - Json = 'application/json', - FormData = 'multipart/form-data', - UrlEncoded = 'application/x-www-form-urlencoded', - Text = 'text/plain', + Json = "application/json", + FormData = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded", + Text = "text/plain", } export class HttpClient { - public baseUrl: string = ''; + public baseUrl: string = ""; private securityData: SecurityDataType | null = null; - private securityWorker?: ApiConfig['securityWorker']; + private securityWorker?: ApiConfig["securityWorker"]; private abortControllers = new Map(); - private customFetch = (...fetchParams: Parameters) => - fetch(...fetchParams); + private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); private baseApiParams: RequestParams = { - credentials: 'same-origin', + credentials: "same-origin", headers: {}, - redirect: 'follow', - referrerPolicy: 'no-referrer', + redirect: "follow", + referrerPolicy: "no-referrer", }; constructor(apiConfig: ApiConfig = {}) { @@ -582,9 +575,7 @@ export class HttpClient { protected encodeQueryParam(key: string, value: any) { const encodedKey = encodeURIComponent(key); - return `${encodedKey}=${encodeURIComponent( - typeof value === 'number' ? value : `${value}`, - )}`; + return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; } protected addQueryParam(query: QueryParamsType, key: string) { @@ -593,37 +584,26 @@ export class HttpClient { protected addArrayQueryParam(query: QueryParamsType, key: string) { const value = query[key]; - return value.map((v: any) => this.encodeQueryParam(key, v)).join('&'); + return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); } protected toQueryString(rawQuery?: QueryParamsType): string { const query = rawQuery || {}; - const keys = Object.keys(query).filter( - (key) => 'undefined' !== typeof query[key], - ); + const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); return keys - .map((key) => - Array.isArray(query[key]) - ? this.addArrayQueryParam(query, key) - : this.addQueryParam(query, key), - ) - .join('&'); + .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) + .join("&"); } protected addQueryParams(rawQuery?: QueryParamsType): string { const queryString = this.toQueryString(rawQuery); - return queryString ? `?${queryString}` : ''; + return queryString ? `?${queryString}` : ""; } private contentFormatters: Record any> = { [ContentType.Json]: (input: any) => - input !== null && (typeof input === 'object' || typeof input === 'string') - ? JSON.stringify(input) - : input, - [ContentType.Text]: (input: any) => - input !== null && typeof input !== 'string' - ? JSON.stringify(input) - : input, + input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, + [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), [ContentType.FormData]: (input: any) => Object.keys(input || {}).reduce((formData, key) => { const property = input[key]; @@ -631,7 +611,7 @@ export class HttpClient { key, property instanceof Blob ? property - : typeof property === 'object' && property !== null + : typeof property === "object" && property !== null ? JSON.stringify(property) : `${property}`, ); @@ -640,10 +620,7 @@ export class HttpClient { [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), }; - protected mergeRequestParams( - params1: RequestParams, - params2?: RequestParams, - ): RequestParams { + protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { return { ...this.baseApiParams, ...params1, @@ -656,9 +633,7 @@ export class HttpClient { }; } - protected createAbortSignal = ( - cancelToken: CancelToken, - ): AbortSignal | undefined => { + protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { if (this.abortControllers.has(cancelToken)) { const abortController = this.abortControllers.get(cancelToken); if (abortController) { @@ -693,7 +668,7 @@ export class HttpClient { ...params }: FullRequestParams): Promise> => { const secureParams = - ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && + ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {}; @@ -702,28 +677,15 @@ export class HttpClient { const payloadFormatter = this.contentFormatters[type || ContentType.Json]; const responseFormat = format || requestParams.format; - return this.customFetch( - `${baseUrl || this.baseUrl || ''}${path}${ - queryString ? `?${queryString}` : '' - }`, - { - ...requestParams, - headers: { - ...(requestParams.headers || {}), - ...(type && type !== ContentType.FormData - ? { 'Content-Type': type } - : {}), - }, - signal: - (cancelToken - ? this.createAbortSignal(cancelToken) - : requestParams.signal) || null, - body: - typeof body === 'undefined' || body === null - ? null - : payloadFormatter(body), + return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), }, - ).then(async (response) => { + signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null, + body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), + }).then(async (response) => { const r = response as HttpResponse; r.data = null as unknown as T; r.error = null as unknown as E; @@ -760,9 +722,7 @@ export class HttpClient { * * API endpoints for LinguaChat */ -export class Api< - SecurityDataType extends unknown, -> extends HttpClient { +export class Api extends HttpClient { api = { /** * @description Takes a set of user credentials and returns an access and refresh JSON web token pair to prove the authentication of those credentials. @@ -771,16 +731,13 @@ export class Api< * @name AuthJwtCreateCreate * @request POST:/api/v1/auth/jwt/create/ */ - authJwtCreateCreate: ( - data: TokenObtainPairRequest, - params: RequestParams = {}, - ) => + authJwtCreateCreate: (data: TokenObtainPairRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/auth/jwt/create/`, - method: 'POST', + method: "POST", body: data, type: ContentType.Json, - format: 'json', + format: "json", ...params, }), @@ -791,16 +748,13 @@ export class Api< * @name AuthJwtRefreshCreate * @request POST:/api/v1/auth/jwt/refresh/ */ - authJwtRefreshCreate: ( - data: TokenRefreshRequest, - params: RequestParams = {}, - ) => + authJwtRefreshCreate: (data: TokenRefreshRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/auth/jwt/refresh/`, - method: 'POST', + method: "POST", body: data, type: ContentType.Json, - format: 'json', + format: "json", ...params, }), @@ -811,13 +765,10 @@ export class Api< * @name AuthJwtVerifyCreate * @request POST:/api/v1/auth/jwt/verify/ */ - authJwtVerifyCreate: ( - data: TokenVerifyRequest, - params: RequestParams = {}, - ) => + authJwtVerifyCreate: (data: TokenVerifyRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/auth/jwt/verify/`, - method: 'POST', + method: "POST", body: data, type: ContentType.Json, ...params, @@ -847,10 +798,10 @@ export class Api< ) => this.request({ path: `/api/v1/chats/`, - method: 'GET', + method: "GET", query: query, secure: true, - format: 'json', + format: "json", ...params, }), @@ -866,9 +817,9 @@ export class Api< chatsRetrieve: (id: number, params: RequestParams = {}) => this.request({ path: `/api/v1/chats/${id}/`, - method: 'GET', + method: "GET", secure: true, - format: 'json', + format: "json", ...params, }), @@ -881,18 +832,14 @@ export class Api< * @request POST:/api/v1/chats/{id}/send-message/ * @secure */ - chatsSendMessageCreate: ( - id: number, - data: MessageRequest, - params: RequestParams = {}, - ) => + chatsSendMessageCreate: (id: number, data: MessageRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/chats/${id}/send-message/`, - method: 'POST', + method: "POST", body: data, secure: true, type: ContentType.Json, - format: 'json', + format: "json", ...params, }), @@ -905,17 +852,14 @@ export class Api< * @request POST:/api/v1/chats/start-personal-chat/ * @secure */ - chatsStartPersonalChatCreate: ( - data: ChatStartRequest, - params: RequestParams = {}, - ) => + chatsStartPersonalChatCreate: (data: ChatStartRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/chats/start-personal-chat/`, - method: 'POST', + method: "POST", body: data, secure: true, type: ContentType.Json, - format: 'json', + format: "json", ...params, }), @@ -937,10 +881,10 @@ export class Api< ) => this.request({ path: `/api/v1/countries/`, - method: 'GET', + method: "GET", query: query, secure: true, - format: 'json', + format: "json", ...params, }), @@ -956,9 +900,9 @@ export class Api< countriesRetrieve: (code: string, params: RequestParams = {}) => this.request({ path: `/api/v1/countries/${code}/`, - method: 'GET', + method: "GET", secure: true, - format: 'json', + format: "json", ...params, }), @@ -982,10 +926,10 @@ export class Api< ) => this.request({ path: `/api/v1/goals/`, - method: 'GET', + method: "GET", query: query, secure: true, - format: 'json', + format: "json", ...params, }), @@ -1011,10 +955,10 @@ export class Api< ) => this.request({ path: `/api/v1/interests/`, - method: 'GET', + method: "GET", query: query, secure: true, - format: 'json', + format: "json", ...params, }), @@ -1036,10 +980,10 @@ export class Api< ) => this.request({ path: `/api/v1/languages/`, - method: 'GET', + method: "GET", query: query, secure: true, - format: 'json', + format: "json", ...params, }), @@ -1055,9 +999,9 @@ export class Api< languagesRetrieve: (isocode: string, params: RequestParams = {}) => this.request({ path: `/api/v1/languages/${isocode}/`, - method: 'GET', + method: "GET", secure: true, - format: 'json', + format: "json", ...params, }), @@ -1085,7 +1029,7 @@ export class Api< * * `Male` - Мужской * * `Female` - Женский */ - gender?: 'Female' | 'Male' | null; + gender?: "Female" | "Male" | null; is_online?: boolean; /** * ISO 639-1 Код языка @@ -1109,22 +1053,16 @@ export class Api< * * `Guru` - Гуру * * `Native` - Носитель */ - skill_level?: - | 'Amateur' - | 'Expert' - | 'Guru' - | 'Native' - | 'Newbie' - | 'Profi'; + skill_level?: "Amateur" | "Expert" | "Guru" | "Native" | "Newbie" | "Profi"; }, params: RequestParams = {}, ) => this.request({ path: `/api/v1/users/`, - method: 'GET', + method: "GET", query: query, secure: true, - format: 'json', + format: "json", ...params, }), @@ -1140,7 +1078,7 @@ export class Api< usersCreate: (data: UserCreateRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/users/`, - method: 'POST', + method: "POST", body: data, secure: true, type: ContentType.Json, @@ -1159,9 +1097,9 @@ export class Api< usersRetrieve: (slug: string, params: RequestParams = {}) => this.request({ path: `/api/v1/users/${slug}/`, - method: 'GET', + method: "GET", secure: true, - format: 'json', + format: "json", ...params, }), @@ -1177,7 +1115,7 @@ export class Api< usersBlockUserCreate: (slug: string, params: RequestParams = {}) => this.request({ path: `/api/v1/users/${slug}/block_user/`, - method: 'POST', + method: "POST", secure: true, ...params, }), @@ -1194,7 +1132,7 @@ export class Api< usersReportUserRetrieve: (slug: string, params: RequestParams = {}) => this.request({ path: `/api/v1/users/${slug}/report_user/`, - method: 'GET', + method: "GET", secure: true, ...params, }), @@ -1211,7 +1149,7 @@ export class Api< usersReportUserCreate: (slug: string, params: RequestParams = {}) => this.request({ path: `/api/v1/users/${slug}/report_user/`, - method: 'POST', + method: "POST", secure: true, ...params, }), @@ -1228,7 +1166,7 @@ export class Api< usersUnblockUserCreate: (slug: string, params: RequestParams = {}) => this.request({ path: `/api/v1/users/${slug}/unblock_user/`, - method: 'POST', + method: "POST", secure: true, ...params, }), @@ -1245,9 +1183,9 @@ export class Api< usersHideShowAgePartialUpdate: (params: RequestParams = {}) => this.request({ path: `/api/v1/users/hide_show_age/`, - method: 'PATCH', + method: "PATCH", secure: true, - format: 'json', + format: "json", ...params, }), @@ -1263,9 +1201,9 @@ export class Api< usersHideShowGenderPartialUpdate: (params: RequestParams = {}) => this.request({ path: `/api/v1/users/hide_show_gender/`, - method: 'PATCH', + method: "PATCH", secure: true, - format: 'json', + format: "json", ...params, }), @@ -1281,9 +1219,9 @@ export class Api< usersMeRetrieve: (params: RequestParams = {}) => this.request({ path: `/api/v1/users/me/`, - method: 'GET', + method: "GET", secure: true, - format: 'json', + format: "json", ...params, }), @@ -1296,17 +1234,14 @@ export class Api< * @request PATCH:/api/v1/users/me/ * @secure */ - usersMePartialUpdate: ( - data: PatchedUserProfileRequest, - params: RequestParams = {}, - ) => + usersMePartialUpdate: (data: PatchedUserProfileRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/users/me/`, - method: 'PATCH', + method: "PATCH", body: data, secure: true, type: ContentType.Json, - format: 'json', + format: "json", ...params, }), @@ -1322,7 +1257,7 @@ export class Api< usersMeDestroy: (params: RequestParams = {}) => this.request({ path: `/api/v1/users/me/`, - method: 'DELETE', + method: "DELETE", secure: true, ...params, }), @@ -1336,18 +1271,15 @@ export class Api< * @request POST:/api/v1/users/set_password/ * @secure */ - usersSetPasswordCreate: ( - data: SetPasswordRequest, - params: RequestParams = {}, - ) => + usersSetPasswordCreate: (data: SetPasswordRequest, params: RequestParams = {}) => this.request({ path: `/api/v1/users/set_password/`, - method: 'POST', + method: "POST", body: data, secure: true, type: ContentType.Json, - format: 'json', + format: "json", ...params, }), }; -} +} \ No newline at end of file From 318c67e5b409bda507ab44d19072bf40c4e2bf02 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 28 Aug 2023 10:11:46 +0300 Subject: [PATCH 108/153] fix: fillOutPages --- src/components/Avatars/Avatars.tsx | 1 - .../CountrySelection/CountrySelection.tsx | 1 - src/components/Gender/Gender.tsx | 3 + src/components/SignupSigninForm/model.ts | 2 +- src/components/UI/Input/Input.tsx | 4 +- src/components/UI/Textarea/Textarea.tsx | 2 +- .../FillOutProfilePage1.tsx | 53 +++++++++-- .../FillOutProfilePage2.tsx | 91 +++++++++---------- .../FillOutProfilePage5.tsx | 49 ++-------- .../FillOutProfilePage6.tsx | 5 +- .../FillOutProfilePages.module.scss | 32 ++++--- src/pages/FillOutProfilePages/model.ts | 2 - src/types/types.ts | 4 +- 13 files changed, 131 insertions(+), 118 deletions(-) diff --git a/src/components/Avatars/Avatars.tsx b/src/components/Avatars/Avatars.tsx index 80fb30d..bbd9f59 100644 --- a/src/components/Avatars/Avatars.tsx +++ b/src/components/Avatars/Avatars.tsx @@ -8,7 +8,6 @@ import cn from "classnames"; const Avatars = ({selectedAvatar, setSelectedAvatar}) => { const handleSetAvatar = (event: React.MouseEvent) => { setSelectedAvatar(event.target.src); - console.log(event.target.src); } return ( diff --git a/src/components/CountrySelection/CountrySelection.tsx b/src/components/CountrySelection/CountrySelection.tsx index 626678a..e2c8a53 100644 --- a/src/components/CountrySelection/CountrySelection.tsx +++ b/src/components/CountrySelection/CountrySelection.tsx @@ -211,7 +211,6 @@ const CountrySelection: FC = ({ fontSize={pageName === "FillOutProfile2" ? "16" : "14"} isLabelHintHidden={true} placeholder="Начните вводить название" - required onChange={handleSearchInputChange} onKeyDown={handleKeyDown} /> diff --git a/src/components/Gender/Gender.tsx b/src/components/Gender/Gender.tsx index 1fd37a0..1a4293b 100644 --- a/src/components/Gender/Gender.tsx +++ b/src/components/Gender/Gender.tsx @@ -18,6 +18,7 @@ const Gender = ({ selectedGender, setSelectedGender, componentName }: GenderProp return (
{

Пожалуйста, используйте форматы JPG и PNG

+
+ Аватар пользователя +
- + -
@@ -123,7 +151,8 @@ const FillOutProfilePage1 = () => { ? 'Loading' : } - + handleSetPhoto(event)}/>

Если у вас возникли сложности с загрузкой, попробуйте выбрать фотографию меньшего размера.

@@ -131,17 +160,21 @@ const FillOutProfilePage1 = () => {

Выберите свой аватар

- + -
@@ -152,4 +185,4 @@ const FillOutProfilePage1 = () => { ); } -export default FillOutProfilePage1; +export default observer(FillOutProfilePage1); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx index 8ffb9e4..8113a5e 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx @@ -1,16 +1,18 @@ import React, {FormEvent, useEffect, useMemo, useState} from "react"; import {useNavigate} from "react-router-dom"; +import {observer} from "mobx-react-lite"; import Header from "../../components/Header/Header"; import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import CountrySelection from "../../components/CountrySelection/CountrySelection"; import LanguageModule from "../../components/LanguageModule/LanguageModule"; import {Button} from "../../components/UI/Button/Button"; +import CountrySelection from "../../components/CountrySelection/CountrySelection"; import {Country, Language, SkillLevelEnum} from "../../utils/openapi"; import styles from './FillOutProfilePages.module.scss'; + const FillOutProfilePage2 = () => { const navigate = useNavigate(); @@ -25,11 +27,6 @@ const FillOutProfilePage2 = () => { { language: Language | null; skillLevels: SkillLevelEnum[] }[] >([initialLanguageAndLevels]); - const handleSelectedCountriesChange = () => { - setSelectedCountries(selectedCountries); - console.log(selectedCountries); - } - const handleReturnButtonClick = () => { navigate("/fill-out-1"); } @@ -54,46 +51,48 @@ const FillOutProfilePage2 = () => { return ( <> -
-
- -
- -

Укажите страну и родной язык

-
-
-

Страна, в которой Вы сейчас - живете

- -
-
-

Ваш родной язык, язык на котором - Вы - свободно говорите

- +
+
+ +
+ +

Укажите страну и родной язык

+ +
+

+ Страна, в которой Вы сейчас живете +

+ +
+
+

+ Ваш родной язык, язык на котором Вы свободно говорите +

+ +
+ +
- - -
-
- -) - ; + + + ); }; -export default FillOutProfilePage2; +export default observer(FillOutProfilePage2); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx index d06864c..49b8a63 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx @@ -1,41 +1,23 @@ import {FormEvent, useEffect, useState} from "react"; import {useNavigate} from "react-router-dom"; +import {observer} from "mobx-react-lite"; import Header from "../../components/Header/Header"; import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; import {Input} from "../../components/UI/Input/Input"; import {Button} from "../../components/UI/Button/Button"; -import {api} from "../../utils/constants"; -import {Interest, PaginatedInterestList} from "../../utils/openapi"; +import InterestsSelection from "../../components/InterestsSelection/InterestsSelection"; import {useModel} from "./model"; import styles from "./FillOutProfilePages.module.scss"; +import {Interest} from "../../utils/openapi"; const FillOutProfilePage5 = () => { const model = useModel(); const navigate = useNavigate(); - const [interestsList, setInterestsList] = useState([]); - - const getInterestsList = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.interestsList(); - console.log('ответ получен -', response); - - if (response.data && response.data.results) { - setInterestsList(response.data.results); - console.log(response.data.results); - } - } catch (error) { - console.error('Ошибка при получении данных -', error); - } - }; - - useEffect(() => { - getInterestsList(); - }, []); + const [selectedInterests, setSelectedInterests] = useState([]); const handleReturnButtonClick = () => { navigate("/fill-out-4"); @@ -58,24 +40,11 @@ const FillOutProfilePage5 = () => {

Укажите ваши интересы

- -
- {interestsList && - interestsList.map((interest, index) => ( - - )) - } -
+ ))} +
+
+ + ); +}; + +export default observer(InputSearchList); diff --git a/src/components/InterestsSelection/InterestsSelection.tsx b/src/components/InterestsSelection/InterestsSelection.tsx new file mode 100644 index 0000000..ba4d44f --- /dev/null +++ b/src/components/InterestsSelection/InterestsSelection.tsx @@ -0,0 +1,43 @@ +import {useEffect, useState} from "react"; +import {observer} from "mobx-react-lite"; + +import InputSearchList from "../InputSearchList/InputSearchList"; + +import {Interest} from "../../utils/openapi"; +import {api} from "../../utils/constants"; + +const InterestsSelection = ({pageName, onSelectedInterestsChange}) => { + const [interestsList, setInterestsList] = useState([]); + + const getInterestsList = async () => { + try { + console.log('отправка запроса ---'); + const response = await api.api.interestsList(); + console.log('ответ получен -', response); + + if (response.data && response.data.results) { + setInterestsList(response.data.results); + console.log(response.data.results); + } + } catch (error) { + console.error('Ошибка при получении данных -', error); + } + }; + + useEffect(() => { + getInterestsList(); + }, []); + + + + return ( + + ); +} + +export default observer(InterestsSelection); diff --git a/src/utils/rest/auth.ts b/src/utils/rest/auth.ts new file mode 100644 index 0000000..5491abb --- /dev/null +++ b/src/utils/rest/auth.ts @@ -0,0 +1,96 @@ +import {TokenObtainPairRequest} from "../openapi"; + +import {TokenRefresh} from "../openapi"; +import {TokenRefreshRequest} from "../openapi"; +import {UserCreateRequest} from "../openapi"; +import {UserRepr} from "../openapi"; + +import {api, headersWithToken as headers} from "../constants"; + +export type Email = string; +export type Password = string; + +export interface Tokens { + access: string; + refresh: string; +} + +export const TokenObtain = async ({ + email, password + }: { email: Email; password: Password }): Promise => { + + const { + data: {token}, + error + } = await api.api.authJwtCreateCreate({ + username: email, + password: password + }); + + if (error) { + throw error; + } + + if (token) { + return { + access: token.access as string, + refresh: token.refresh as string, + }; + } + + return null; +}; + +export const signInWithEmail = async (): Promise => { +const { + data: {user}, + loginError +} = api.api.usersMeRetrieve({headers}); + +if (loginError) { + throw loginError; +} + + if (user) { + return { + access: token.access as string, + refresh: token.refresh as string, + }; + } + + return null; +}; + + +export const signOut = async (): Promise => { + const {error} = await client.auth.signOut(); + + if (error) { + throw error; + } + +}; + +export const getMe = async (): Promise => { + const { + data: {session}, + error, + } = await client.auth.getSession(); + + if (error) { + throw error; + } + + if (!session) { + return null; + } + + const {user} = session; + + return { + id: user.id as string, + email: user.email as string, + firstName: user.user_metadata.first_name || null, + lastName: user.user_metadata.last_name || null, + }; +}; From 9cf1a28761f0cf770745c5e4dcc084fcb2b14539 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 28 Aug 2023 13:13:48 +0300 Subject: [PATCH 110/153] fix: rests --- .../SignupSigninForm/SignupSigninForm.tsx | 56 ++++---- src/components/SignupSigninForm/model.ts | 131 +++++++----------- src/models/userSession/User.ts | 62 +++++++++ src/models/userSession/UserSession.ts | 36 +++++ .../SignupSigninPage/SignupSigninPage.tsx | 3 +- src/utils/rest/auth.ts | 115 ++++++++------- src/utils/rest/getCountries.ts | 24 ++++ src/utils/rest/getInterests.ts | 23 +++ src/utils/rest/getLanguages.ts | 25 ++++ src/utils/rest/register.ts | 14 ++ 10 files changed, 317 insertions(+), 172 deletions(-) create mode 100644 src/models/userSession/User.ts create mode 100644 src/models/userSession/UserSession.ts create mode 100644 src/utils/rest/getCountries.ts create mode 100644 src/utils/rest/getInterests.ts create mode 100644 src/utils/rest/getLanguages.ts create mode 100644 src/utils/rest/register.ts diff --git a/src/components/SignupSigninForm/SignupSigninForm.tsx b/src/components/SignupSigninForm/SignupSigninForm.tsx index 7de134c..1db9312 100644 --- a/src/components/SignupSigninForm/SignupSigninForm.tsx +++ b/src/components/SignupSigninForm/SignupSigninForm.tsx @@ -1,37 +1,37 @@ import {useEffect, useState} from "react"; -import {useLocation, Link, useNavigate } from "react-router-dom"; +import {useLocation, Link} from "react-router-dom"; import {observer} from 'mobx-react-lite'; import {Input} from '../UI/Input/Input'; import {Button} from '../UI/Button/Button'; +import {loggedIn} from '../../models/LoggedIn'; -import styles from './SignupSigninForm.module.scss'; import {useModel} from './model'; + +import styles from './SignupSigninForm.module.scss'; import cn from "classnames"; -import { loggedIn } from '../../models/LoggedIn'; const SignupSigninForm = () => { const model = useModel(); - const navigate = useNavigate(); const location = useLocation(); const pathName: string = location.pathname; - const [isSignUp, setSignUp] = useState(true); + const[isSignUp, setIsSignUp] = useState(false); const checkIsSignUp = () => { pathName === "/signup" - ? setSignUp(true) - : setSignUp(false) - } + ? setIsSignUp(true) + : setIsSignUp(false) + }; useEffect(() => { checkIsSignUp(); }, [pathName]); - useEffect(() => { - model.getCurrentUser(); - }, [loggedIn.loggedIn]); + useEffect(() => { + model.getCurrentUser(); + }, [loggedIn.loggedIn]); useEffect(() => { console.log('loginForm') @@ -41,27 +41,29 @@ const SignupSigninForm = () => { return (
    - + Вход - + Регистрация
{isSignUp && ( - + )} { Продолжая, вы соглашаетесь с Условиями пользования Сервисом + > Условиями пользования Сервисом )} diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 303ef38..078d50e 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -1,16 +1,17 @@ -import {useLocalObservable} from "mobx-react-lite"; import {FormEvent} from "react"; -import {useNavigate} from "react-router-dom"; - -import {api} from "../../utils/constants"; -import {headersWithToken as headers} from "../../utils/constants"; +import {useLocation, useNavigate} from "react-router-dom"; +import {useLocalObservable} from "mobx-react-lite"; -import {loggedIn} from "../../models/LoggedIn"; +import {getMe, signInWithEmail} from "../../utils/rest/auth"; +import {signUp} from "../../utils/rest/register"; export const useModel = () => { - const navigate = useNavigate(); - const model = useLocalObservable(() => { + const navigate = useNavigate(); + + const location = useLocation(); + const pathName: string = location.pathname; + return { username: "", email: "", @@ -21,98 +22,60 @@ export const useModel = () => { message: "", refresh: "", access: "", - isModalOpen: false, - user: {}, + isSignUp: false, handleValue({name, value}: { name: "username" | "email" | "password" | "confirmPassword"; value: string }) { model[name] = value; }, - setModalOpen(newModalOpen: boolean) { - model.isModalOpen = newModalOpen; + get toMain(): string { + return "/"; + }, + + get toFillOut(): string { + return "fill-out-1"; }, async handleRegister(event: FormEvent) { event.preventDefault(); - try { - model.error = "", - model.message = "", - model.isLoading = true; - const response = await api.api.usersCreate({ - email: model.email, - username: model.username, - password: model.password, - }); - - console.log('ответ получен -', response); - - if (response) { - navigate("/fill-out-1"); - model.setModalOpen(true); - - console.log(response); - } - - model.isLoading = false; - } catch (error) { - console.error('Ошибка при получении данных -', error); - model.isLoading = false; - } + + model.error = ""; + model.message = ""; + model.isLoading = true; + + await signUp({ + email: model.email, + username: model.username, + password: model.password, + }); + + // navigate(model.toFillOut); + model.isLoading = false; }, - async handleLogin(event: FormEvent) { + async handleLogin(event: FormEvent) { event.preventDefault(); - try { - model.error = "", - model.message = "", - model.isLoading = true; - - const response = await api.api.authJwtCreateCreate({ - username: model.email, - password: model.password - }); - console.log('ответ login получен -', response); - - if (response && response.data.refresh && response.data.access) { - model.refresh = response.data.refresh; - model.access = response.data.access; - localStorage.setItem('accessToken', response.data.access); - localStorage.setItem('refreshToken', response.data.refresh); - - loggedIn.setLoggedInTrue(); - - console.log(loggedIn.loggedIn); - navigate("/"); - } - - model.isLoading = false; - - } catch (error) { - console.error('Ошибка при получении данных -', error); - model.isLoading = false; - } + + model.error = ""; + model.message = ""; + model.isLoading = true; + + await signInWithEmail({username: model.email, password: model.password}); + + model.isLoading = false; }, async getCurrentUser() { - try { - model.error = "", - model.message = "", - model.isLoading = true; - const response = await api.api.usersMeRetrieve({headers}); - - console.log('ответ user получен -', response); - - if (response) { - model.user = response; - console.log(model.user); - } - - model.isLoading = false; - } catch (error) { - console.error('Ошибка при получении данных -', error); - model.isLoading = false; - } + model.error = ""; + model.message = ""; + model.isLoading = true; + + await getMe(); + + // navigate(model.toMain); + model.isLoading = false; }, + }; }); diff --git a/src/models/userSession/User.ts b/src/models/userSession/User.ts new file mode 100644 index 0000000..b6ad512 --- /dev/null +++ b/src/models/userSession/User.ts @@ -0,0 +1,62 @@ +import { makeObservable, observable } from "mobx"; + +import {Country, GenderEnum, Goal, NullEnum, UserLanguage, UserRepr} from "../../utils/openapi"; + + +export class User { + username: string; + firsName: string; + avatar: string; + age: string; + slug: string | null; + country: Country; + languages: UserLanguage[]; + gender: GenderEnum | NullEnum | null; + goals: Goal[]; + interests: string[]; + about: string; + lasActivity: string | null; + iOnline: string; + genderIsHidden: boolean; + ageIsHidden: boolean; + role: string; + + constructor({ + username, firsName, avatar, age, slug, country, languages, gender, goals, interests, about, lasActivity, iOnline, genderIsHidden, ageIsHidden, role + }: UserRepr) { + makeObservable(this, { + username: observable, + firsName: observable, + avatar: observable, + age: observable, + slug: observable, + country: observable, + languages: observable, + gender: observable, + goals: observable, + interests: observable, + lasActivity: observable, + iOnline: observable, + genderIsHidden: observable, + ageIsHidden: observable, + role: observable, + }) + + this.username = username; + this.firsName = firsName; + this.avatar = avatar; + this.age = age; + this.slug = slug; + this.country = country; + this.languages = languages; + this.gender = gender; + this.goals = goals; + this.interests = interests; + this.lasActivity = lasActivity; + this.iOnline = iOnline; + this.genderIsHidden = genderIsHidden; + this.ageIsHidden = ageIsHidden; + this.role = role; + } + +} diff --git a/src/models/userSession/UserSession.ts b/src/models/userSession/UserSession.ts new file mode 100644 index 0000000..505dbfc --- /dev/null +++ b/src/models/userSession/UserSession.ts @@ -0,0 +1,36 @@ +import { action, computed, makeObservable, observable } from "mobx"; +import { User } from "./User"; + +import {UserRepr} from "../../utils/openapi"; + +class UserSession { + private _user: User | null = null; + + constructor() { + makeObservable(this, { + _user: observable, + user: computed, + isAuthenticated: computed, + update: action, + signOut: action, + }); + } + + get user() { + return this._user; + } + + get isAuthenticated(): boolean { + return this._user !== null; + } + + update(data: UserRepr) { + this._user = new User(data); + } + + signOut() { + this._user = null; + } +} + +export const userSession = new UserSession(); diff --git a/src/pages/SignupSigninPage/SignupSigninPage.tsx b/src/pages/SignupSigninPage/SignupSigninPage.tsx index 7cb735b..65c5dc8 100644 --- a/src/pages/SignupSigninPage/SignupSigninPage.tsx +++ b/src/pages/SignupSigninPage/SignupSigninPage.tsx @@ -5,6 +5,7 @@ import styles from './SignupSigninPage.module.scss'; import logo from "../../images/svg/logo.svg"; import PicturesBlock from "../../components/PicturesBlock/PicturesBlock"; import { Link } from 'react-router-dom'; +import {observer} from "mobx-react-lite"; const SignupSigninPage = () => { return ( @@ -24,4 +25,4 @@ const SignupSigninPage = () => { ); }; -export default SignupSigninPage; +export default observer(SignupSigninPage); diff --git a/src/utils/rest/auth.ts b/src/utils/rest/auth.ts index 5491abb..7fcf6ba 100644 --- a/src/utils/rest/auth.ts +++ b/src/utils/rest/auth.ts @@ -1,29 +1,24 @@ -import {TokenObtainPairRequest} from "../openapi"; - -import {TokenRefresh} from "../openapi"; -import {TokenRefreshRequest} from "../openapi"; -import {UserCreateRequest} from "../openapi"; -import {UserRepr} from "../openapi"; +import {loggedIn} from "../../models/LoggedIn"; + +import { + TokenObtainPairRequest, + TokenObtainPair, + UserRepr, + Country, + GenderEnum, + Goal, + NullEnum, + UserLanguage, +} from "../openapi"; import {api, headersWithToken as headers} from "../constants"; -export type Email = string; -export type Password = string; - -export interface Tokens { - access: string; - refresh: string; -} - -export const TokenObtain = async ({ - email, password - }: { email: Email; password: Password }): Promise => { - +export const signInWithEmail = async ({username, password}: TokenObtainPairRequest): Promise => { const { data: {token}, error } = await api.api.authJwtCreateCreate({ - username: email, + username: username, password: password }); @@ -32,6 +27,8 @@ export const TokenObtain = async ({ } if (token) { + localStorage.setItem('accessToken', token.access); + localStorage.setItem('refreshToken', token.refresh); return { access: token.access as string, refresh: token.refresh as string, @@ -41,20 +38,41 @@ export const TokenObtain = async ({ return null; }; -export const signInWithEmail = async (): Promise => { -const { - data: {user}, - loginError -} = api.api.usersMeRetrieve({headers}); +export const getMe = async (): Promise => { + const { + data: {user}, + error + } = api.api.usersMeRetrieve({headers}); + + if (error) { + throw error; + } -if (loginError) { - throw loginError; -} + if (!user) { + return null; + } if (user) { + loggedIn.setLoggedInTrue(); + console.log(loggedIn.loggedIn); + return { - access: token.access as string, - refresh: token.refresh as string, + username: user.username as string, + first_name: user.first_name as string, + avatar: user.avatar as string, + age: user.age as string, + slug: user.slug as string | null, + country: user.country as Country, + languages: user.languages as UserLanguage[], + gender: user.gender as GenderEnum | NullEnum | null, + goals: user.goals as Goal[], + interests: user.interests as string[], + about: user.about as string, + last_activity: user.last_activity as string | null, + is_online: user.is_online as string, + gender_is_hidden: user.gender_is_hidden as boolean, + age_is_hidden: user.age_is_hidden as boolean, + role: user.role as string }; } @@ -62,35 +80,12 @@ if (loginError) { }; -export const signOut = async (): Promise => { - const {error} = await client.auth.signOut(); +// export const signOut = async (): Promise => { +// const {error} = await api.api.signOut(); +// +// if (error) { +// throw error; +// } +// +// }; - if (error) { - throw error; - } - -}; - -export const getMe = async (): Promise => { - const { - data: {session}, - error, - } = await client.auth.getSession(); - - if (error) { - throw error; - } - - if (!session) { - return null; - } - - const {user} = session; - - return { - id: user.id as string, - email: user.email as string, - firstName: user.user_metadata.first_name || null, - lastName: user.user_metadata.last_name || null, - }; -}; diff --git a/src/utils/rest/getCountries.ts b/src/utils/rest/getCountries.ts new file mode 100644 index 0000000..80e8b9c --- /dev/null +++ b/src/utils/rest/getCountries.ts @@ -0,0 +1,24 @@ +import {Country} from "../openapi"; +import {api} from "../constants"; + +export const getCountries = async (): Promise => { + + const { + data: {countries}, + error + } = await api.api.countriesList(); + + if (error) { + throw error; + } + + if (countries) { + return countries.map((country) => ({ + code: country.code as string | null, + name: country.name as string, + flag_icon: country.flag_icon as string, + })); + } + + return []; +}; diff --git a/src/utils/rest/getInterests.ts b/src/utils/rest/getInterests.ts new file mode 100644 index 0000000..025368f --- /dev/null +++ b/src/utils/rest/getInterests.ts @@ -0,0 +1,23 @@ +import {Interest} from "../openapi"; +import {api} from "../constants"; + +export const getInterests = async (): Promise => { + + const { + data: {interests}, + error + } = await api.api.interestsList(); + + if (error) { + throw error; + } + + if (interests) { + return interests.map((interest) => ({ + name: interest.name as string, + sorting: interest.sorting as string, + })); + } + + return []; +}; diff --git a/src/utils/rest/getLanguages.ts b/src/utils/rest/getLanguages.ts new file mode 100644 index 0000000..f58b73a --- /dev/null +++ b/src/utils/rest/getLanguages.ts @@ -0,0 +1,25 @@ +import {Language} from "../openapi"; +import {api} from "../constants"; + +export const getLanguages = async (): Promise => { + + const { + data: {languages}, + error + } = await api.api.languagesList(); + + if (error) { + throw error; + } + + if (languages) { + return languages.map((language) => ({ + name: language.name as string, + name_local: language.name_local as string, + isocode: language.isocode as string, + sorting: language.sorting as number, + })); + } + + return []; +}; diff --git a/src/utils/rest/register.ts b/src/utils/rest/register.ts new file mode 100644 index 0000000..cbe6c29 --- /dev/null +++ b/src/utils/rest/register.ts @@ -0,0 +1,14 @@ +import {UserCreateRequest} from "../openapi"; +import {api} from "../constants"; + +export const signUp = async ({email, username, password}: UserCreateRequest): Promise => { + const {error} = await api.api.usersCreate({ + email: email, + username: username, + password: password + }); + + if (error) { + throw error; + } +}; From 3d3235a491ee5eec5cde944a8d0850bc8c6d653a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Mon, 28 Aug 2023 16:13:21 +0300 Subject: [PATCH 111/153] =?UTF-8?q?fix=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D1=82=D0=B8=D1=82=D0=BE=D1=80=20+=20=D0=B5?= =?UTF-8?q?=D1=81=D0=BB=D0=B8=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CountrySelection/CountrySelection.tsx | 485 ++++++++++-------- .../LanguageLevel/LanguageLevel.tsx | 67 ++- .../LanguageModule/LanguageModule.tsx | 76 ++- src/components/Sort/Sort.tsx | 49 +- src/components/UI/Button/Button.tsx | 2 +- src/pages/MainPage/MainPage.tsx | 2 - 6 files changed, 373 insertions(+), 308 deletions(-) diff --git a/src/components/CountrySelection/CountrySelection.tsx b/src/components/CountrySelection/CountrySelection.tsx index 0b4d8f1..e5ab40e 100644 --- a/src/components/CountrySelection/CountrySelection.tsx +++ b/src/components/CountrySelection/CountrySelection.tsx @@ -1,135 +1,148 @@ -import {FC, useEffect, useState} from "react"; +import { FC, useEffect, useState } from 'react'; -import {Button} from "../UI/Button/Button"; -import {Input} from "../UI/Input/Input"; +import { Button } from '../UI/Button/Button'; +import { Input } from '../UI/Input/Input'; -import {Country} from '../../utils/openapi'; -import {api} from "../../utils/constants"; +import { Country } from '../../utils/openapi'; +import { api } from '../../utils/constants'; -import styles from "./CountrySelection.module.scss"; +import styles from './CountrySelection.module.scss'; import classNames from 'classnames'; interface CountrySelectionProps { - pageName: string; - onSelectedCountriesChange: (selectedCountries: Country[]) => void; - onClearFilter: () => void; + pageName: string; + onSelectedCountriesChange: (selectedCountries: Country[]) => void; + onClearFilter: () => void; } const CountrySelection: FC = ({ - pageName, - onSelectedCountriesChange, - onClearFilter, - }) => { - const [countriesData, setCountriesData] = useState([]); - const [isCountryListVisible, setCountryListVisible] = useState(false); - const [searchValue, setSearchValue] = useState(''); - const [isError, setIsError] = useState(false); - const [errorMessage, setErrorMessage] = useState(''); - const [selectedCountries, setSelectedCountries] = useState([]); - const [filteredCountries, setFilteredCountries] = useState(countriesData); - const [selectedCountry, setSelectedCountry] = useState(null); - const [suggestedCountries, setSuggestedCountries] = useState([]); - const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(null); - const [lastPressedLetter, setLastPressedLetter] = useState(null); + pageName, + onSelectedCountriesChange, + onClearFilter, +}) => { + const [countriesData, setCountriesData] = useState([]); + const [isCountryListVisible, setCountryListVisible] = useState(false); + const [searchValue, setSearchValue] = useState(''); + const [isError, setIsError] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [selectedCountries, setSelectedCountries] = useState([]); + const [filteredCountries, setFilteredCountries] = + useState(countriesData); + const [selectedCountry, setSelectedCountry] = useState(null); + const [suggestedCountries, setSuggestedCountries] = useState([]); + const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState< + number | null + >(null); + const [lastPressedLetter, setLastPressedLetter] = useState( + null, + ); - const fetchCountriesData = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.countriesList(); - console.log('ответ получен -', response); - const countries = response.data.map((country) => ({ - code: country.code, - name: country.name, - flag_icon: country.flag_icon, - })); - setCountriesData(countries); - } catch (error) { - console.error("Ошибка при получении данных о странах:", error); - } - }; + const fetchCountriesData = async () => { + try { + console.log('отправка запроса ---'); + const response = await api.api.countriesList(); + console.log('ответ получен -', response); + const countries = response.data.map((country) => ({ + code: country.code, + name: country.name, + flag_icon: country.flag_icon, + })); + setCountriesData(countries); + } catch (error) { + console.error('Ошибка при получении данных о странах:', error); + } + }; - useEffect(() => { - fetchCountriesData(); - }, []); + useEffect(() => { + fetchCountriesData(); + }, []); - const i = pageName === "Sort" ? 5 : 1; + const i = pageName === 'Sort' ? 5 : 1; - const handleSelectCountry = (country: Country) => { - if (selectedCountries.length < i && !selectedCountries.includes(country)) { - const updatedSelectedCountries = [...selectedCountries, country]; - setSelectedCountries(updatedSelectedCountries); - setSelectedCountry(country); - setCountryListVisible(false); - setSearchValue(''); - onSelectedCountriesChange(updatedSelectedCountries); - } - }; + const handleSelectCountry = (country: Country) => { + if (selectedCountries.length < i && !selectedCountries.includes(country)) { + const updatedSelectedCountries = [...selectedCountries, country]; + setSelectedCountries(updatedSelectedCountries); + setSelectedCountry(country); + setCountryListVisible(false); + setSearchValue(''); + onSelectedCountriesChange(updatedSelectedCountries); + } + }; - const handleSearchInputChange = (e: React.ChangeEvent) => { - const newSearchValue = e.target.value; - setSearchValue(newSearchValue); - if (newSearchValue) { - setCountryListVisible(true); - } else { - setCountryListVisible(false); - } + const handleSearchInputChange = (e: React.ChangeEvent) => { + const newSearchValue = e.target.value; + setSearchValue(newSearchValue); + if (newSearchValue) { + setCountryListVisible(true); + } else { + setCountryListVisible(false); + } - const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); - const filtered = countriesData.filter((country) => - country.name.toLocaleLowerCase('ru').includes(searchValueLower) && !selectedCountries.includes(country) - ); - setFilteredCountries(filtered); + const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); + const filtered = countriesData.filter( + (country) => + country.name.toLocaleLowerCase('ru').includes(searchValueLower) && + !selectedCountries.includes(country), + ); + setFilteredCountries(filtered); - const suggested = countriesData.filter((country) => - country.name.toLocaleLowerCase('ru').startsWith(searchValueLower) - ); - setSuggestedCountries(suggested); + const suggested = countriesData.filter((country) => + country.name.toLocaleLowerCase('ru').startsWith(searchValueLower), + ); + setSuggestedCountries(suggested); - const firstLetter = searchValueLower.length > 0 ? searchValueLower.charAt(0) : null; - setLastPressedLetter(firstLetter); + const firstLetter = + searchValueLower.length > 0 ? searchValueLower.charAt(0) : null; + setLastPressedLetter(firstLetter); - const isInvalidSearch = newSearchValue.length > 0 && filtered.length === 0 && suggested.length === 0; - const errorMessage = isInvalidSearch ? 'Страны не существует, возможно ошибка' : ''; - setIsError(isInvalidSearch); - setErrorMessage(errorMessage); + const isInvalidSearch = + newSearchValue.length > 0 && + filtered.length === 0 && + suggested.length === 0; + const errorMessage = isInvalidSearch + ? 'Страны не существует, возможно ошибка' + : ''; + setIsError(isInvalidSearch); + setErrorMessage(errorMessage); - setCountryListVisible(filtered.length > 0 && newSearchValue.length > 0); + setCountryListVisible(filtered.length > 0 && newSearchValue.length > 0); - setSelectedSuggestionIndex(null); - }; + setSelectedSuggestionIndex(null); + }; - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return 0; - } else if (prevIndex < suggestedCountries.length - 1) { - return prevIndex + 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return suggestedCountries.length - 1; - } else if (prevIndex > 0) { - return prevIndex - 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'Enter') { - e.preventDefault(); - if (selectedSuggestionIndex !== null) { - handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); - } else if (selectedCountry) { - handleSelectCountry(selectedCountry); - } + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return 0; + } else if (prevIndex < suggestedCountries.length - 1) { + return prevIndex + 1; + } else { + return prevIndex; } - }; + }); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return suggestedCountries.length - 1; + } else if (prevIndex > 0) { + return prevIndex - 1; + } else { + return prevIndex; + } + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedSuggestionIndex !== null) { + handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); + } else if (selectedCountry) { + handleSelectCountry(selectedCountry); + } + } + }; const handleRemoveCountry = (country: Country) => { const updatedCountries = selectedCountries.filter( @@ -138,131 +151,147 @@ const CountrySelection: FC = ({ setSelectedCountries(updatedCountries); }; - const handleDropdownKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return 0; - } else if (prevIndex < suggestedCountries.length - 1) { - return prevIndex + 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return suggestedCountries.length - 1; - } else if (prevIndex > 0) { - return prevIndex - 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'Enter') { - e.preventDefault(); - if (selectedSuggestionIndex !== null) { - handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); - } else if (selectedCountry) { - handleSelectCountry(selectedCountry); - } + const handleDropdownKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return 0; + } else if (prevIndex < suggestedCountries.length - 1) { + return prevIndex + 1; + } else { + return prevIndex; } - }; - - const sortCountriesByLastLetter = () => { - if (lastPressedLetter) { - return filteredCountries.sort((a, b) => { - const nameA = a.name.toLowerCase(); - const nameB = b.name.toLowerCase(); - if (nameA.startsWith(lastPressedLetter) && !nameB.startsWith(lastPressedLetter)) { - return -1; - } - if (!nameA.startsWith(lastPressedLetter) && nameB.startsWith(lastPressedLetter)) { - return 1; - } - return nameA.localeCompare(nameB); - }); + }); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return suggestedCountries.length - 1; + } else if (prevIndex > 0) { + return prevIndex - 1; } else { - return filteredCountries; + return prevIndex; } - }; - - const handleClearFilter = () => { - onClearFilter(); - setSearchValue(""); - setIsError(false); - setErrorMessage(""); - setSelectedCountry(null); - setSuggestedCountries([]); - setSelectedSuggestionIndex(null); - setLastPressedLetter(null); - }; + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedSuggestionIndex !== null) { + handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); + } else if (selectedCountry) { + handleSelectCountry(selectedCountry); + } + } + }; - const handleSelectCountryFromList = (countryName: string) => { - const selectedCountry = countriesData.find(country => country.name.toLocaleLowerCase('ru') === countryName); - if (selectedCountry) { - handleSelectCountry(selectedCountry); + const sortCountriesByLastLetter = () => { + if (lastPressedLetter) { + return filteredCountries.sort((a, b) => { + const nameA = a.name.toLowerCase(); + const nameB = b.name.toLowerCase(); + if ( + nameA.startsWith(lastPressedLetter) && + !nameB.startsWith(lastPressedLetter) + ) { + return -1; + } + if ( + !nameA.startsWith(lastPressedLetter) && + nameB.startsWith(lastPressedLetter) + ) { + return 1; } - }; + return nameA.localeCompare(nameB); + }); + } else { + return filteredCountries; + } + }; - return ( -
- { + onClearFilter(); + setSearchValue(''); + setIsError(false); + setErrorMessage(''); + setSelectedCountry(null); + setSuggestedCountries([]); + setSelectedSuggestionIndex(null); + setLastPressedLetter(null); + }; + + const handleSelectCountryFromList = (countryName: string) => { + const selectedCountry = countriesData.find( + (country) => country.name.toLocaleLowerCase('ru') === countryName, + ); + if (selectedCountry) { + handleSelectCountry(selectedCountry); + } + }; + + return ( +
+ + {isError && ( + {errorMessage} + )} +
+ {selectedCountries.map((country) => ( +
+ {country.name} +
+ ))} + {isCountryListVisible && ( +
0, + })} + onKeyDown={handleDropdownKeyDown} + > + {sortCountriesByLastLetter().map((country) => + country && country.name ? ( +
+ handleSelectCountryFromList( + country.name.toLocaleLowerCase('ru'), + ) + } + className={classNames(styles.country__countryList_option, { + [styles.selected]: selectedCountry?.code === country.code, + [styles.suggested]: suggestedCountries.includes(country), + })} + > + {`${country.name} + {country.name} +
+ ) : null, )} -
- {selectedCountries.map((country) => ( -
- {country.name} -
- ))} - {isCountryListVisible && ( -
0, - })} - onKeyDown={handleDropdownKeyDown} - > - {sortCountriesByLastLetter().map((country) => ( - country && country.name ? ( -
handleSelectCountryFromList(country.name.toLocaleLowerCase('ru'))} - className={classNames(styles.country__countryList_option, { - [styles.selected]: selectedCountry?.code === country.code, - [styles.suggested]: suggestedCountries.includes(country), - })} - > - {`${country.name} - {country.name} -
- ) : null - ))} -
- )} -
-
- ); +
+ )} +
+
+ ); }; -export default CountrySelection; \ No newline at end of file +export default CountrySelection; diff --git a/src/components/LanguageLevel/LanguageLevel.tsx b/src/components/LanguageLevel/LanguageLevel.tsx index 3a9e8e8..47b6910 100644 --- a/src/components/LanguageLevel/LanguageLevel.tsx +++ b/src/components/LanguageLevel/LanguageLevel.tsx @@ -1,14 +1,14 @@ -import React, { useState, useEffect } from "react"; -import { Language, SkillLevelEnum } from "../../utils/openapi"; -import styles from "./LanguageLevel.module.scss"; +import React, { useState, useEffect } from 'react'; +import { Language, SkillLevelEnum } from '../../utils/openapi'; +import styles from './LanguageLevel.module.scss'; const skillLevelNames: Record = { - [SkillLevelEnum.Newbie]: "Новичок", - [SkillLevelEnum.Amateur]: "Любитель", - [SkillLevelEnum.Profi]: "Профи", - [SkillLevelEnum.Expert]: "Эксперт", - [SkillLevelEnum.Guru]: "Гуру", - [SkillLevelEnum.Native]: "Носитель", + [SkillLevelEnum.Newbie]: 'Новичок', + [SkillLevelEnum.Amateur]: 'Любитель', + [SkillLevelEnum.Profi]: 'Профи', + [SkillLevelEnum.Expert]: 'Эксперт', + [SkillLevelEnum.Guru]: 'Гуру', + [SkillLevelEnum.Native]: 'Носитель', }; interface LanguageLevelProps { @@ -20,7 +20,10 @@ interface LanguageLevelProps { onSkillLevelsChange: (skillLevels: SkillLevelEnum[]) => void; onReset: () => void; onRemoveLanguage: () => void; - initialLanguageAndLevels: { language: Language | null; skillLevels: SkillLevelEnum[] }; + initialLanguageAndLevels: { + language: Language | null; + skillLevels: SkillLevelEnum[]; + }; } const LanguageLevel: React.FC = ({ @@ -36,10 +39,15 @@ const LanguageLevel: React.FC = ({ }) => { const [isOpen, setIsOpen] = useState(false); const [inputValue, setInputValue] = useState(''); - const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(null); - - const [language, setLanguage] = useState(initialLanguageAndLevels.language); - const [skillLevels, setSkillLevels] = useState(initialLanguageAndLevels.skillLevels); + const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState< + number | null + >(null); + const [language, setLanguage] = useState( + initialLanguageAndLevels.language, + ); + const [skillLevels, setSkillLevels] = useState( + initialLanguageAndLevels.skillLevels, + ); useEffect(() => { setLanguage(selectedLanguage); @@ -49,7 +57,7 @@ const LanguageLevel: React.FC = ({ const filteredLanguages = languages.filter( (language) => language.name.toLowerCase().includes(inputValue.toLowerCase()) || - language.name_local.toLowerCase().includes(inputValue.toLowerCase()) + language.name_local.toLowerCase().includes(inputValue.toLowerCase()), ); const handleLanguageSelect = (language: Language) => { @@ -61,13 +69,18 @@ const LanguageLevel: React.FC = ({ const handleSkillLevelChange = (skillLevel: SkillLevelEnum) => { if (selectedSkillLevels.includes(skillLevel)) { - const updatedSkillLevels = selectedSkillLevels.filter((level) => level !== skillLevel); + const updatedSkillLevels = selectedSkillLevels.filter( + (level) => level !== skillLevel, + ); onSkillLevelsChange(updatedSkillLevels); } else { if (skillLevel === SkillLevelEnum.Native) { onSkillLevelsChange([SkillLevelEnum.Native]); } else { - if (selectedSkillLevels.length < 3 && !selectedSkillLevels.includes(SkillLevelEnum.Native)) { + if ( + selectedSkillLevels.length < 3 && + !selectedSkillLevels.includes(SkillLevelEnum.Native) + ) { const updatedSkillLevels = [...selectedSkillLevels, skillLevel]; onSkillLevelsChange(updatedSkillLevels); } @@ -76,7 +89,7 @@ const LanguageLevel: React.FC = ({ }; const sortedLanguages = filteredLanguages.sort((a, b) => - a.name.localeCompare(b.name, 'ru', { sensitivity: 'base' }) + a.name.localeCompare(b.name, 'ru', { sensitivity: 'base' }), ); const handleKeyDown = (e: React.KeyboardEvent) => { @@ -107,8 +120,9 @@ const LanguageLevel: React.FC = ({ if (selectedSuggestionIndex !== null) { handleLanguageSelect(sortedLanguages[selectedSuggestionIndex]); } else if (inputValue.trim() !== '') { - const matchedLanguage = sortedLanguages.find(language => - language.name.toLowerCase() === inputValue.toLowerCase() + const matchedLanguage = sortedLanguages.find( + (language) => + language.name.toLowerCase() === inputValue.toLowerCase(), ); if (matchedLanguage) { handleLanguageSelect(matchedLanguage); @@ -144,10 +158,10 @@ const LanguageLevel: React.FC = ({ <>
setIsOpen(!isOpen)} onChange={(e) => setInputValue(e.target.value)} onKeyDown={handleKeyDown} @@ -158,7 +172,7 @@ const LanguageLevel: React.FC = ({
- {pageName === "Sort" && + {pageName === 'Sort' && Object.entries(skillLevelNames).map(([key, level]) => (
@@ -141,7 +134,11 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { children={'Очистить фильтр'} onClick={handleClearFilter} /> - +
); }; diff --git a/src/components/UI/Button/Button.tsx b/src/components/UI/Button/Button.tsx index ecf2364..c1a6c7d 100644 --- a/src/components/UI/Button/Button.tsx +++ b/src/components/UI/Button/Button.tsx @@ -1,4 +1,4 @@ -import {MouseEventHandler, ReactNode, FC} from "react"; +import {MouseEventHandler, ReactNode, FC} from 'react'; import cn from 'classnames'; diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index faa60fc..8f14a74 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -2,8 +2,6 @@ import React, { useEffect, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { api } from '../../utils/constants'; -import { Country } from '../../utils/openapi'; -import { Language } from '../../utils/openapi'; import Card from '../../components/Card/Card'; import Header from '../../components/Header/Header'; From 4f977690bff809fa8b253fc1a2daa8d56ccd2d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Mon, 28 Aug 2023 16:51:11 +0300 Subject: [PATCH 112/153] =?UTF-8?q?=D0=A1=D0=BB=D0=B8=D0=BB=20=D1=81=D0=B2?= =?UTF-8?q?=D0=BE=D1=8E=20=D0=B2=D0=B5=D1=82=D0=BA=D1=83=20=D1=81=20=D0=9C?= =?UTF-8?q?=D1=84=D1=80=D0=B8=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UI/Input/Input.module.scss | 120 ---------------------- 1 file changed, 120 deletions(-) diff --git a/src/components/UI/Input/Input.module.scss b/src/components/UI/Input/Input.module.scss index 4a0f343..e69de29 100644 --- a/src/components/UI/Input/Input.module.scss +++ b/src/components/UI/Input/Input.module.scss @@ -1,120 +0,0 @@ -.inputElement { - display: flex; - flex-direction: column; - - &__label { - color: var(--dark-violet); - } - - &__label16 { - font: var(--font-text-16-120-400); - } - - &__label18 { - font: var(--font-title-18-120-500); - margin-bottom: 4px; - } - - &__hint { - margin-bottom: 12px; - font: var(--font-text-14-120-400); - color: var(--dark-violet-gradient-400); - - &_true { - display: none; - } - - &_false { - display: inline-block; - } - } - - &__input { - display: inline-block; - display: inline-block; - height: 40px; - box-sizing: border-box; - background-color: var(--color-base-white); - border: 1px solid var(--soft-violet); - border-radius: 20px; - padding: 8px 12px; - font: var(--font-text-14-100-400); - color: var(--dark-violet); - - &:hover { - border: 2px solid var(--soft-violet); - } - - :active { - border: 1px solid var(--dark-violet); - } - - &:disabled { - background: var(--grey-gradient-50); - border: none; - color: var(--soft-violet); - } - - &::placeholder { - color: var(--soft-violet); - } - - &_dateInput::-webkit-calendar-picker-indicator { - color: transparent; - opacity: 1; - background: url("../../../images/svg/input-type-date-calendar.svg") no-repeat center; - background-size: contain; - } - - &_dateInput::-webkit-datetime-edit-fields-wrapper { - color: var(--soft-violet); - } - - &_searchInput { - padding-left: 40px; - background-image: url("../../../images/svg/search.svg"); - background-repeat: no-repeat; - background-position: left 12px center; - background-size: 20px 20px; - } - - &_searchInput:hover { - padding-left: 39px; - } - - &_searchInput:active { - padding-left: 40px; - } - } -} - -.fontSize-14 { - font: var(--font-text-14-100-400); -} - -.fontSize-16 { - font: var(--font-text-16-100-400); -} - -/*.hint {*/ -/* display: block;*/ -/* margin-top: 6px;*/ -/* color: var(--color-gray-500);*/ -/* font: var(--font-text-sm-regular);*/ -/*}*/ - -/*.error {*/ -/* display: block;*/ -/* margin-top: 6px;*/ -/* color: var(--color-error-500);*/ -/* font: var(--font-text-sm-regular);*/ -/*}*/ - -/*.has-error {*/ -/* border-color: var(--color-error-300);*/ -/*}*/ - -/*.has-error:focus-visible {*/ -/* border-color: var(--color-error-300);*/ -/* box-shadow: var(--shadow-xs), 0 0 0 4px var(--color-error-100);*/ -/*}*/ \ No newline at end of file From 493460cdd7befcf4fd947f23e56faf2094b84453 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 28 Aug 2023 17:05:42 +0300 Subject: [PATCH 113/153] fix: prettier --- package-lock.json | 1454 +++-------------- src/components/App/App.tsx | 1 - src/components/App/providers/index.tsx | 1 - src/components/Avatars/Avatars.tsx | 65 +- src/components/Card/Card.tsx | 188 ++- src/components/Card/CardRandomAvatar.ts | 26 - src/components/Categories/Categories.tsx | 4 +- src/components/Gender/Gender.tsx | 113 +- .../InputSearchList/InputSearchList.tsx | 531 +++--- .../UI/GenderAndAgeIcon/GenderAndAgeIcon.tsx | 9 +- src/components/UI/Input/Input.tsx | 211 +-- .../selectedItems/selectedItems.module.scss | 0 .../UI/selectedItems/selectedItems.tsx | 53 + src/pages/MainPage/MainPage.tsx | 291 ++-- src/utils/rest/auth.ts | 127 +- 15 files changed, 1073 insertions(+), 2001 deletions(-) delete mode 100644 src/components/Card/CardRandomAvatar.ts create mode 100644 src/components/UI/selectedItems/selectedItems.module.scss create mode 100644 src/components/UI/selectedItems/selectedItems.tsx diff --git a/package-lock.json b/package-lock.json index 36e1015..6f8cb5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,17 +8,12 @@ "name": "conversation-exchange-frontend", "version": "0.1.0", "dependencies": { - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", "classnames": "^2.3.2", - "compose-function": "^3.0.3", - "gh-pages": "^5.0.0", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.1", - "react-scripts": "5.0.1", + "react-scripts": "^5.0.1", "react-select": "^5.7.4" }, "devDependencies": { @@ -51,7 +46,8 @@ "node_modules/@adobe/css-tools": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", - "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==" + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "dev": true }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", @@ -3031,17 +3027,6 @@ "node": ">=8" } }, - "node_modules/@jest/expect-utils": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.3.tgz", - "integrity": "sha512-nvOEW4YoqRKD9HBJ9OJ6przvIvP9qilp5nAn1462P5ZlL/MM9SgPEZFyjTGPfs7QkocdUsJa6KjHhyRn4ueItA==", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/fake-timers": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", @@ -3511,17 +3496,6 @@ "node": ">=8" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/source-map": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", @@ -3794,86 +3768,6 @@ "node": ">=8" } }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -4210,11 +4104,6 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz", "integrity": "sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==" }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" - }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -4449,405 +4338,115 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/dom": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", - "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "engines": { - "node": ">=14" + "node": ">= 6" } }, - "node_modules/@testing-library/dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10.13.0" } }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/@testing-library/dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "node_modules/@testing-library/dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, - "engines": { - "node": ">=8" + "@babel/types": "^7.0.0" } }, - "node_modules/@testing-library/dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/@testing-library/jest-dom": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", - "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" + "@babel/types": "^7.20.7" } }, - "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" + "@types/node": "*" } }, - "node_modules/@testing-library/jest-dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@types/compose-function": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/compose-function/-/compose-function-0.0.30.tgz", + "integrity": "sha512-TY6zsIzbLU9nsRMVwaq5hYNSoglao3ZOm2I4jAi1EojDZJdF8vtDqBG+f086WJ17pwRH4sQseXp/U6GVxUxHwA==", + "dev": true + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@types/node": "*" } }, - "node_modules/@testing-library/jest-dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@testing-library/jest-dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "node_modules/@testing-library/jest-dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@types/eslint": { + "version": "8.44.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", + "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", - "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@testing-library/react/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@testing-library/react/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@testing-library/react/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/react/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@testing-library/react/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/react/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" - }, - "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/compose-function": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/compose-function/-/compose-function-0.0.30.tgz", - "integrity": "sha512-TY6zsIzbLU9nsRMVwaq5hYNSoglao3ZOm2I4jAi1EojDZJdF8vtDqBG+f086WJ17pwRH4sQseXp/U6GVxUxHwA==", - "dev": true - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.44.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", - "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "@types/eslint": "*", + "@types/estree": "*" } }, "node_modules/@types/estree": { @@ -4924,44 +4523,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@types/jest/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -5050,14 +4611,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-transition-group": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", @@ -5129,14 +4682,6 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", - "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", - "dependencies": { - "@types/jest": "*" - } - }, "node_modules/@types/trusted-types": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", @@ -5880,11 +5425,6 @@ "deep-equal": "^2.0.5" } }, - "node_modules/arity-n": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", - "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==" - }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -5920,25 +5460,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array.prototype.findlastindex": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", @@ -6984,14 +6505,6 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, - "node_modules/compose-function": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", - "integrity": "sha512-xzhzTJ5eC+gmIzvZq+C3kCJHsp9os6tJkrigDRZclyGtOKINbZtE8n1Tzmeh32jW+BUDPbvZpibwvJHBLGMVwg==", - "dependencies": { - "arity-n": "^1.0.4" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -7388,11 +6901,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" - }, "node_modules/cssdb": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.1.tgz", @@ -7897,14 +7405,6 @@ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7948,11 +7448,6 @@ "node": ">=6.0.0" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" - }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -8091,11 +7586,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.498.tgz", "integrity": "sha512-4LODxAzKGVy7CJyhhN5mebwe7U2L29P+0G+HUriHnabm0d7LSff8Yn7t+Wq+2/9ze2Fu1dhX7mww090xfv7qXQ==" }, - "node_modules/email-addresses": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", - "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==" - }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -9035,21 +8525,6 @@ "node": ">= 0.8.0" } }, - "node_modules/expect": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.3.tgz", - "integrity": "sha512-x1vY4LlEMWUYVZQrFi4ZANXFwqYbJ/JNQspLVvzhW2BNY28aNcXMQH6imBbt+RBf5sVRTodYHXtSP/TLEU0Dxw==", - "dependencies": { - "@jest/expect-utils": "^29.6.3", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -9257,34 +8732,10 @@ "node": ">=10" } }, - "node_modules/filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/filenamify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dependencies": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.1", - "trim-repeated": "^1.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", "engines": { "node": ">= 0.4.0" } @@ -9656,19 +9107,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/fs-monkey": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", @@ -9791,27 +9229,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", - "dependencies": { - "async": "^3.2.4", - "commander": "^2.18.0", - "email-addresses": "^5.0.0", - "filenamify": "^4.3.0", - "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", - "globby": "^6.1.0" - }, - "bin": { - "gh-pages": "bin/gh-pages.js", - "gh-pages-clean": "bin/gh-pages-clean.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -9904,21 +9321,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -10418,14 +9820,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -11784,113 +11178,6 @@ "node": ">=8" } }, - "node_modules/jest-diff": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.3.tgz", - "integrity": "sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-docblock": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", @@ -12267,14 +11554,6 @@ "node": ">=8" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-haste-map": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", @@ -12565,280 +11844,41 @@ } }, "node_modules/jest-jasmine2/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.3.tgz", - "integrity": "sha512-6ZrMYINZdwduSt5Xu18/n49O1IgXdjsfG7NEZaQws9k69eTKWKcVbJBw/MZsjOZe2sSyJFmuzh8042XWwl54Zg==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" + "node_modules/jest-jasmine2/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-message-util/node_modules/supports-color": { + "node_modules/jest-jasmine2/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -12849,6 +11889,26 @@ "node": ">=8" } }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/jest-mock": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", @@ -13772,86 +12832,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-validate": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", @@ -14575,14 +13555,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -14852,14 +13824,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "bin": { - "lz-string": "bin/bin.js" - } - }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -14995,14 +13959,6 @@ "node": ">=6" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" - } - }, "node_modules/mini-css-extract-plugin": { "version": "2.7.6", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", @@ -15726,25 +14682,6 @@ "node": ">=0.10.0" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -17675,6 +16612,84 @@ } } }, + "node_modules/react-scripts/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/react-scripts/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/react-scripts/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/react-scripts/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/react-scripts/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/react-select": { "version": "5.7.4", "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.4.tgz", @@ -17753,18 +16768,6 @@ "node": ">=6.0.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz", @@ -18873,17 +17876,6 @@ "node": ">=6" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -18895,17 +17887,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/style-loader": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", @@ -19492,17 +18473,6 @@ "node": ">=8" } }, - "node_modules/trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -19842,14 +18812,6 @@ "node": ">=8" } }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 1450e50..9757ebb 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -1,5 +1,4 @@ import React from 'react'; - import { Routing } from '../../pages/Routing'; import { withProviders } from './providers'; diff --git a/src/components/App/providers/index.tsx b/src/components/App/providers/index.tsx index 46645d9..73b92f4 100644 --- a/src/components/App/providers/index.tsx +++ b/src/components/App/providers/index.tsx @@ -1,5 +1,4 @@ import compose from 'compose-function'; - import { withRouter } from './withRouter'; export const withProviders = compose(withRouter); diff --git a/src/components/Avatars/Avatars.tsx b/src/components/Avatars/Avatars.tsx index bbd9f59..55e5449 100644 --- a/src/components/Avatars/Avatars.tsx +++ b/src/components/Avatars/Avatars.tsx @@ -1,34 +1,43 @@ -import React from "react"; +import React from 'react'; -import {avatarList} from "./AvatarsIcons"; +import { avatarList } from './AvatarsIcons'; -import styles from "./Avatars.module.scss"; -import cn from "classnames"; +import styles from './Avatars.module.scss'; +import cn from 'classnames'; -const Avatars = ({selectedAvatar, setSelectedAvatar}) => { - const handleSetAvatar = (event: React.MouseEvent) => { - setSelectedAvatar(event.target.src); - } - - return ( -
- {avatarList && - avatarList.map((avatar, i) => { - return ((i < avatarList.length) && - - ); - }) - } -
- ); +interface AvatarProps { + selectedAvatar: string; + setSelectedAvatar: (selectedAvatar: string) => void; } +const Avatars = ({ selectedAvatar, setSelectedAvatar }: AvatarProps) => { + const handleSetAvatar = (event: React.MouseEvent) => { + const target = event.target as HTMLImageElement; + setSelectedAvatar(target.src); + }; + + return ( +
+ {avatarList && + avatarList.map((avatar, i) => { + return ( + i < avatarList.length && ( + + ) + ); + })} +
+ ); +}; + export default Avatars; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index aa3e5f3..810eab3 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,103 +1,117 @@ -import {FC} from 'react'; +import { FC } from 'react'; +import CountryIcon from '../UI/CountryIcon/CountryIcon'; +import UserStatusIsOnline from '../UI/UserStatusIsOnline/UserStatusIsOnline'; +import GenderAndAgeIcon from '../UI/GenderAndAgeIcon/GenderAndAgeIcon'; import LanguagesTag from '../UI/LanguagesTag/LanguagesTag'; -import {сardRandomMaleAvatar, сardRandomFemaleAvatar} from "./CardRandomAvatar"; import arrows from '../../images/svg/card-arrows-parallel.svg'; -import CountryIcon from '../UI/CountryIcon/CountryIcon'; -import UserStatusIsOnline from '../UI/UserStatusIsOnline/UserStatusIsOnline'; -import GenderAndAgeIcon from '../UI/GenderAndAgeIcon/GenderAndAgeIcon'; -import {Country, UserLanguage} from '../../utils/openapi'; +import { + UserLanguage, + Country, + GenderEnum, + NullEnum, +} from '../../utils/openapi'; import styles from './Card.module.scss'; -import cn from "classnames"; - +import cn from 'classnames'; interface ICards { - country?: Country | null; - is_online: boolean; - avatar?: string; - first_name?: string; - gender?: string; - gender_is_hidden: boolean; - age?: string; - age_is_hidden?: boolean; - about?: string; - languages?: any; -} + first_name: string; + avatar: string; + age: string; -const Card: FC = ({ - country = null, - avatar, - first_name, - gender, - gender_is_hidden, - age, - age_is_hidden, - about, - is_online, - languages, - }) => { + country: Country; + languages: UserLanguage[]; - const getUserAvatar = () => { - return avatar - ? avatar - : gender === "Male" - ? сardRandomMaleAvatar() - : сardRandomFemaleAvatar() - }; + gender: GenderEnum | NullEnum | null; - return ( -
-
- - -
-
- Аватар пользователя -
-
-

{first_name}

- -
-
-
    - {languages && - languages.map((languages: UserLanguage) => { - return (languages.skill_level === "Native" && - - ); - })} -
-
- Параллельные стрелки между изученными и изучаемыми языками -
-
    - {languages && - languages.map((languages: UserLanguage) => { - return (languages.skill_level !== "Native" && - - ); - })} -
-
-
-
-
+ about: string; + + is_online: boolean; + gender_is_hidden: boolean; + age_is_hidden: boolean; +} + +const Card: FC = ({ + first_name, + avatar, + age, + country, + languages, + gender, + about, + is_online, + gender_is_hidden, + age_is_hidden, +}: ICards) => { + return ( +
+
+ + +
+
+ Аватар пользователя +
+
+

+ {first_name} +

+ +
+
+
    + {languages && + languages.map((languages: UserLanguage) => { + return ( + languages.skill_level === 'Native' && ( + + ) + ); + })} +
+
+ Параллельные стрелки между изученными и изучаемыми языками +
+
    + {languages && + languages.map((languages: UserLanguage) => { + return ( + languages.skill_level !== 'Native' && ( + + ) + ); + })} +
+
-

{about}

-
- ); +
+ + +

{about}

+
+ ); }; export default Card; diff --git a/src/components/Card/CardRandomAvatar.ts b/src/components/Card/CardRandomAvatar.ts deleted file mode 100644 index 998f1dd..0000000 --- a/src/components/Card/CardRandomAvatar.ts +++ /dev/null @@ -1,26 +0,0 @@ -import cardAvatarMale1 from '../../images/svg/avatars/card-avatar-male-1.svg'; -import cardAvatarMale2 from '../../images/svg/avatars/card-avatar-male-2.svg'; -import cardAvatarFemale1 from '../../images/svg/avatars/card-avatar-female-1.svg'; -import cardAvatarFemale2 from '../../images/svg/avatars/card-avatar-female-2.svg'; -import cardAvatarFemale3 from '../../images/svg/avatars/card-avatar-female-3.svg'; -import cardAvatarFemale4 from '../../images/svg/avatars/card-avatar-female-4.svg'; - -const cardMaleAvatars = [cardAvatarMale1, cardAvatarMale2]; - -const cardFemaleAvatars = [ - cardAvatarFemale1, - cardAvatarFemale2, - cardAvatarFemale3, - cardAvatarFemale4, -]; - -export const сardRandomMaleAvatar = () => { - const randomAvatars = Math.floor(Math.random() * cardMaleAvatars.length); - console.log(cardMaleAvatars[randomAvatars]); - return cardMaleAvatars[randomAvatars]; -}; - -export const сardRandomFemaleAvatar = () => { - const randomAvatars = Math.floor(Math.random() * cardFemaleAvatars.length); - return cardFemaleAvatars[randomAvatars]; -}; diff --git a/src/components/Categories/Categories.tsx b/src/components/Categories/Categories.tsx index 5d78e46..5ecf066 100644 --- a/src/components/Categories/Categories.tsx +++ b/src/components/Categories/Categories.tsx @@ -18,10 +18,8 @@ const Categories: React.FC = memo( { name: 'Сейчас онлайн', path: 'True' }, { name: 'Новые пользователи', path: '-date_joined' }, ]; - const [isActive, setIsActive] = useState('Все'); - const setActiveStyle = (item: any) => { - setIsActive(item.name); + const setActiveStyle = (item: { name: string; path: string }) => { onChangeCategory(item); }; diff --git a/src/components/Gender/Gender.tsx b/src/components/Gender/Gender.tsx index 1a4293b..6b58ffe 100644 --- a/src/components/Gender/Gender.tsx +++ b/src/components/Gender/Gender.tsx @@ -1,61 +1,70 @@ -import { Button } from "../UI/Button/Button"; -import { GenderEnum } from "../../utils/openapi"; +import { Button } from '../UI/Button/Button'; -import styles from "./Gender.module.scss"; -import cn from "classnames"; +import { GenderEnum, NullEnum } from '../../utils/openapi'; + +import styles from './Gender.module.scss'; +import cn from 'classnames'; interface GenderProps { - selectedGender: GenderEnum | 'unspecified' | undefined; - setSelectedGender: (gender: GenderEnum | 'unspecified' | undefined) => void; - componentName: string; + selectedGender: GenderEnum | NullEnum | null; + setSelectedGender: (gender: GenderEnum | NullEnum | null) => void; + componentName: string; } -const Gender = ({ selectedGender, setSelectedGender, componentName }: GenderProps) => { - const handleGenderSelection = (gender: GenderEnum | 'unspecified' | undefined) => { - setSelectedGender(gender); - }; +const Gender = ({ + selectedGender, + setSelectedGender, + componentName, +}: GenderProps) => { + const handleGenderSelection = (gender: GenderEnum | NullEnum | null) => { + setSelectedGender(gender); + }; - return ( -
-
- ); + return ( +
+
+ ); }; export default Gender; diff --git a/src/components/InputSearchList/InputSearchList.tsx b/src/components/InputSearchList/InputSearchList.tsx index ef20241..b65ac37 100644 --- a/src/components/InputSearchList/InputSearchList.tsx +++ b/src/components/InputSearchList/InputSearchList.tsx @@ -1,281 +1,320 @@ -import {useEffect, useState} from "react"; -import {observer} from "mobx-react-lite"; +import { useEffect, useState } from 'react'; +import { observer } from 'mobx-react-lite'; -import {Input} from "../UI/Input/Input"; +import { Input } from '../UI/Input/Input'; -import styles from "./InputSearchList.module.scss"; -import classNames from "classnames"; -import cn from "classnames"; -import {Interest, Language, SkillLevelEnum} from "../../utils/openapi"; +import styles from './InputSearchList.module.scss'; +import classNames from 'classnames'; +import cn from 'classnames'; +import { Interest } from '../../utils/openapi'; + +interface IPageName { + pageName: string; + data: ; +} const InputSearchList = ({ - pageName, - data, - dataList, - onSelectedItemsChange - }) => { - const [isSearchListVisible, setSearchListVisible] = useState(false); - const [searchValue, setSearchValue] = useState(''); - const [filteredItems, setFilteredItems] = useState([]); - const [selectedItem, setSelectedItem] = useState(null); - const [lastPressedLetter, setLastPressedLetter] = useState(null); - const [suggestedItems, setSuggestedItems] = useState([]); - const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(null); - const [isError, setIsError] = useState(false); - const [errorMessage, setErrorMessage] = useState(''); - const [selectedItems, setSelectedItems] = useState([]); + pageName, + data, + dataList, + onSelectedItemsChange, +}) => { + const [isSearchListVisible, setSearchListVisible] = useState(false); + const [searchValue, setSearchValue] = useState(''); + const [filteredItems, setFilteredItems] = useState([]); + const [selectedItem, setSelectedItem] = useState(null); + const [lastPressedLetter, setLastPressedLetter] = useState( + null, + ); + const [suggestedItems, setSuggestedItems] = useState([]); + const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState< + number | null + >(null); + const [isError, setIsError] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [selectedItems, setSelectedItems] = useState([]); - const i = data === "countries" - ? pageName === "Sort" ? 5 : 1 - : 100; + const i = data === 'countries' ? (pageName === 'Sort' ? 5 : 1) : 100; - console.log(isSearchListVisible); - console.log(searchValue); - console.log(i); - console.log(selectedItems); + console.log(isSearchListVisible); + console.log(searchValue); + console.log(i); + console.log(selectedItems); - useEffect(() => { - setSelectedItems(dataList); - }, [dataList]); + useEffect(() => { + setSelectedItems(dataList); + }, [dataList]); - const handleInputValue = (event) => { - const newSearchValue = event.value; - console.log(newSearchValue); - setSearchValue(newSearchValue); + const handleInputValue = (event) => { + const newSearchValue = event.value; + console.log(newSearchValue); + setSearchValue(newSearchValue); - if (newSearchValue.length > 0) { - setSearchListVisible(true); - } else { - setSearchListVisible(false); - } + if (newSearchValue.length > 0) { + setSearchListVisible(true); + } else { + setSearchListVisible(false); + } - const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); + const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); - const filtered = dataList.filter((item) => - item.name.toLocaleLowerCase('ru').includes(searchValueLower) && !selectedItems.includes(item) - ); - setFilteredItems(filtered); + const filtered = dataList.filter( + (item) => + item.name.toLocaleLowerCase('ru').includes(searchValueLower) && + !selectedItems.includes(item), + ); + setFilteredItems(filtered); - const suggested = dataList.filter((item) => - item.name.toLocaleLowerCase('ru').startsWith(searchValueLower) - ); - setSuggestedItems(suggested); + const suggested = dataList.filter((item) => + item.name.toLocaleLowerCase('ru').startsWith(searchValueLower), + ); + setSuggestedItems(suggested); - const firstLetter = searchValueLower.length > 0 ? searchValueLower.charAt(0) : null; - setLastPressedLetter(firstLetter); + const firstLetter = + searchValueLower.length > 0 ? searchValueLower.charAt(0) : null; + setLastPressedLetter(firstLetter); - const isInvalidSearch = searchValue.length > 0 && filtered.length === 0 && suggested.length === 0; + const isInvalidSearch = + searchValue.length > 0 && filtered.length === 0 && suggested.length === 0; - const errorMessage = isInvalidSearch - ? data === "countries" - ? 'Страны не существует, возможно ошибка' - : '' - : '' + const errorMessage = isInvalidSearch + ? data === 'countries' + ? 'Страны не существует, возможно ошибка' + : '' + : ''; - setIsError(isInvalidSearch); - setErrorMessage(errorMessage); + setIsError(isInvalidSearch); + setErrorMessage(errorMessage); - setSearchListVisible(filtered.length > 0 && newSearchValue.length > 0); - }; + setSearchListVisible(filtered.length > 0 && newSearchValue.length > 0); + }; - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return 0; - } else if (prevIndex < suggestedItems.length - 1) { - return prevIndex + 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return suggestedItems.length - 1; - } else if (prevIndex > 0) { - return prevIndex - 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'Enter') { - e.preventDefault(); - if (data === "countries" && selectedSuggestionIndex !== null) { - handleSelectItem(suggestedItems[selectedSuggestionIndex]); - } else if (data === "interests") { - handleSelectItem({name: e.target.value}); - console.log(e.target.value); - } else if (selectedItem) { - handleSelectItem(selectedItem); - } + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return 0; + } else if (prevIndex < suggestedItems.length - 1) { + return prevIndex + 1; + } else { + return prevIndex; } - }; - - const handleDropdownKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return 0; - } else if (prevIndex < suggestedItems.length - 1) { - return prevIndex + 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return suggestedItems.length - 1; - } else if (prevIndex > 0) { - return prevIndex - 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'Enter') { - e.preventDefault(); - if (data === "countries" && selectedSuggestionIndex !== null) { - handleSelectItem(suggestedItems[selectedSuggestionIndex]); - } else if (data === "interests") { - handleSelectItem({name: e.target.value}); - console.log(e.target.value); - } else if (selectedItem) { - handleSelectItem(selectedItem); - } + }); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return suggestedItems.length - 1; + } else if (prevIndex > 0) { + return prevIndex - 1; + } else { + return prevIndex; } - }; + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (data === 'countries' && selectedSuggestionIndex !== null) { + handleSelectItem(suggestedItems[selectedSuggestionIndex]); + } else if (data === 'interests') { + handleSelectItem({ name: e.target.value }); + console.log(e.target.value); + } else if (selectedItem) { + handleSelectItem(selectedItem); + } + } + }; - const sortItemsByLastLetter = () => { - if (lastPressedLetter) { - return filteredItems.sort((a, b) => { - const nameA = a.name.toLowerCase(); - const nameB = b.name.toLowerCase(); - if (nameA.startsWith(lastPressedLetter) && !nameB.startsWith(lastPressedLetter)) { - return -1; - } - if (!nameA.startsWith(lastPressedLetter) && nameB.startsWith(lastPressedLetter)) { - return 1; - } - return nameA.localeCompare(nameB); - }); + const handleDropdownKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return 0; + } else if (prevIndex < suggestedItems.length - 1) { + return prevIndex + 1; } else { - return filteredItems; + return prevIndex; } - }; + }); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return suggestedItems.length - 1; + } else if (prevIndex > 0) { + return prevIndex - 1; + } else { + return prevIndex; + } + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (data === 'countries' && selectedSuggestionIndex !== null) { + handleSelectItem(suggestedItems[selectedSuggestionIndex]); + } else if (data === 'interests') { + handleSelectItem({ name: e.target.value }); + console.log(e.target.value); + } else if (selectedItem) { + handleSelectItem(selectedItem); + } + } + }; - const handleSelectItem = (item) => { - if (selectedItems.length < i && !selectedItems.includes(item)) { - const updatedSelectedItems = [...selectedItems, item]; - setSelectedItems(updatedSelectedItems); - setSelectedItem(item); - setSearchListVisible(false); - setSearchValue(''); - // onSortCountry(country); + const sortItemsByLastLetter = () => { + if (lastPressedLetter) { + return filteredItems.sort((a, b) => { + const nameA = a.name.toLowerCase(); + const nameB = b.name.toLowerCase(); + if ( + nameA.startsWith(lastPressedLetter) && + !nameB.startsWith(lastPressedLetter) + ) { + return -1; + } + if ( + !nameA.startsWith(lastPressedLetter) && + nameB.startsWith(lastPressedLetter) + ) { + return 1; + } + return nameA.localeCompare(nameB); + }); + } else { + return filteredItems; + } + }; - onSelectedItemsChange(updatedSelectedItems) + const handleSelectItem = (item) => { + if (selectedItems.length < i && !selectedItems.includes(item)) { + const updatedSelectedItems = [...selectedItems, item]; + setSelectedItems(updatedSelectedItems); + setSelectedItem(item); + setSearchListVisible(false); + setSearchValue(''); + // onSortCountry(country); - } - }; + onSelectedItemsChange(updatedSelectedItems); + } + }; + + const handleSelectItemFromList = (itemName: string) => { + const selectedItem = dataList.find( + (item) => item.name.toLocaleLowerCase('ru') === itemName, + ); + if (selectedItem) { + handleSelectItem(selectedItem); + } + }; - const handleSelectItemFromList = (itemName: string) => { - const selectedItem = dataList.find(item => item.name.toLocaleLowerCase('ru') === itemName); - if (selectedItem) { - handleSelectItem(selectedItem); - } - }; - const handleRemoveItem = (item) => { - const updatedItems = selectedItems.filter((c) => c.name !== item.name); - setSelectedItems(updatedItems); - }; - return ( + return ( + <> + {data === 'interests' && ( + handleInputValue(event)} + onKeyDown={handleKeyDown} + autoComplete='off' + /> + )} + {data === 'countries' && ( <> - {data === "interests" && - handleInputValue(event)} - onKeyDown={handleKeyDown} - autoComplete="off" - /> - } - {data === "countries" && - <> - handleInputValue(event)} - onKeyDown={handleKeyDown} - autoComplete="off" - /> - {isError && ( - {errorMessage} - )} - - } -
- {(isSearchListVisible && -
0,} - )} - onKeyDown={handleDropdownKeyDown} - > - {sortItemsByLastLetter().map((item, index) => ( - item && item.name ? ( -
handleSelectItemFromList(item.name.toLocaleLowerCase('ru'))} - className={classNames(styles.items__itemsList_option, { - [styles.selected]: selectedItem?.code === item.code, - [styles.suggested]: suggestedItems.includes(item), - })} - > - {`${item.name} - {item.name} -
- ) : null - ))} -
- )} + handleInputValue(event)} + onKeyDown={handleKeyDown} + autoComplete='off' + /> + {isError && ( + {errorMessage} + )} + + )} +
+ {isSearchListVisible && ( +
0, + })} + onKeyDown={handleDropdownKeyDown} + > + {sortItemsByLastLetter().map((item, index) => + item && item.name ? (
- {selectedItems && - selectedItems.map((item, index) => ( - - ))} + key={index} + onClick={() => + handleSelectItemFromList(item.name.toLocaleLowerCase('ru')) + } + className={classNames(styles.items__itemsList_option, { + [styles.selected]: selectedItem?.code === item.code, + [styles.suggested]: suggestedItems.includes(item), + })} + > + {`${item.name} + {item.name}
-
- - ); + ) : null, + )} +
+ )} +
+ {selectedItems && + selectedItems.map((item, index) => ( + + ))} +
+
+ + ); }; export default observer(InputSearchList); diff --git a/src/components/UI/GenderAndAgeIcon/GenderAndAgeIcon.tsx b/src/components/UI/GenderAndAgeIcon/GenderAndAgeIcon.tsx index 520b529..b8b6225 100644 --- a/src/components/UI/GenderAndAgeIcon/GenderAndAgeIcon.tsx +++ b/src/components/UI/GenderAndAgeIcon/GenderAndAgeIcon.tsx @@ -1,13 +1,16 @@ import React, { FC } from 'react'; -import cn from 'classnames'; + import femaleGender from '../../../images/svg/card-gender-female.svg'; import maleGender from '../../../images/svg/card-gender-male.svg'; +import { GenderEnum, NullEnum } from '../../../utils/openapi'; + import styles from './GenderAndAgeIcon.module.scss'; +import cn from 'classnames'; interface GenderAndAgeIconProps { - gender: string | undefined; - age: string | undefined; + gender: GenderEnum | NullEnum | null; + age: string; gender_is_hidden?: boolean; age_is_hidden?: boolean; } diff --git a/src/components/UI/Input/Input.tsx b/src/components/UI/Input/Input.tsx index ea4a32b..80f3857 100644 --- a/src/components/UI/Input/Input.tsx +++ b/src/components/UI/Input/Input.tsx @@ -1,111 +1,120 @@ -import type {ChangeEvent, InputHTMLAttributes} from 'react'; - -import search from "../../../images/svg/search.svg"; +import type { ChangeEvent, InputHTMLAttributes } from 'react'; import styles from './Input.module.scss'; import cn from 'classnames'; export interface InputProps - extends InputHTMLAttributes { - className?: string; - onValue: ({value, name}: { value: string; name: T }) => void; - value: string; - name: T; - label?: string; - labelStyles?: string; - labelHint?: string; - isLabelHintHidden?: boolean; - type: string; - fontSize?: "14" | "16"; - placeholder: string; - hint?: string; - required: boolean; - hasError?: boolean; - error?: string; + extends InputHTMLAttributes { + className?: string; + onValue: ({ value, name }: { value: string; name: T }) => void; + value: string; + name: T; + label?: string; + labelStyles?: string; + labelHint?: string; + isLabelHintHidden?: boolean; + type: string; + fontSize?: '14' | '16'; + placeholder: string; + hint?: string; + required?: boolean; + hasError?: boolean; + error?: string; } export const Input = ({ - className, - onValue, - value, - name, - label, - labelStyles = "label16", - labelHint, - isLabelHintHidden, - type, - fontSize = "14", - placeholder, - hint, - required, - hasError, - error, - ...rest - }: InputProps) => { - const handleChange = (event: ChangeEvent) => { - const {value, name} = event.currentTarget; - onValue({value, name: name as T}); - console.log(event.currentTarget); - }; + className, + onValue, + value, + name, + label, + labelStyles = 'label16', + labelHint, + isLabelHintHidden, + type, + fontSize = '14', + placeholder, + hint, + required, + hasError, + error, + ...rest +}: InputProps) => { + const handleChange = (event: ChangeEvent) => { + const { value, name } = event.currentTarget; + onValue({ value, name: name as T }); + console.log(event.currentTarget); + }; - return label ? ( - - ) : ( -
- - {hasError ? ( - {error} - ) : ( - hint && {hint} - )} -
- ); + return label ? ( + + ) : ( +
+ + {hasError ? ( + {error} + ) : ( + hint && {hint} + )} +
+ ); }; diff --git a/src/components/UI/selectedItems/selectedItems.module.scss b/src/components/UI/selectedItems/selectedItems.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/components/UI/selectedItems/selectedItems.tsx b/src/components/UI/selectedItems/selectedItems.tsx new file mode 100644 index 0000000..9cfc7fa --- /dev/null +++ b/src/components/UI/selectedItems/selectedItems.tsx @@ -0,0 +1,53 @@ +import styles from '../../InputSearchList/InputSearchList.module.scss'; +import { Country, Interest } from '../../../utils/openapi'; +import cn from 'classnames'; + +type Items = Interest[] | Country[]; +type Item = Interest | Country; + +interface SelectedItemsProps { + data: string; + selectedItems: Items; + setSelectedItems: (selectedItems: Items) => void; +} + +const selectedItems = ({ + data, + selectedItems, + setSelectedItems, +}: SelectedItemsProps) => { + const handleRemoveItem = (item: Item) => { + const itemsArray: Items = []; + const itemsArrayForFilter: Array = itemsArray + + + const updatedItems: Interest = Items.filter( + (c: Interest) => c.name !== item.name, + ); + } + setSelectedItems(updatedItems); + }; + + return ( + <> + {selectedItems && + selectedItems.map((item: Item, index: number) => ( + + ))} + + ); +}; + +export default selectedItems; diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 926cb04..a858b07 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -1,157 +1,166 @@ -import React, {useEffect, useState} from 'react'; -import {observer} from "mobx-react-lite"; +import React, { useEffect, useState } from 'react'; +import { observer } from 'mobx-react-lite'; -import {api} from '../../utils/constants'; -import {Country} from '../../utils/openapi'; -import {Language} from '../../utils/openapi'; +import { api } from '../../utils/constants'; +import { Country } from '../../utils/openapi'; +import { Language } from '../../utils/openapi'; import Card from '../../components/Card/Card'; import Header from '../../components/Header/Header'; -import Categories from "../../components/Categories/Categories"; -import Sort from "../../components/Sort/Sort"; +import Categories from '../../components/Categories/Categories'; +import Sort from '../../components/Sort/Sort'; import Footer from '../../components/Footer/Footer'; -import MoreCards from "../../components/MoreCards/MoreCards"; +import MoreCards from '../../components/MoreCards/MoreCards'; import styles from './MainPage.module.scss'; -import cn from "classnames"; +import cn from 'classnames'; import { loggedIn } from '../../models/LoggedIn'; const MainPage = () => { - useEffect(() => { - console.log(`main_loggedIn: ${loggedIn.loggedIn}`); - }, []); - - const [usersList, setUsersList] = useState([]); - const [cardsListLength, setCardsListLength] = useState(0); - const [isUsersList, setIsUsersList] = useState(false); - const [category, setCategory] = useState({name: 'Все', path: ''}); - const [sortType, setSortType] = useState({}); - const [isSortPopupOpen, setSortPopupOpen] = useState(false); - const [languagesData, setLanguagesData] = useState([]); - const [countriesData, setCountriesData] = useState([]); - - const handleOpenSortPopup = () => { - setSortPopupOpen(!isSortPopupOpen); + useEffect(() => { + console.log(`main_loggedIn: ${loggedIn.loggedIn}`); + }, []); + + const [usersList, setUsersList] = useState([]); + const [cardsListLength, setCardsListLength] = useState(0); + const [isUsersList, setIsUsersList] = useState(false); + const [category, setCategory] = useState({ name: 'Все', path: '' }); + const [sortType, setSortType] = useState({}); + const [isSortPopupOpen, setSortPopupOpen] = useState(false); + const [languagesData, setLanguagesData] = useState([]); + const [countriesData, setCountriesData] = useState([]); + + const handleOpenSortPopup = () => { + setSortPopupOpen(!isSortPopupOpen); + }; + + const getUsersList = async (filters: any) => { + try { + console.log('отправка запроса ---'); + const response = await api.api.usersList({ + ordering: `${category.path}`, + ...filters, + }); + console.log('ответ получен -', response); + setIsUsersList(true); + + if (response.data && response.data.results) { + setUsersList(response.data.results); + console.log(response.data.results); + } + } catch (error) { + console.error('Ошибка при получении данных -', error); + setIsUsersList(false); } - - const getUsersList = async (filters:any) => { - try { - console.log('отправка запроса ---'); - const response = await api.api.usersList({ - ordering: `${category.path}`, - ...filters, - }); - console.log('ответ получен -', response); - setIsUsersList(true); - - if (response.data && response.data.results) { - setUsersList(response.data.results); - console.log(response.data.results); - } - } catch (error) { - console.error('Ошибка при получении данных -', error); - setIsUsersList(false); - } - }; - - useEffect(() => { - getUsersList(sortType); - }, [category, sortType]); - - //Запрос массива языков - const fetchLanguagesData = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.languagesList(); - console.log('ответ получен -', response); - const languages = response.data; - setLanguagesData(languages); - } catch (error) { - console.error("Ошибка при получении данных о языках:", error); - } - }; - - useEffect(() => { - fetchLanguagesData(); - }, []); - - //Запрос страны - const fetchCountriesData = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.countriesList(); - console.log('ответ получен -', response); - const countries = response.data.map((country) => ({ - code: country.code, - name: country.name, - flag_icon: country.flag_icon, - })); - setCountriesData(countries); - } catch (error) { - console.error("Ошибка при получении данных о странах:", error); - } - }; - - useEffect(() => { - fetchCountriesData(); - }, []); - - return ( - <> -
-
-

Поиск партнера

-
- -
-

Фильтр

- -
-
-
-
-
- {isUsersList && - usersList.map((user, i) => { - return ((i < cardsListLength) && - - ); - } - )} -
- -
- {/**/} -
-
-
- - ); + }; + + useEffect(() => { + getUsersList(sortType); + }, [category, sortType]); + + //Запрос массива языков + const fetchLanguagesData = async () => { + try { + console.log('отправка запроса ---'); + const response = await api.api.languagesList(); + console.log('ответ получен -', response); + const languages = response.data; + setLanguagesData(languages); + } catch (error) { + console.error('Ошибка при получении данных о языках:', error); + } + }; + + useEffect(() => { + fetchLanguagesData(); + }, []); + + //Запрос страны + const fetchCountriesData = async () => { + try { + console.log('отправка запроса ---'); + const response = await api.api.countriesList(); + console.log('ответ получен -', response); + const countries = response.data.map((country) => ({ + code: country.code, + name: country.name, + flag_icon: country.flag_icon, + })); + setCountriesData(countries); + } catch (error) { + console.error('Ошибка при получении данных о странах:', error); + } + }; + + useEffect(() => { + fetchCountriesData(); + }, []); + + return ( + <> +
+
+

Поиск партнера

+
+ +
+

Фильтр

+ +
+
+
+
+
+ {isUsersList && + usersList.map((user, i) => { + return ( + i < cardsListLength && ( + + ) + ); + })} +
+ +
+ {/**/} +
+
+
+ + ); }; export default observer(MainPage); diff --git a/src/utils/rest/auth.ts b/src/utils/rest/auth.ts index 7fcf6ba..e0d7d4b 100644 --- a/src/utils/rest/auth.ts +++ b/src/utils/rest/auth.ts @@ -1,85 +1,81 @@ -import {loggedIn} from "../../models/LoggedIn"; +import { loggedIn } from '../../models/LoggedIn'; import { - TokenObtainPairRequest, - TokenObtainPair, - UserRepr, - Country, - GenderEnum, - Goal, - NullEnum, - UserLanguage, -} from "../openapi"; + TokenObtainPairRequest, + TokenObtainPair, + UserRepr, + Country, + GenderEnum, + Goal, + NullEnum, + UserLanguage, +} from '../openapi'; -import {api, headersWithToken as headers} from "../constants"; +import { api, headersWithToken as headers } from '../constants'; -export const signInWithEmail = async ({username, password}: TokenObtainPairRequest): Promise => { - const { - data: {token}, - error - } = await api.api.authJwtCreateCreate({ - username: username, - password: password - }); +export const signInWithEmail = async ({ + username, + password, +}: TokenObtainPairRequest): Promise => { + const { data: token, error } = await api.api.authJwtCreateCreate({ + username: username, + password: password, + }); - if (error) { - throw error; - } + if (error) { + throw error; + } - if (token) { - localStorage.setItem('accessToken', token.access); - localStorage.setItem('refreshToken', token.refresh); - return { - access: token.access as string, - refresh: token.refresh as string, - }; - } + if (token) { + localStorage.setItem('accessToken', token.access); + localStorage.setItem('refreshToken', token.refresh); + return { + access: token.access as string, + refresh: token.refresh as string, + }; + } - return null; + return null; }; export const getMe = async (): Promise => { - const { - data: {user}, - error - } = api.api.usersMeRetrieve({headers}); + const { data: user, error } = api.api.usersMeRetrieve({ headers }); - if (error) { - throw error; - } + if (error) { + throw error; + } - if (!user) { - return null; - } + if (!user) { + return null; + } - if (user) { - loggedIn.setLoggedInTrue(); - console.log(loggedIn.loggedIn); + if (user) { + loggedIn.setLoggedInTrue(); + console.log(loggedIn.loggedIn); - return { - username: user.username as string, - first_name: user.first_name as string, - avatar: user.avatar as string, - age: user.age as string, - slug: user.slug as string | null, - country: user.country as Country, - languages: user.languages as UserLanguage[], - gender: user.gender as GenderEnum | NullEnum | null, - goals: user.goals as Goal[], - interests: user.interests as string[], - about: user.about as string, - last_activity: user.last_activity as string | null, - is_online: user.is_online as string, - gender_is_hidden: user.gender_is_hidden as boolean, - age_is_hidden: user.age_is_hidden as boolean, - role: user.role as string - }; - } + return { + username: user.username as string, + first_name: user.first_name as string, + avatar: user.avatar as string, + age: user.age as string, + slug: user.slug as string | null, + country: user.country as Country, + languages: user.languages as UserLanguage[], + gender: user.gender as GenderEnum | NullEnum | null, + goals: user.goals as Goal[], + interests: user.interests as string[], + about: user.about as string, + last_activity: user.last_activity as string | null, + is_online: user.is_online as string, + gender_is_hidden: user.gender_is_hidden as boolean, + age_is_hidden: user.age_is_hidden as boolean, + role: user.role as string, + }; + } - return null; + return null; }; - // export const signOut = async (): Promise => { // const {error} = await api.api.signOut(); // @@ -88,4 +84,3 @@ export const getMe = async (): Promise => { // } // // }; - From 35453f31f8908261878aca3006d7bf2eb93f5c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Mon, 28 Aug 2023 17:35:58 +0300 Subject: [PATCH 114/153] fix --- src/pages/MainPage/MainPage.tsx | 180 +++++++++++++++++--------------- 1 file changed, 98 insertions(+), 82 deletions(-) diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 6a28a48..4a167a1 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -1,25 +1,33 @@ -import React, {useEffect, useState} from 'react'; -import {observer} from "mobx-react-lite"; +import React, { useEffect, useState } from 'react'; +import { observer } from 'mobx-react-lite'; import { api } from '../../utils/constants'; import Card from '../../components/Card/Card'; import Header from '../../components/Header/Header'; -import Categories from "../../components/Categories/Categories"; -import Sort from "../../components/Sort/Sort"; +import Categories from '../../components/Categories/Categories'; +import Sort from '../../components/Sort/Sort'; import Footer from '../../components/Footer/Footer'; -import MoreCards from "../../components/MoreCards/MoreCards"; +import MoreCards from '../../components/MoreCards/MoreCards'; import styles from './MainPage.module.scss'; -import cn from "classnames"; +import cn from 'classnames'; import { loggedIn } from '../../models/LoggedIn'; +import Modal from '../../components/Modal/Modal'; +import { useModel } from '../../components/SignupSigninForm/model'; const MainPage = () => { - useEffect(() => { - console.log(`main_loggedIn: ${loggedIn.loggedIn}`); - }, []); + const model = useModel(); + + const handleCloseModal = () => { + model.isModalOpen = false; + }; + + useEffect(() => { + console.log(`main_loggedIn: ${loggedIn.loggedIn}`); + }, []); const [usersList, setUsersList] = useState([]); const [cardsListLength, setCardsListLength] = useState(0); @@ -27,19 +35,17 @@ const MainPage = () => { const [category, setCategory] = useState({ name: 'Все', path: '' }); const [filters, setFilters] = useState({}); const [isSortPopupOpen, setSortPopupOpen] = useState(false); - - const handleOpenSortPopup = () => { - setSortPopupOpen(!isSortPopupOpen); - } + const handleOpenSortPopup = () => { + setSortPopupOpen(!isSortPopupOpen); + }; const getUsersList = async (filters: any) => { try { console.log('отправка запроса ---'); const languageFilters = filters.languages.map((languageFilter: any) => { return `${languageFilter.language},${languageFilter.skill_level}`; - }); - + }); const response = await api.api.usersList({ ordering: `${category.path}`, age: filters.age, @@ -50,78 +56,88 @@ const MainPage = () => { console.log('ответ получен -', response); setIsUsersList(true); - if (response.data && response.data.results) { - setUsersList(response.data.results); - console.log(response.data.results); - } - } catch (error) { - console.error('Ошибка при получении данных -', error); - setIsUsersList(false); - } - }; + if (response.data && response.data.results) { + setUsersList(response.data.results); + console.log(response.data.results); + } + } catch (error) { + console.error('Ошибка при получении данных -', error); + setIsUsersList(false); + } + }; useEffect(() => { getUsersList(filters); }, [category, filters]); - return ( - <> -
-
-

Поиск партнера

-
- -
-

Фильтр

- -
-
-
-
-
- {isUsersList && - usersList.map((user, i) => { - return ((i < cardsListLength) && - - ); - } - )} -
- -
- -
- - -

Подтвердите адрес электронной почты

-

Пожалуйста, проверьте электронную почту, которую - указали - при регистрации, и перейдите по ссылке для подтверждения

-

Ссылка будет активна в течении 24 часов

-
-
-
- - ); + return ( + <> +
+
+

Поиск партнера

+
+ +
+

Фильтр

+ +
+
+
+
+
+ {isUsersList && + usersList.map((user, i) => { + return ( + i < cardsListLength && ( + + ) + ); + })} +
+ +
+ +
+ +

+ Подтвердите адрес электронной почты +

+

+ Пожалуйста, проверьте электронную почту, которую указали при + регистрации, и перейдите по ссылке для подтверждения +

+

+ Ссылка будет активна в течении 24 часов +

+
+
+
+ + ); }; export default observer(MainPage); From 70a8d4f7fda9775320102decfc7f1679ed3d81ea Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 28 Aug 2023 17:39:18 +0300 Subject: [PATCH 115/153] fix: prettier --- package-lock.json | 14 + package.json | 1 + .../SignupSigninForm/SignupSigninForm.tsx | 322 +++++++++--------- src/components/SignupSigninForm/model.ts | 164 ++++----- src/utils/rest/auth.ts | 10 +- src/utils/rest/getCountries.ts | 32 +- src/utils/rest/getInterests.ts | 49 ++- src/utils/rest/getLanguages.ts | 34 +- src/utils/rest/register.ts | 26 +- 9 files changed, 352 insertions(+), 300 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f8cb5d..a98bdbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "classnames": "^2.3.2", + "compose-function": "^3.0.3", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -5425,6 +5426,11 @@ "deep-equal": "^2.0.5" } }, + "node_modules/arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -6505,6 +6511,14 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "node_modules/compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha512-xzhzTJ5eC+gmIzvZq+C3kCJHsp9os6tJkrigDRZclyGtOKINbZtE8n1Tzmeh32jW+BUDPbvZpibwvJHBLGMVwg==", + "dependencies": { + "arity-n": "^1.0.4" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", diff --git a/package.json b/package.json index eaf7ce3..74b4a73 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "private": true, "dependencies": { "classnames": "^2.3.2", + "compose-function": "^3.0.3", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/components/SignupSigninForm/SignupSigninForm.tsx b/src/components/SignupSigninForm/SignupSigninForm.tsx index 1db9312..4d92af0 100644 --- a/src/components/SignupSigninForm/SignupSigninForm.tsx +++ b/src/components/SignupSigninForm/SignupSigninForm.tsx @@ -1,171 +1,187 @@ -import {useEffect, useState} from "react"; -import {useLocation, Link} from "react-router-dom"; -import {observer} from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { useLocation, Link, useNavigate } from 'react-router-dom'; +import { observer } from 'mobx-react-lite'; -import {Input} from '../UI/Input/Input'; -import {Button} from '../UI/Button/Button'; -import {loggedIn} from '../../models/LoggedIn'; +import { Input } from '../UI/Input/Input'; +import { Button } from '../UI/Button/Button'; +import { loggedIn } from '../../models/LoggedIn'; -import {useModel} from './model'; +import { useModel } from './model'; import styles from './SignupSigninForm.module.scss'; -import cn from "classnames"; +import cn from 'classnames'; const SignupSigninForm = () => { - const model = useModel(); + const model = useModel(); - const location = useLocation(); - const pathName: string = location.pathname; + const location = useLocation(); + const pathName: string = location.pathname; - const[isSignUp, setIsSignUp] = useState(false); + const [isSignUp, setIsSignUp] = useState(false); - const checkIsSignUp = () => { - pathName === "/signup" - ? setIsSignUp(true) - : setIsSignUp(false) - }; + const checkIsSignUp = () => { + pathName === '/signup' ? setIsSignUp(true) : setIsSignUp(false); + }; - useEffect(() => { - checkIsSignUp(); - }, [pathName]); + useEffect(() => { + checkIsSignUp(); + }, [pathName]); - useEffect(() => { - model.getCurrentUser(); - }, [loggedIn.loggedIn]); + useEffect(() => { + model.getCurrentUser(); + }, [loggedIn.loggedIn]); - useEffect(() => { - console.log('loginForm') - console.log(loggedIn.loggedIn) - }, [loggedIn.loggedIn]); + useEffect(() => { + console.log('loginForm'); + console.log(loggedIn.loggedIn); + }, [loggedIn.loggedIn]); - return ( - -
    - - Вход - - - Регистрация - -
- {isSignUp && ( - - )} - +
    + + Вход + + + Регистрация + +
+ {isSignUp && ( + + )} + + + {isSignUp && ( + + )} +
+ {isSignUp && ( + + )} + {!isSignUp && ( + + )} + {!isSignUp && ( + + Не помню пароль + + )} +
+ + + ); }; export default observer(SignupSigninForm); diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 078d50e..2db1a60 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -1,84 +1,88 @@ -import {FormEvent} from "react"; -import {useLocation, useNavigate} from "react-router-dom"; -import {useLocalObservable} from "mobx-react-lite"; +import { FormEvent } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useLocalObservable } from 'mobx-react-lite'; -import {getMe, signInWithEmail} from "../../utils/rest/auth"; -import {signUp} from "../../utils/rest/register"; +import { getMe, signInWithEmail } from '../../utils/rest/auth'; +import { signUp } from '../../utils/rest/register'; export const useModel = () => { - const model = useLocalObservable(() => { - const navigate = useNavigate(); - - const location = useLocation(); - const pathName: string = location.pathname; - - return { - username: "", - email: "", - password: "", - confirmPassword: "", - isLoading: false, - error: "", - message: "", - refresh: "", - access: "", - isSignUp: false, - - handleValue({name, value}: { name: "username" | "email" | "password" | "confirmPassword"; value: string }) { - model[name] = value; - }, - - get toMain(): string { - return "/"; - }, - - get toFillOut(): string { - return "fill-out-1"; - }, - - async handleRegister(event: FormEvent) { - event.preventDefault(); - - model.error = ""; - model.message = ""; - model.isLoading = true; - - await signUp({ - email: model.email, - username: model.username, - password: model.password, - }); - - // navigate(model.toFillOut); - model.isLoading = false; - }, - - async handleLogin(event: FormEvent) { - event.preventDefault(); - - model.error = ""; - model.message = ""; - model.isLoading = true; - - await signInWithEmail({username: model.email, password: model.password}); - - model.isLoading = false; - }, - - async getCurrentUser() { - model.error = ""; - model.message = ""; - model.isLoading = true; - - await getMe(); - - // navigate(model.toMain); - model.isLoading = false; - }, - - }; - }); - - return model; + const navigate = useNavigate(); + + const model = useLocalObservable(() => { + return { + username: '', + email: '', + password: '', + confirmPassword: '', + isLoading: false, + error: '', + message: '', + refresh: '', + access: '', + isSignUp: false, + + handleValue({ + name, + value, + }: { + name: 'username' | 'email' | 'password' | 'confirmPassword'; + value: string; + }) { + model[name] = value; + }, + + get toMain(): string { + return '/'; + }, + + get toFillOut(): string { + return 'fill-out-1'; + }, + + async handleRegister(event: FormEvent) { + event.preventDefault(); + + model.error = ''; + model.message = ''; + model.isLoading = true; + + await signUp({ + email: model.email, + username: model.username, + password: model.password, + }); + + navigate(model.toFillOut); + model.isLoading = false; + }, + + async handleLogin(event: FormEvent) { + event.preventDefault(); + + model.error = ''; + model.message = ''; + model.isLoading = true; + + await signInWithEmail({ + username: model.email, + password: model.password, + }); + + model.isLoading = false; + }, + + async getCurrentUser() { + model.error = ''; + model.message = ''; + model.isLoading = true; + + await getMe(); + + navigate(model.toMain); + model.isLoading = false; + }, + }; + }); + + return model; }; - diff --git a/src/utils/rest/auth.ts b/src/utils/rest/auth.ts index e0d7d4b..3df4bc5 100644 --- a/src/utils/rest/auth.ts +++ b/src/utils/rest/auth.ts @@ -39,7 +39,7 @@ export const signInWithEmail = async ({ }; export const getMe = async (): Promise => { - const { data: user, error } = api.api.usersMeRetrieve({ headers }); + const { data: user, error } = await api.api.usersMeRetrieve({ headers }); if (error) { throw error; @@ -77,10 +77,16 @@ export const getMe = async (): Promise => { }; // export const signOut = async (): Promise => { -// const {error} = await api.api.signOut(); +// const {data: res, error} = await api.api.signOut(); // // if (error) { // throw error; // } + +// if (res) { +// localStorage.clear(); +// loggedIn.setLoggedInFalse(); +// } + // // }; diff --git a/src/utils/rest/getCountries.ts b/src/utils/rest/getCountries.ts index 80e8b9c..dbf23ef 100644 --- a/src/utils/rest/getCountries.ts +++ b/src/utils/rest/getCountries.ts @@ -1,24 +1,20 @@ -import {Country} from "../openapi"; -import {api} from "../constants"; +import { Country } from '../openapi'; +import { api } from '../constants'; export const getCountries = async (): Promise => { + const { data: countries, error } = await api.api.countriesList(); - const { - data: {countries}, - error - } = await api.api.countriesList(); + if (error) { + throw error; + } - if (error) { - throw error; - } + if (countries) { + return countries.map((country) => ({ + code: country.code as string | null, + name: country.name as string, + flag_icon: country.flag_icon as string, + })); + } - if (countries) { - return countries.map((country) => ({ - code: country.code as string | null, - name: country.name as string, - flag_icon: country.flag_icon as string, - })); - } - - return []; + return []; }; diff --git a/src/utils/rest/getInterests.ts b/src/utils/rest/getInterests.ts index 025368f..8c820f1 100644 --- a/src/utils/rest/getInterests.ts +++ b/src/utils/rest/getInterests.ts @@ -1,23 +1,38 @@ -import {Interest} from "../openapi"; -import {api} from "../constants"; +import { Interest, PaginatedInterestList } from '../openapi'; +import { api } from '../constants'; -export const getInterests = async (): Promise => { +export const getInterests = async (): Promise => { + const { data: interests, error } = await api.api.interestsList(); - const { - data: {interests}, - error - } = await api.api.interestsList(); + if (error) { + throw error; + } - if (error) { - throw error; - } + const interestList: Interest[] | undefined = interests.results; - if (interests) { - return interests.map((interest) => ({ - name: interest.name as string, - sorting: interest.sorting as string, - })); - } + if (interestList) { + } - return []; + return interestList.map((interest) => ({ + name: interest.name as string, + sorting: interest.sorting as string, + })); +}; + +export const getInterests = async (): Promise => { + const { data: interests, error } = await api.api.interestsList(); + + if (error) { + throw error; + } + + const interestList: Interest[] | undefined = interests.results; + + if (interestList) { + } + + return interestList.map((interest) => ({ + name: interest.name as string, + sorting: interest.sorting as string, + })); }; diff --git a/src/utils/rest/getLanguages.ts b/src/utils/rest/getLanguages.ts index f58b73a..16c568e 100644 --- a/src/utils/rest/getLanguages.ts +++ b/src/utils/rest/getLanguages.ts @@ -1,25 +1,21 @@ -import {Language} from "../openapi"; -import {api} from "../constants"; +import { Language } from '../openapi'; +import { api } from '../constants'; export const getLanguages = async (): Promise => { + const { data: languages, error } = await api.api.languagesList(); - const { - data: {languages}, - error - } = await api.api.languagesList(); + if (error) { + throw error; + } - if (error) { - throw error; - } + if (languages) { + return languages.map((language) => ({ + name: language.name as string, + name_local: language.name_local as string, + isocode: language.isocode as string, + sorting: language.sorting as number, + })); + } - if (languages) { - return languages.map((language) => ({ - name: language.name as string, - name_local: language.name_local as string, - isocode: language.isocode as string, - sorting: language.sorting as number, - })); - } - - return []; + return []; }; diff --git a/src/utils/rest/register.ts b/src/utils/rest/register.ts index cbe6c29..8bf8b2c 100644 --- a/src/utils/rest/register.ts +++ b/src/utils/rest/register.ts @@ -1,14 +1,18 @@ -import {UserCreateRequest} from "../openapi"; -import {api} from "../constants"; +import { UserCreateRequest } from '../openapi'; +import { api } from '../constants'; -export const signUp = async ({email, username, password}: UserCreateRequest): Promise => { - const {error} = await api.api.usersCreate({ - email: email, - username: username, - password: password - }); +export const signUp = async ({ + email, + username, + password, +}: UserCreateRequest): Promise => { + const { error } = await api.api.usersCreate({ + email: email, + username: username, + password: password, + }); - if (error) { - throw error; - } + if (error) { + throw error; + } }; From 6de5f2e8cc85a89f40bda68387d51fae46c217b3 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 28 Aug 2023 18:21:14 +0300 Subject: [PATCH 116/153] fix: getInterests --- src/utils/rest/getInterests.ts | 37 ++++++++++------------------------ 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/utils/rest/getInterests.ts b/src/utils/rest/getInterests.ts index 8c820f1..de8797c 100644 --- a/src/utils/rest/getInterests.ts +++ b/src/utils/rest/getInterests.ts @@ -1,38 +1,23 @@ -import { Interest, PaginatedInterestList } from '../openapi'; +import { Interest } from '../openapi'; import { api } from '../constants'; -export const getInterests = async (): Promise => { +export const getInterests = async (): Promise => { const { data: interests, error } = await api.api.interestsList(); if (error) { throw error; } - const interestList: Interest[] | undefined = interests.results; + if (interests) { + const interestList = interests.results; - if (interestList) { + if (interestList) { + return interestList.map((result) => ({ + name: result.name as string, + sorting: result.sorting as string, + })); + } } - return interestList.map((interest) => ({ - name: interest.name as string, - sorting: interest.sorting as string, - })); -}; - -export const getInterests = async (): Promise => { - const { data: interests, error } = await api.api.interestsList(); - - if (error) { - throw error; - } - - const interestList: Interest[] | undefined = interests.results; - - if (interestList) { - } - - return interestList.map((interest) => ({ - name: interest.name as string, - sorting: interest.sorting as string, - })); + return []; }; From c33c608798c30c440fd776edbefde0fc8c162d38 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 28 Aug 2023 19:53:17 +0300 Subject: [PATCH 117/153] fix: prettier --- package.json | 2 +- src/components/Card/Card.tsx | 4 - src/components/Categories/Categories.tsx | 2 +- .../SignupSigninForm/SignupSigninForm.tsx | 2 +- .../UI/selectedItems/selectedItems.tsx | 11 ++- src/pages/MainPage/MainPage.tsx | 47 +---------- src/pages/Routing.tsx | 77 +++++++++++-------- .../SignupSigninPage/SignupSigninPage.tsx | 11 +-- src/types/types.ts | 2 - 9 files changed, 59 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index 74b4a73..e85d64d 100644 --- a/package.json +++ b/package.json @@ -61,4 +61,4 @@ "typescript": "^4.0.0", "typescript-plugin-css-modules": "^5.0.1" } - } +} diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 810eab3..7824112 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -21,14 +21,10 @@ interface ICards { first_name: string; avatar: string; age: string; - country: Country; languages: UserLanguage[]; - gender: GenderEnum | NullEnum | null; - about: string; - is_online: boolean; gender_is_hidden: boolean; age_is_hidden: boolean; diff --git a/src/components/Categories/Categories.tsx b/src/components/Categories/Categories.tsx index 5ecf066..337df2a 100644 --- a/src/components/Categories/Categories.tsx +++ b/src/components/Categories/Categories.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect, useState } from 'react'; +import React, { memo, useEffect } from 'react'; import styles from '../Categories/Categories.module.scss'; type Category = { diff --git a/src/components/SignupSigninForm/SignupSigninForm.tsx b/src/components/SignupSigninForm/SignupSigninForm.tsx index 4d92af0..9b19563 100644 --- a/src/components/SignupSigninForm/SignupSigninForm.tsx +++ b/src/components/SignupSigninForm/SignupSigninForm.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { useLocation, Link, useNavigate } from 'react-router-dom'; +import { useLocation, Link } from 'react-router-dom'; import { observer } from 'mobx-react-lite'; import { Input } from '../UI/Input/Input'; diff --git a/src/components/UI/selectedItems/selectedItems.tsx b/src/components/UI/selectedItems/selectedItems.tsx index 9cfc7fa..17dc545 100644 --- a/src/components/UI/selectedItems/selectedItems.tsx +++ b/src/components/UI/selectedItems/selectedItems.tsx @@ -17,14 +17,13 @@ const selectedItems = ({ setSelectedItems, }: SelectedItemsProps) => { const handleRemoveItem = (item: Item) => { - const itemsArray: Items = []; - const itemsArrayForFilter: Array = itemsArray + const itemsArray: Items = []; + const interests: Interest[] = itemsArray; + const updatedItems: Interest = Items.filter( + (c: Interest) => c.name !== item.name, + ); - const updatedItems: Interest = Items.filter( - (c: Interest) => c.name !== item.name, - ); - } setSelectedItems(updatedItems); }; diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index a858b07..c02d1dc 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -28,9 +28,8 @@ const MainPage = () => { const [isUsersList, setIsUsersList] = useState(false); const [category, setCategory] = useState({ name: 'Все', path: '' }); const [sortType, setSortType] = useState({}); + const [filters, setFilters] = useState({}); const [isSortPopupOpen, setSortPopupOpen] = useState(false); - const [languagesData, setLanguagesData] = useState([]); - const [countriesData, setCountriesData] = useState([]); const handleOpenSortPopup = () => { setSortPopupOpen(!isSortPopupOpen); @@ -61,42 +60,6 @@ const MainPage = () => { }, [category, sortType]); //Запрос массива языков - const fetchLanguagesData = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.languagesList(); - console.log('ответ получен -', response); - const languages = response.data; - setLanguagesData(languages); - } catch (error) { - console.error('Ошибка при получении данных о языках:', error); - } - }; - - useEffect(() => { - fetchLanguagesData(); - }, []); - - //Запрос страны - const fetchCountriesData = async () => { - try { - console.log('отправка запроса ---'); - const response = await api.api.countriesList(); - console.log('ответ получен -', response); - const countries = response.data.map((country) => ({ - code: country.code, - name: country.name, - flag_icon: country.flag_icon, - })); - setCountriesData(countries); - } catch (error) { - console.error('Ошибка при получении данных о странах:', error); - } - }; - - useEffect(() => { - fetchCountriesData(); - }, []); return ( <> @@ -149,13 +112,7 @@ const MainPage = () => { setCardsListLength={setCardsListLength} /> - {/**/} +
diff --git a/src/pages/Routing.tsx b/src/pages/Routing.tsx index 585e8f7..5110b53 100644 --- a/src/pages/Routing.tsx +++ b/src/pages/Routing.tsx @@ -1,7 +1,5 @@ -import {lazy} from 'react'; -import {Route, Routes} from 'react-router-dom'; - -// import ProtectedRoute from '../components/ProtectedRoute/ProtectedRoute'; +import { lazy } from 'react'; +import { Route, Routes } from 'react-router-dom'; const SignInPage = lazy(() => import('./SignupSigninPage/SignupSigninPage')); const SignUpPage = lazy(() => import('./SignupSigninPage/SignupSigninPage')); @@ -13,36 +11,47 @@ const PolicyPage = lazy(() => import('./PolicyPage/PolicyPage')); const RulesPage = lazy(() => import('./RulesPage/RulesPage')); const AgreementPage = lazy(() => import('./AgreementPage/AgreementPage')); const ProfilePage = lazy(() => import('./UserProfile/UserProfile')); -const FillOutProfilePage1 = lazy(() => import('./FillOutProfilePages/FillOutProfilePage1')); -const FillOutProfilePage2 = lazy(() => import('./FillOutProfilePages/FillOutProfilePage2')); -const FillOutProfilePage3 = lazy(() => import('./FillOutProfilePages/FillOutProfilePage3')); -const FillOutProfilePage4 = lazy(() => import('./FillOutProfilePages/FillOutProfilePage4')); -const FillOutProfilePage5 = lazy(() => import('./FillOutProfilePages/FillOutProfilePage5')); -const FillOutProfilePage6 = lazy(() => import('./FillOutProfilePages/FillOutProfilePage6')); +const FillOutProfilePage1 = lazy( + () => import('./FillOutProfilePages/FillOutProfilePage1'), +); +const FillOutProfilePage2 = lazy( + () => import('./FillOutProfilePages/FillOutProfilePage2'), +); +const FillOutProfilePage3 = lazy( + () => import('./FillOutProfilePages/FillOutProfilePage3'), +); +const FillOutProfilePage4 = lazy( + () => import('./FillOutProfilePages/FillOutProfilePage4'), +); +const FillOutProfilePage5 = lazy( + () => import('./FillOutProfilePages/FillOutProfilePage5'), +); +const FillOutProfilePage6 = lazy( + () => import('./FillOutProfilePages/FillOutProfilePage6'), +); export const Routing = () => { - - return ( - - {/**/} - {/* */} - {/**/} - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - }/> - - ); + return ( + + {/**/} + {/* */} + {/**/} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); }; diff --git a/src/pages/SignupSigninPage/SignupSigninPage.tsx b/src/pages/SignupSigninPage/SignupSigninPage.tsx index 87eb063..94e08ad 100644 --- a/src/pages/SignupSigninPage/SignupSigninPage.tsx +++ b/src/pages/SignupSigninPage/SignupSigninPage.tsx @@ -1,11 +1,12 @@ -import SignupSigninForm from '../../components/SignupSigninForm/SignupSigninForm'; +import { Link } from 'react-router-dom'; +import { observer } from 'mobx-react-lite'; -import styles from './SignupSigninPage.module.scss'; +import SignupSigninForm from '../../components/SignupSigninForm/SignupSigninForm'; +import PicturesBlock from '../../components/PicturesBlock/PicturesBlock'; import logo from '../../images/svg/logo.svg'; -import PicturesBlock from '../../components/PicturesBlock/PicturesBlock'; -import { Link } from 'react-router-dom'; -import {observer} from "mobx-react-lite"; + +import styles from './SignupSigninPage.module.scss'; const SignupSigninPage = () => { return ( diff --git a/src/types/types.ts b/src/types/types.ts index aa397af..a874e05 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -3,5 +3,3 @@ export default interface IQuestion { text: string[]; id: number; } - - From 6c1b0f6d4f475cf3c867d753df7abaedbfaec7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Mon, 28 Aug 2023 19:57:01 +0300 Subject: [PATCH 118/153] fix update1 --- .../LanguageLevel/LanguageLevel.tsx | 138 +++++++++--------- .../LanguageLevelModal/LanguageLevelModal.tsx | 42 +----- 2 files changed, 73 insertions(+), 107 deletions(-) diff --git a/src/components/LanguageLevel/LanguageLevel.tsx b/src/components/LanguageLevel/LanguageLevel.tsx index 91baa31..47b6910 100644 --- a/src/components/LanguageLevel/LanguageLevel.tsx +++ b/src/components/LanguageLevel/LanguageLevel.tsx @@ -3,12 +3,12 @@ import { Language, SkillLevelEnum } from '../../utils/openapi'; import styles from './LanguageLevel.module.scss'; const skillLevelNames: Record = { - [SkillLevelEnum.Newbie]: "Новичок", - [SkillLevelEnum.Amateur]: "Любитель", - [SkillLevelEnum.Profi]: "Профи", - [SkillLevelEnum.Expert]: "Эксперт", - [SkillLevelEnum.Guru]: "Гуру", - [SkillLevelEnum.Native]: "Носитель", + [SkillLevelEnum.Newbie]: 'Новичок', + [SkillLevelEnum.Amateur]: 'Любитель', + [SkillLevelEnum.Profi]: 'Профи', + [SkillLevelEnum.Expert]: 'Эксперт', + [SkillLevelEnum.Guru]: 'Гуру', + [SkillLevelEnum.Native]: 'Носитель', }; interface LanguageLevelProps { @@ -49,23 +49,23 @@ const LanguageLevel: React.FC = ({ initialLanguageAndLevels.skillLevels, ); - useEffect(() => { - setLanguage(selectedLanguage); - setSkillLevels(selectedSkillLevels); - }, [selectedLanguage, selectedSkillLevels]); + useEffect(() => { + setLanguage(selectedLanguage); + setSkillLevels(selectedSkillLevels); + }, [selectedLanguage, selectedSkillLevels]); - const filteredLanguages = languages.filter( - (language) => - language.name.toLowerCase().includes(inputValue.toLowerCase()) || - language.name_local.toLowerCase().includes(inputValue.toLowerCase()) - ); + const filteredLanguages = languages.filter( + (language) => + language.name.toLowerCase().includes(inputValue.toLowerCase()) || + language.name_local.toLowerCase().includes(inputValue.toLowerCase()), + ); - const handleLanguageSelect = (language: Language) => { - onLanguageChange(language); - setInputValue(language.name); - setIsOpen(false); - setSelectedSuggestionIndex(null); - }; + const handleLanguageSelect = (language: Language) => { + onLanguageChange(language); + setInputValue(language.name); + setIsOpen(false); + setSelectedSuggestionIndex(null); + }; const handleSkillLevelChange = (skillLevel: SkillLevelEnum) => { if (selectedSkillLevels.includes(skillLevel)) { @@ -88,55 +88,56 @@ const LanguageLevel: React.FC = ({ } }; - const sortedLanguages = filteredLanguages.sort((a, b) => - a.name.localeCompare(b.name, 'ru', {sensitivity: 'base'}) - ); + const sortedLanguages = filteredLanguages.sort((a, b) => + a.name.localeCompare(b.name, 'ru', { sensitivity: 'base' }), + ); - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return 0; - } else if (prevIndex < sortedLanguages.length - 1) { - return prevIndex + 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return sortedLanguages.length - 1; - } else if (prevIndex > 0) { - return prevIndex - 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'Enter') { - e.preventDefault(); - if (selectedSuggestionIndex !== null) { - handleLanguageSelect(sortedLanguages[selectedSuggestionIndex]); - } else if (inputValue.trim() !== '') { - const matchedLanguage = sortedLanguages.find(language => - language.name.toLowerCase() === inputValue.toLowerCase() - ); - if (matchedLanguage) { - handleLanguageSelect(matchedLanguage); - } - } - setIsOpen(false); - } else if (e.key === 'Backspace' || e.key === 'Delete') { - if (inputValue === '' && selectedSuggestionIndex === null) { - onLanguageChange(null); - } - setInputValue(''); - setSelectedSuggestionIndex(null); - setIsOpen(false); + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return 0; + } else if (prevIndex < sortedLanguages.length - 1) { + return prevIndex + 1; + } else { + return prevIndex; } - }; + }); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return sortedLanguages.length - 1; + } else if (prevIndex > 0) { + return prevIndex - 1; + } else { + return prevIndex; + } + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedSuggestionIndex !== null) { + handleLanguageSelect(sortedLanguages[selectedSuggestionIndex]); + } else if (inputValue.trim() !== '') { + const matchedLanguage = sortedLanguages.find( + (language) => + language.name.toLowerCase() === inputValue.toLowerCase(), + ); + if (matchedLanguage) { + handleLanguageSelect(matchedLanguage); + } + } + setIsOpen(false); + } else if (e.key === 'Backspace' || e.key === 'Delete') { + if (inputValue === '' && selectedSuggestionIndex === null) { + onLanguageChange(null); + } + setInputValue(''); + setSelectedSuggestionIndex(null); + setIsOpen(false); + } + }; const handleReset = () => { onReset(); @@ -212,4 +213,3 @@ const LanguageLevel: React.FC = ({ }; export default LanguageLevel; -export default LanguageLevel; diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.tsx b/src/components/LanguageLevelModal/LanguageLevelModal.tsx index 2609ef0..8d41da8 100644 --- a/src/components/LanguageLevelModal/LanguageLevelModal.tsx +++ b/src/components/LanguageLevelModal/LanguageLevelModal.tsx @@ -34,42 +34,8 @@ const LanguageLevelModal: React.FC = ({isModalOpen, set ); }; +<<<<<<< HEAD export default LanguageLevelModal; - -import React from "react"; -import Modal from "../Modal/Modal"; - -import {Levels} from "./Levels"; - -import styles from "./LanguageLevelModal.module.scss"; - -interface LanguageLevelModalProps { - isModalOpen: boolean; - setModalOpen: React.Dispatch>; - pageName: string; -} - -const LanguageLevelModal: React.FC = ({isModalOpen, setModalOpen, pageName}) => { - const handleCloseModal = () => { - setModalOpen(false); - }; - - return ( - - {Levels.map((level) => ( -
-
-

{level.name}

-

- {level.description} -

-
- {level.name} -
- ))} -
- ); -}; - -export default LanguageLevelModal; \ No newline at end of file +======= +export default LanguageLevelModal; +>>>>>>> berezina From aa8da27712eb959b06216c45a92009de16aa351b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Mon, 28 Aug 2023 23:00:54 +0300 Subject: [PATCH 119/153] =?UTF-8?q?fix=20=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=87=D0=B5=D0=BC=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CountrySelection.module.scss | 36 +++-- .../CountrySelection/CountrySelection.tsx | 147 ++++++++++-------- .../LanguageLevelModal/LanguageLevelModal.tsx | 71 +++++---- src/components/UI/Input/Input.tsx | 4 +- src/pages/MainPage/MainPage.tsx | 7 + 5 files changed, 150 insertions(+), 115 deletions(-) diff --git a/src/components/CountrySelection/CountrySelection.module.scss b/src/components/CountrySelection/CountrySelection.module.scss index 7b2b451..b22e109 100644 --- a/src/components/CountrySelection/CountrySelection.module.scss +++ b/src/components/CountrySelection/CountrySelection.module.scss @@ -5,13 +5,6 @@ &__input { width: 296px; -.country { - display: flex; - flex-direction: column; - align-items: flex-start; - - &__input { - width: 296px; &_error { color: var(--error, #DE3D3D); @@ -23,8 +16,23 @@ &_showSuggestions::before { display: block; } - } + &:hover { + border: 2px solid var(--soft-violet, #A5A0C5); + } + + &:focus { + border: 1px solid var(--dark-violet, #403A6D); + background-image: url("../../images/svg/search_dark.svg"); + } + + &:disabled { + background: var(--grey-gradient-50, #F0F2F4); + border: none; + color: var(--soft-violet, #A5A0C5); + } + } + &__selectedCountries { position: relative; width: 296px; @@ -40,7 +48,7 @@ font-weight: 400; line-height: normal; display: flex; - align-items: center; + align-items: center; word-wrap: break-word; white-space: nowrap; } @@ -70,7 +78,7 @@ background-color: var(--accent-color-salad, #C3EE11); display: flex; flex-wrap: wrap; - width: fit-content; + width: fit-content; } &__countryList { @@ -88,14 +96,14 @@ z-index: 1000; &::-webkit-scrollbar { - width: .75em; + width: .75em; } - + &::-webkit-scrollbar-track { background-color: var(--white, #FFF); margin-block: .5em; } - + &::-webkit-scrollbar-thumb { background-color: var(--soft-violet, #A5A0C5); border-radius: 100vw; @@ -116,6 +124,6 @@ width: 16px; height: 16px; margin-right: 8px; - } + } } } diff --git a/src/components/CountrySelection/CountrySelection.tsx b/src/components/CountrySelection/CountrySelection.tsx index fff9921..6698867 100644 --- a/src/components/CountrySelection/CountrySelection.tsx +++ b/src/components/CountrySelection/CountrySelection.tsx @@ -70,76 +70,85 @@ const CountrySelection: FC = ({ } }; - const handleSearchInputChange = (e: React.ChangeEvent) => { - const newSearchValue = e.target.value; - setSearchValue(newSearchValue); - if (newSearchValue) { - setCountryListVisible(true); - } else { - setCountryListVisible(false); - } - - const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); - const filtered = countriesData.filter((country) => - country.name.toLocaleLowerCase('ru').includes(searchValueLower) && !selectedCountries.includes(country) - ); - setFilteredCountries(filtered); + const handleSearchInputChange = (e: React.ChangeEvent) => { + const newSearchValue = e.target.value; + setSearchValue(newSearchValue); + if (newSearchValue) { + setCountryListVisible(true); + } else { + setCountryListVisible(false); + } - const suggested = countriesData.filter((country) => - country.name.toLocaleLowerCase('ru').startsWith(searchValueLower) - ); - setSuggestedCountries(suggested); + const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); + const filtered = countriesData.filter( + (country) => + country.name.toLocaleLowerCase('ru').includes(searchValueLower) && + !selectedCountries.includes(country), + ); + setFilteredCountries(filtered); - const firstLetter = searchValueLower.length > 0 ? searchValueLower.charAt(0) : null; - setLastPressedLetter(firstLetter); + const suggested = countriesData.filter((country) => + country.name.toLocaleLowerCase('ru').startsWith(searchValueLower), + ); + setSuggestedCountries(suggested); + const firstLetter = + searchValueLower.length > 0 ? searchValueLower.charAt(0) : null; + setLastPressedLetter(firstLetter); - const isInvalidSearch = newSearchValue.length > 0 && filtered.length === 0 && suggested.length === 0; - const errorMessage = isInvalidSearch ? 'Страны не существует, возможно ошибка' : ''; - setIsError(isInvalidSearch); - setErrorMessage(errorMessage); + const isInvalidSearch = + newSearchValue.length > 0 && + filtered.length === 0 && + suggested.length === 0; + const errorMessage = isInvalidSearch + ? 'Страны не существует, возможно ошибка' + : ''; + setIsError(isInvalidSearch); + setErrorMessage(errorMessage); - setCountryListVisible(filtered.length > 0 && newSearchValue.length > 0); + setCountryListVisible(filtered.length > 0 && newSearchValue.length > 0); - setSelectedSuggestionIndex(null); - }; + setSelectedSuggestionIndex(null); + }; - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return 0; - } else if (prevIndex < suggestedCountries.length - 1) { - return prevIndex + 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - setSelectedSuggestionIndex((prevIndex) => { - if (prevIndex === null) { - return suggestedCountries.length - 1; - } else if (prevIndex > 0) { - return prevIndex - 1; - } else { - return prevIndex; - } - }); - } else if (e.key === 'Enter') { - e.preventDefault(); - if (selectedSuggestionIndex !== null) { - handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); - } else if (selectedCountry) { - handleSelectCountry(selectedCountry); - } + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return 0; + } else if (prevIndex < suggestedCountries.length - 1) { + return prevIndex + 1; + } else { + return prevIndex; + } + }); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedSuggestionIndex((prevIndex) => { + if (prevIndex === null) { + return suggestedCountries.length - 1; + } else if (prevIndex > 0) { + return prevIndex - 1; + } else { + return prevIndex; } - }; + }); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedSuggestionIndex !== null) { + handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); + } else if (selectedCountry) { + handleSelectCountry(selectedCountry); + } + } + }; - const handleRemoveCountry = (country: Country) => { - const updatedCountries = selectedCountries.filter((c) => c.code !== country.code); - setSelectedCountries(updatedCountries); - }; + const handleRemoveCountry = (country: Country) => { + const updatedCountries = selectedCountries.filter( + (c) => c.code !== country.code, + ); + setSelectedCountries(updatedCountries); + }; const handleDropdownKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'ArrowDown') { @@ -209,12 +218,14 @@ const CountrySelection: FC = ({ setLastPressedLetter(null); }; - const handleSelectCountryFromList = (countryName: string) => { - const selectedCountry = countriesData.find(country => country.name.toLocaleLowerCase('ru') === countryName); - if (selectedCountry) { - handleSelectCountry(selectedCountry); - } - }; + const handleSelectCountryFromList = (countryName: string) => { + const selectedCountry = countriesData.find( + (country) => country.name.toLocaleLowerCase('ru') === countryName, + ); + if (selectedCountry) { + handleSelectCountry(selectedCountry); + } + }; return (
diff --git a/src/components/LanguageLevelModal/LanguageLevelModal.tsx b/src/components/LanguageLevelModal/LanguageLevelModal.tsx index 8d41da8..29767fa 100644 --- a/src/components/LanguageLevelModal/LanguageLevelModal.tsx +++ b/src/components/LanguageLevelModal/LanguageLevelModal.tsx @@ -1,41 +1,50 @@ -import React from "react"; -import Modal from "../Modal/Modal"; +import React from 'react'; +import Modal from '../Modal/Modal'; -import {Levels} from "./Levels"; +import { Levels } from './Levels'; -import styles from "./LanguageLevelModal.module.scss"; +import styles from './LanguageLevelModal.module.scss'; interface LanguageLevelModalProps { - isModalOpen: boolean; - setModalOpen: React.Dispatch>; - pageName: string; + isModalOpen: boolean; + setModalOpen: React.Dispatch>; + pageName: string; } -const LanguageLevelModal: React.FC = ({isModalOpen, setModalOpen, pageName}) => { - const handleCloseModal = () => { - setModalOpen(false); - }; +const LanguageLevelModal: React.FC = ({ + isModalOpen, + setModalOpen, + pageName, +}) => { + const handleCloseModal = () => { + setModalOpen(false); + }; - return ( - - {Levels.map((level) => ( -
-
-

{level.name}

-

- {level.description} -

-
- {level.name} -
- ))} -
- ); + return ( + + {Levels.map((level) => ( +
+
+

{level.name}

+

{level.description}

+
+ {level.name} +
+ ))} +
+ ); }; -<<<<<<< HEAD export default LanguageLevelModal; -======= -export default LanguageLevelModal; ->>>>>>> berezina diff --git a/src/components/UI/Input/Input.tsx b/src/components/UI/Input/Input.tsx index e3a97ea..9c5d1e1 100644 --- a/src/components/UI/Input/Input.tsx +++ b/src/components/UI/Input/Input.tsx @@ -6,7 +6,7 @@ import cn from 'classnames'; export interface InputProps extends InputHTMLAttributes { className?: string; - onValue: ({ value, name }: { value: string; name: T }) => void; + onValue?: ({ value, name }: { value: string; name: T }) => void; value: string; name: T; label?: string; @@ -117,4 +117,4 @@ export const Input = ({ )}
); -}; \ No newline at end of file +}; diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 726e038..4c469f1 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -19,6 +19,13 @@ import Modal from '../../components/Modal/Modal'; import { useModel } from '../../components/SignupSigninForm/model'; const MainPage = () => { + + const model = useModel(); + + const handleCloseModal = () => { + model.isModalOpen = false; + }; + useEffect(() => { console.log(`main_loggedIn: ${loggedIn.loggedIn}`); }, []); From b5d3eaf90b446cd7e7da093b5325f343fa84d3ac Mon Sep 17 00:00:00 2001 From: Keti <“rudnevaketi@gmail.com”> Date: Tue, 29 Aug 2023 08:25:45 +0300 Subject: [PATCH 120/153] added TS --- package-lock.json | 65 +++- .../About/{About.jsx => About.tsx} | 28 +- .../{EditButton.jsx => EditButton.tsx} | 0 .../{Certificates.jsx => Certificates.tsx} | 0 .../{LevelLanguage.jsx => LevelLanguage.tsx} | 0 .../Reviews/{Reviews.jsx => Reviews.tsx} | 0 .../Topics/{Topics.jsx => Topics.tsx} | 19 +- .../UserCard/{UserCard.jsx => UserCard.tsx} | 80 ++-- .../{UserLanguages.jsx => UserLanguages.tsx} | 23 +- .../{UserProfile.jsx => UserProfile.tsx} | 182 +++++---- src/utils/openapi.ts | 355 +++++++++++++----- 11 files changed, 513 insertions(+), 239 deletions(-) rename src/pages/UserProfile/About/{About.jsx => About.tsx} (70%) rename src/pages/UserProfile/Buttons/EditButton/{EditButton.jsx => EditButton.tsx} (100%) rename src/pages/UserProfile/Certificates/{Certificates.jsx => Certificates.tsx} (100%) rename src/pages/UserProfile/LevelLanguage/{LevelLanguage.jsx => LevelLanguage.tsx} (100%) rename src/pages/UserProfile/Reviews/{Reviews.jsx => Reviews.tsx} (100%) rename src/pages/UserProfile/Topics/{Topics.jsx => Topics.tsx} (80%) rename src/pages/UserProfile/UserCard/{UserCard.jsx => UserCard.tsx} (73%) rename src/pages/UserProfile/UserLanguages/{UserLanguages.jsx => UserLanguages.tsx} (85%) rename src/pages/UserProfile/{UserProfile.jsx => UserProfile.tsx} (61%) diff --git a/package-lock.json b/package-lock.json index 83d9d31..b59431d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "classnames": "^2.3.2", "compose-function": "^3.0.3", "gh-pages": "^5.0.0", + "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.1", @@ -25,7 +26,7 @@ "@types/mini-css-extract-plugin": "^2.5.1", "css-loader": "^6.8.1", "mini-css-extract-plugin": "^2.7.6", - "mobx": "^6.9.0", + "mobx": "^6.10.0", "mobx-react-lite": "^3.4.3", "style-loader": "^3.3.3", "typescript": "^5.1.6", @@ -12410,15 +12411,39 @@ } }, "node_modules/mobx": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.9.0.tgz", - "integrity": "sha512-HdKewQEREEJgsWnErClfbFoVebze6rGazxFLU/XUyrII8dORfVszN1V0BMRnQSzcgsNNtkX8DHj3nC6cdWE9YQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.10.0.tgz", + "integrity": "sha512-WMbVpCMFtolbB8swQ5E2YRrU+Yu8iLozCVx3CdGjbBKlP7dFiCSuiG06uea3JCFN5DnvtAX7+G5Bp82e2xu0ww==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" } }, + "node_modules/mobx-react": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.0.0.tgz", + "integrity": "sha512-rhUBWXK/k4H/jMXZqbL0uTPncRSvJR23tpmJemoAEjJjm0Fkv6tTLG3Cze9MIgvf882Kradx6msizuQxLAiQwA==", + "dependencies": { + "mobx-react-lite": "^4.0.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/mobx-react-lite": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", @@ -12441,6 +12466,30 @@ } } }, + "node_modules/mobx-react/node_modules/mobx-react-lite": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.0.3.tgz", + "integrity": "sha512-wEE1oT5zvDdvplG4HnRrFgPwg5GFVVrEtl42Er85k23zeu3om8H8wbDPgdbQP88zAihVsik6xJfw6VnzUl8fQw==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -16894,6 +16943,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/src/pages/UserProfile/About/About.jsx b/src/pages/UserProfile/About/About.tsx similarity index 70% rename from src/pages/UserProfile/About/About.jsx rename to src/pages/UserProfile/About/About.tsx index fd6348f..c1dc1a5 100644 --- a/src/pages/UserProfile/About/About.jsx +++ b/src/pages/UserProfile/About/About.tsx @@ -1,11 +1,19 @@ +import React, { FC } from 'react'; import styles from "./About.module.scss"; +interface AboutProps { + isEditing: boolean; + about: string; + setAboutMe: (value: string) => void; + setLearningLanguage: (value: string) => void; +} -const About = ({isEditing, aboutMe, setAboutMe, learningLanguage, setLearningLanguage}) => { - - const handleChange = (setState) => (event) => { - setState(event.target.value); - } +const About: FC = ({ + isEditing, + about, + setAboutMe, + setLearningLanguage, +}) => { return( <> @@ -13,11 +21,10 @@ const About = ({isEditing, aboutMe, setAboutMe, learningLanguage, setLearningLan

Обо мне

-

{aboutMe}

+

{about}

Я изучаю язык, чтобы

-

{learningLanguage}

):( @@ -25,8 +32,8 @@ const About = ({isEditing, aboutMe, setAboutMe, learningLanguage, setLearningLan

Обо мне

- {hasError ? ( - {error} - ) : ( - hint && {hint} - )} - - ) : ( - <> - - {hasError ? ( - {error} - ) : ( - hint && {hint} - )} - - ); + return label ? ( + + ) : ( + <> + + {hasError ? ( + {error} + ) : ( + hint && {hint} + )} + + ); }; diff --git a/src/components/UI/UserStatusIsOnline/UserStatusIsOnline.tsx b/src/components/UI/UserStatusIsOnline/UserStatusIsOnline.tsx index b5e9888..7c59495 100644 --- a/src/components/UI/UserStatusIsOnline/UserStatusIsOnline.tsx +++ b/src/components/UI/UserStatusIsOnline/UserStatusIsOnline.tsx @@ -1,7 +1,9 @@ -import { FC } from 'react'; -import styles from './UserStatusIsOnline.module.scss'; -import logInIndicator from '../../../images/svg/card-indicator-logIn.svg'; -import logOutIndicator from '../../../images/svg/card-indicator-logOut.svg'; +import { FC } from "react"; + +import logInIndicator from "../../../images/svg/card-indicator-logIn.svg"; +import logOutIndicator from "../../../images/svg/card-indicator-logOut.svg"; + +import styles from "./UserStatusIsOnline.module.scss"; interface IUserStatusIsOnline { is_online: boolean; @@ -16,10 +18,10 @@ const UserStatusIsOnline: FC = ({ is_online }) => { className={styles.card__isOnline} src={indicatorIcon} alt={`Индикатор статуса пользователя (${ - is_online ? 'в сети' : 'не в сети' + is_online ? "в сети" : "не в сети" })`} /> -

{is_online ? 'в сети' : 'не в сети'}

+

{is_online ? "в сети" : "не в сети"}

); }; diff --git a/src/features/authenticate/authenticate.ts b/src/features/authenticate/authenticate.ts index d5d61fc..dce1054 100644 --- a/src/features/authenticate/authenticate.ts +++ b/src/features/authenticate/authenticate.ts @@ -1,5 +1,5 @@ -import { store } from '../../models/store'; -import { getAcceessToken, getMe } from '../../utils/rest/auth'; +import { store } from "../../models/store"; +import { getAcceessToken, getMe } from "../../utils/rest/auth"; export const authenticate = async (): Promise => { try { @@ -9,11 +9,11 @@ export const authenticate = async (): Promise => { store.session.updateUser(user); } } catch (error) { - console.log('features.authenticate updateUser', error); + console.log("features.authenticate updateUser", error); } try { - const refreshToken = localStorage.getItem('refreshToken'); + const refreshToken = localStorage.getItem("refreshToken"); if (refreshToken) { const accessToken = await getAcceessToken(refreshToken); if (accessToken) { @@ -21,6 +21,6 @@ export const authenticate = async (): Promise => { } } } catch (error) { - console.log('features.authenticate getAccessToken', error); + console.log("features.authenticate getAccessToken", error); } }; diff --git a/src/index.tsx b/src/index.tsx index 0aa7810..1932bc3 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,14 +1,14 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './vendor/index.css'; -import App from './components/App/App'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./vendor/index.css"; +import App from "./components/App/App"; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLFormElement, + document.getElementById("root") as HTMLFormElement ); root.render( - , + ); diff --git a/src/models/LoggedIn.ts b/src/models/LoggedIn.ts index b503619..1d44f9f 100644 --- a/src/models/LoggedIn.ts +++ b/src/models/LoggedIn.ts @@ -1,4 +1,4 @@ -import { action, makeObservable, observable } from 'mobx'; +import { action, makeObservable, observable } from "mobx"; class LoggedIn { loggedIn = false; diff --git a/src/models/session/Session.ts b/src/models/session/Session.ts index e7b0843..2ce5dee 100644 --- a/src/models/session/Session.ts +++ b/src/models/session/Session.ts @@ -1,5 +1,5 @@ -import { action, computed, makeObservable, observable } from 'mobx'; -import { User, UserInterface } from './User'; +import { action, computed, makeObservable, observable } from "mobx"; +import { User, UserInterface } from "./User"; class Session { private _user: User | null = null; @@ -7,7 +7,7 @@ class Session { private _refreshToken: string | null = null; constructor() { - makeObservable(this, { + makeObservable(this, { _user: observable, _accessToken: observable, _refreshToken: observable, diff --git a/src/models/session/User.ts b/src/models/session/User.ts index 1824467..6baaad2 100644 --- a/src/models/session/User.ts +++ b/src/models/session/User.ts @@ -1,67 +1,106 @@ -import { makeObservable, observable } from 'mobx'; +import { makeObservable, observable } from "mobx"; -import { - Country, - GenderEnum, - Goal, - NullEnum, - UserLanguage, -} from '../../utils/openapi'; +import { GenderEnum, NullEnum, UserLanguage } from "../../utils/openapi"; export interface UserInterface { + username: string; first_name?: string; avatar?: string | null; - country?: Country | null; - birth_date?: string | null; + age: string; + slug: string | null; + country?: string | null; languages?: UserLanguage[]; gender?: GenderEnum | NullEnum | null; - goals?: Goal[]; + goals?: string[]; interests?: string[]; about?: string; + last_activity: string | null; + is_online: boolean; + gender_is_hidden: boolean; + age_is_hidden: boolean; + role: string; + is_blocked: boolean; + birth_date?: string | null; } export class User { + username: string; first_name?: string; avatar?: string | null; - country?: Country | null; - birth_date?: string | null; + age: string; + slug: string | null; + country?: string | null; languages?: UserLanguage[]; gender?: GenderEnum | NullEnum | null; - goals?: Goal[]; + goals?: string[]; interests?: string[]; about?: string; + last_activity: string | null; + is_online: boolean; + gender_is_hidden: boolean; + age_is_hidden: boolean; + role: string; + is_blocked: boolean; + birth_date?: string | null; constructor({ + username, first_name, avatar, + age, + slug, country, - birth_date, languages, gender, goals, interests, about, + last_activity, + is_online, + gender_is_hidden, + age_is_hidden, + role, + is_blocked, + birth_date, }: UserInterface) { makeObservable(this, { + username: observable, first_name: observable, avatar: observable, + age: observable, + slug: observable, country: observable, - birth_date: observable, languages: observable, gender: observable, goals: observable, interests: observable, about: observable, + last_activity: observable, + is_online: observable, + gender_is_hidden: observable, + age_is_hidden: observable, + role: observable, + is_blocked: observable, + birth_date: observable, }); + this.username = username; this.first_name = first_name; this.avatar = avatar; + this.age = age; + this.slug = slug; this.country = country; - this.birth_date = birth_date; this.languages = languages; this.gender = gender; this.goals = goals; this.interests = interests; this.about = about; + this.last_activity = last_activity; + this.is_online = is_online; + this.gender_is_hidden = gender_is_hidden; + this.age_is_hidden = age_is_hidden; + this.role = role; + this.is_blocked = is_blocked; + this.birth_date = birth_date; } } diff --git a/src/models/store.ts b/src/models/store.ts index eb739ea..084c8e3 100644 --- a/src/models/store.ts +++ b/src/models/store.ts @@ -1,4 +1,4 @@ -import { session } from './session/Session'; +import { session } from "./session/Session"; export const store = { session, diff --git a/src/pages/AgreementPage/AgreementPage.tsx b/src/pages/AgreementPage/AgreementPage.tsx index 8e7551b..5916c43 100644 --- a/src/pages/AgreementPage/AgreementPage.tsx +++ b/src/pages/AgreementPage/AgreementPage.tsx @@ -1,7 +1,7 @@ -import Header from '../../components/Header/Header'; -import Footer from '../../components/Footer/Footer'; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; -import styles from './AgreementPage.module.scss'; +import styles from "./AgreementPage.module.scss"; const AgreementPage = () => { return ( @@ -12,8 +12,8 @@ const AgreementPage = () => {

Настоящее Пользовательское Соглашение (Далее Соглашение) регулирует - отношения между владельцем{' '} - https://lingvogo.acceleratorpracticum.ru/ (далее{' '} + отношения между владельцем{" "} + https://lingvogo.acceleratorpracticum.ru/ (далее{" "} https://lingvogo.acceleratorpracticum.ru/ или Администрация) с одной стороны и пользователем сайта с другой.

@@ -23,7 +23,7 @@ const AgreementPage = () => {

Используя сайт, Вы соглашаетесь с условиями данного соглашения. Если - Вы не согласны с условиями данного соглашения, не используйте сайт{' '} + Вы не согласны с условиями данного соглашения, не используйте сайт{" "} https://lingvogo.acceleratorpracticum.ru/!

Предмет соглашения

diff --git a/src/pages/FAQPage/FAQPage.tsx b/src/pages/FAQPage/FAQPage.tsx index 42ee6ce..22a1687 100644 --- a/src/pages/FAQPage/FAQPage.tsx +++ b/src/pages/FAQPage/FAQPage.tsx @@ -1,10 +1,10 @@ -import Header from '../../components/Header/Header'; -import Footer from '../../components/Footer/Footer'; -import QuestionBlock from '../../components/QuestionBlock/QuestionBlock'; -import { FAQArray } from '../../utils/constants'; -import IQuestion from '../../types/types'; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; +import QuestionBlock from "../../components/QuestionBlock/QuestionBlock"; +import { FAQArray } from "../../utils/constants"; +import IQuestion from "../../types/types"; -import styles from './FAQPage.module.scss'; +import styles from "./FAQPage.module.scss"; interface IProps { item: IQuestion; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx index 3f82cc0..bfe6ae0 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx @@ -1,27 +1,27 @@ -import { useEffect } from 'react'; -import { observer } from 'mobx-react-lite'; +import { useEffect } from "react"; +import { observer } from "mobx-react-lite"; -import Header from '../../components/Header/Header'; -import ProgressLine from '../../components/UI/ProgressLine/ProgressLine'; -import { Input } from '../../components/UI/Input/Input'; -import { Button } from '../../components/UI/Button/Button'; -import Gender from '../../components/Gender/Gender'; -import Modal from '../../components/Modal/Modal'; -import Avatars from '../../components/Avatars/Avatars'; +import Header from "../../components/Header/Header"; +import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; +import { Input } from "../../components/UI/Input/Input"; +import { Button } from "../../components/UI/Button/Button"; +import Gender from "../../components/Gender/Gender"; +import Modal from "../../components/Modal/Modal"; +import Avatars from "../../components/Avatars/Avatars"; -import avatarPlace from '../../images/fill-out-profile-export-avatar.png'; -import faceInACircle from '../../images/face-in-a-circle.png'; +import avatarPlace from "../../images/fill-out-profile-export-avatar.png"; +import faceInACircle from "../../images/face-in-a-circle.png"; -import { useModel } from './model'; +import { useModel } from "./model"; -import styles from './FillOutProfilePages.module.scss'; -import cn from 'classnames'; +import styles from "./FillOutProfilePages.module.scss"; +import cn from "classnames"; const FillOutProfilePage1 = () => { const model = useModel(); useEffect(() => { - console.log(model.avatar); + model.handleCurrentUser(); }, []); return ( @@ -34,7 +34,7 @@ const FillOutProfilePage1 = () => { Давайте заполним ваш профиль и начнем общаться

@@ -42,16 +42,16 @@ const FillOutProfilePage1 = () => { {
{ } > Аватар пользователя
{ } > @@ -123,8 +123,8 @@ const FillOutProfilePage1 = () => {
model.handleSetAvatarPhoto(event)} /> @@ -184,20 +184,20 @@ const FillOutProfilePage1 = () => { /> diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx index 8113a5e..02e4a02 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx @@ -1,98 +1,102 @@ -import React, {FormEvent, useEffect, useMemo, useState} from "react"; -import {useNavigate} from "react-router-dom"; -import {observer} from "mobx-react-lite"; +import React, { FormEvent, useEffect, useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { observer } from "mobx-react-lite"; import Header from "../../components/Header/Header"; import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; import LanguageModule from "../../components/LanguageModule/LanguageModule"; -import {Button} from "../../components/UI/Button/Button"; +import { Button } from "../../components/UI/Button/Button"; import CountrySelection from "../../components/CountrySelection/CountrySelection"; -import {Country, Language, SkillLevelEnum} from "../../utils/openapi"; - -import styles from './FillOutProfilePages.module.scss'; +import { Country, Language, SkillLevelEnum } from "../../utils/openapi"; +import styles from "./FillOutProfilePages.module.scss"; const FillOutProfilePage2 = () => { - const navigate = useNavigate(); + const navigate = useNavigate(); - const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); - const [selectedCountries, setSelectedCountries] = useState([]); + const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); + const [selectedCountries, setSelectedCountries] = useState([]); - const initialLanguageAndLevels = useMemo(() => { - return {language: null, skillLevels: []}; - }, []); + const initialLanguageAndLevels = useMemo(() => { + return { language: null, skillLevels: [] }; + }, []); - const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< - { language: Language | null; skillLevels: SkillLevelEnum[] }[] - >([initialLanguageAndLevels]); + const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< + { language: Language | null; skillLevels: SkillLevelEnum[] }[] + >([initialLanguageAndLevels]); - const handleReturnButtonClick = () => { - navigate("/fill-out-1"); - } + const handleReturnButtonClick = () => { + navigate("/fill-out-1"); + }; - const handleSubmitButtonDisabled = () => { - for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { - selectedLanguagesAndLevels[i].language === null - ? setSubmitButtonDisabled(true) - : setSubmitButtonDisabled(false) - } + const handleSubmitButtonDisabled = () => { + for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { + selectedLanguagesAndLevels[i].language === null + ? setSubmitButtonDisabled(true) + : setSubmitButtonDisabled(false); } + }; - useEffect(() => { - handleSubmitButtonDisabled(); - }, [selectedLanguagesAndLevels]); + useEffect(() => { + handleSubmitButtonDisabled(); + }, [selectedLanguagesAndLevels]); - const handleFillOutPage2 = (event: FormEvent) => { - event.preventDefault(); - navigate("/fill-out-3"); - console.log("FillOutPage2"); - }; + const handleFillOutPage2 = (event: FormEvent) => { + event.preventDefault(); + navigate("/fill-out-3"); + console.log("FillOutPage2"); + }; - return ( - <> -
-
- -
- -

Укажите страну и родной язык

- -
-

- Страна, в которой Вы сейчас живете -

- -
-
-

- Ваш родной язык, язык на котором Вы свободно говорите -

- -
- - -
-
- - ); + return ( + <> +
+
+ +
+ +

+ Укажите страну и родной язык +

+
+
+

+ Страна, в которой Вы сейчас живете +

+ +
+
+

+ Ваш родной язык, язык на котором Вы свободно говорите +

+ +
+ +
+
+
+ + ); }; export default observer(FillOutProfilePage2); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx index f495917..97db807 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx @@ -1,95 +1,111 @@ -import React, {FormEvent, useEffect, useMemo, useState} from "react"; -import {useNavigate} from "react-router-dom"; +import React, { FormEvent, useEffect, useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; import Header from "../../components/Header/Header"; import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; import LanguageModule from "../../components/LanguageModule/LanguageModule"; -import {Button} from "../../components/UI/Button/Button"; +import { Button } from "../../components/UI/Button/Button"; -import {Language, SkillLevelEnum} from "../../utils/openapi"; +import { Language, SkillLevelEnum } from "../../utils/openapi"; import styles from "./FillOutProfilePages.module.scss"; import cn from "classnames"; import LanguageLevelModal from "../../components/LanguageLevelModal/LanguageLevelModal"; const FillOutProfilePage1 = () => { - const navigate = useNavigate(); + const navigate = useNavigate(); - const [isModalOpen, setModalOpen] = useState(false); - const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); + const [isModalOpen, setModalOpen] = useState(false); + const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); - const initialLanguageAndLevels = useMemo(() => { - return {language: null, skillLevels: []}; - }, []); + const initialLanguageAndLevels = useMemo(() => { + return { language: null, skillLevels: [] }; + }, []); - const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< - { language: Language | null; skillLevels: SkillLevelEnum[] }[] - >([initialLanguageAndLevels]); + const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< + { language: Language | null; skillLevels: SkillLevelEnum[] }[] + >([initialLanguageAndLevels]); - const handleSubmitButtonDisabled = () => { - for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { - selectedLanguagesAndLevels[i].language === null || selectedLanguagesAndLevels[i].skillLevels.toString() === [].toString() - ? setSubmitButtonDisabled(true) - : setSubmitButtonDisabled(false) - } + const handleSubmitButtonDisabled = () => { + for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { + selectedLanguagesAndLevels[i].language === null || + selectedLanguagesAndLevels[i].skillLevels.toString() === [].toString() + ? setSubmitButtonDisabled(true) + : setSubmitButtonDisabled(false); } + }; - useEffect(() => { - handleSubmitButtonDisabled(); - }, [selectedLanguagesAndLevels]); + useEffect(() => { + handleSubmitButtonDisabled(); + }, [selectedLanguagesAndLevels]); - const handleReturnButtonClick = () => { - navigate("/fill-out-2"); - } + const handleReturnButtonClick = () => { + navigate("/fill-out-2"); + }; - const handleHelpButtonClick = () => { - setModalOpen(true); - } + const handleHelpButtonClick = () => { + setModalOpen(true); + }; - const handleFillOutPage3 = (event: FormEvent) => { - event.preventDefault(); - navigate("/fill-out-4"); - console.log("FillOutPage3"); - }; + const handleFillOutPage3 = (event: FormEvent) => { + event.preventDefault(); + navigate("/fill-out-4"); + console.log("FillOutPage3"); + }; - return ( - <> -
-
- -
- -

Выберите изучаемые языки

-
-
- - -
- -
-
+ return ( + <> +
+
+ +
+ +

Выберите изучаемые языки

+
+
+ + +
+ +
+
- -
- - ); + +
+ + ); }; export default FillOutProfilePage1; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx index c45ce1d..2ca73dd 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx @@ -1,102 +1,117 @@ -import {ChangeEvent, FormEvent, MouseEventHandler, useEffect, useMemo, useState} from "react"; -import {useNavigate} from "react-router-dom"; +import { + ChangeEvent, + FormEvent, + MouseEventHandler, + useEffect, + useMemo, + useState, +} from "react"; +import { useNavigate } from "react-router-dom"; import Header from "../../components/Header/Header"; import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import {Button} from "../../components/UI/Button/Button"; -import {Goals} from "./Goals"; +import { Button } from "../../components/UI/Button/Button"; +import { Goals } from "./Goals"; import styles from "./FillOutProfilePages.module.scss"; const FillOutProfilePage4 = () => { - const navigate = useNavigate(); + const navigate = useNavigate(); - const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); + const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); - const [selectedGoals, setSelectedGoals] = useState<{ - image: string, name: string, description: string, active: boolean - }[]>([]); + const [selectedGoals, setSelectedGoals] = useState< + { + image: string; + name: string; + description: string; + active: boolean; + }[] + >([]); - console.log(selectedGoals); - console.log(isSubmitButtonDisabled); + console.log(selectedGoals); + console.log(isSubmitButtonDisabled); - const handleSubmitButtonDisabled = () => { - selectedGoals.length === 0 - ? setSubmitButtonDisabled(true) - : setSubmitButtonDisabled(false) - } + const handleSubmitButtonDisabled = () => { + selectedGoals.length === 0 + ? setSubmitButtonDisabled(true) + : setSubmitButtonDisabled(false); + }; - useEffect(() => { - handleSubmitButtonDisabled(); - }, [selectedGoals]); + useEffect(() => { + handleSubmitButtonDisabled(); + }, [selectedGoals]); - const handleReturnButtonClick = () => { - navigate("/fill-out-3"); - }; + const handleReturnButtonClick = () => { + navigate("/fill-out-3"); + }; - const handleGoalButtonClick = (event, goal, index) => { - const updatedGoals = [...selectedGoals]; + const handleGoalButtonClick = (event, goal, index) => { + const updatedGoals = [...selectedGoals]; - console.log(goal.active); + console.log(goal.active); - if (!goal.active) { - goal.active = true; - updatedGoals.push(goal); - setSelectedGoals(updatedGoals); - event.currentTarget.classList.add(styles.container__goals_goal_active); - } else { - goal.active = false; - const updatedGoals = selectedGoals.filter((selectedGoal) => selectedGoal.active); - setSelectedGoals(updatedGoals); - event.currentTarget.classList.remove(styles.container__goals_goal_active); - } - } - const handleFillOutPage4 = (event: FormEvent) => { - event.preventDefault(); - navigate("/fill-out-5"); - console.log("FillOutPage4"); - }; - - return ( - <> -
-
- -
- -

Укажите ваши цели

-
-
- {Goals.map((goal, index) => ( - - ))} -
- -
-
-
- - ); + if (!goal.active) { + goal.active = true; + updatedGoals.push(goal); + setSelectedGoals(updatedGoals); + event.currentTarget.classList.add(styles.container__goals_goal_active); + } else { + goal.active = false; + const updatedGoals = selectedGoals.filter( + (selectedGoal) => selectedGoal.active + ); + setSelectedGoals(updatedGoals); + event.currentTarget.classList.remove(styles.container__goals_goal_active); } -; + }; + const handleFillOutPage4 = (event: FormEvent) => { + event.preventDefault(); + navigate("/fill-out-5"); + console.log("FillOutPage4"); + }; + return ( + <> +
+
+ +
+ +

Укажите ваши цели

+
+
+ {Goals.map((goal, index) => ( + + ))} +
+ +
+
+
+ + ); +}; export default FillOutProfilePage4; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx index 0377195..c733084 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx @@ -1,17 +1,17 @@ -import { FormEvent, useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { observer } from 'mobx-react-lite'; +import { FormEvent, useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { observer } from "mobx-react-lite"; -import Header from '../../components/Header/Header'; -import ProgressLine from '../../components/UI/ProgressLine/ProgressLine'; -import { Input } from '../../components/UI/Input/Input'; -import { Button } from '../../components/UI/Button/Button'; -import InterestsSelection from '../../components/CountriesAndInterestsSelection/CountriesAndInterestsSelection'; +import Header from "../../components/Header/Header"; +import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; +import { Input } from "../../components/UI/Input/Input"; +import { Button } from "../../components/UI/Button/Button"; +import InterestsSelection from "../../components/CountriesAndInterestsSelection/CountriesAndInterestsSelection"; -import { useModel } from './model'; +import { useModel } from "./model"; -import styles from './FillOutProfilePages.module.scss'; -import { Interest } from '../../utils/openapi'; +import styles from "./FillOutProfilePages.module.scss"; +import { Interest } from "../../utils/openapi"; const FillOutProfilePage5 = () => { const model = useModel(); @@ -20,13 +20,13 @@ const FillOutProfilePage5 = () => { const [selectedInterests, setSelectedInterests] = useState([]); const handleReturnButtonClick = () => { - navigate('/fill-out-4'); + navigate("/fill-out-4"); }; const handleFillOutPage5 = (event: FormEvent) => { event.preventDefault(); - navigate('/fill-out-6'); - console.log('FillOutPage5'); + navigate("/fill-out-6"); + console.log("FillOutPage5"); }; return ( @@ -44,14 +44,14 @@ const FillOutProfilePage5 = () => {

Укажите ваши интересы

-
- -

Расскажите о себе

- - - - -
- - - ); + return ( + <> +
+
+ +
+ +

Расскажите о себе

+
+ + +
+
+
+ + ); }; export default observer(FillOutProfilePage6); diff --git a/src/pages/FillOutProfilePages/Goals.ts b/src/pages/FillOutProfilePages/Goals.ts index 92372df..871c31a 100644 --- a/src/pages/FillOutProfilePages/Goals.ts +++ b/src/pages/FillOutProfilePages/Goals.ts @@ -6,10 +6,40 @@ import handshakeLogo from "../../images/goals/handshake-logo.png"; import brain from "../../images/goals/brain.png"; export const Goals = [ - {image: graduationCap, name: "Академическая шапочка", description: "Образование", active: false}, - {image: business, name: "Портфель бизнесмена", description: "Карьера и бизнес", active: false}, - {image: geography, name: "Модель планеты", description: "Путешествие", active: false}, - {image: product, name: "Иконка чемодана", description: "Переезд", active: false}, - {image: handshakeLogo, name: "Иконка рукопожатия", description: "Сообщество", active: false}, - {image: brain, name: "Иконка человеческого мозга", description: "Развитие", active: false}, + { + image: graduationCap, + name: "Академическая шапочка", + description: "Образование", + active: false, + }, + { + image: business, + name: "Портфель бизнесмена", + description: "Карьера и бизнес", + active: false, + }, + { + image: geography, + name: "Модель планеты", + description: "Путешествие", + active: false, + }, + { + image: product, + name: "Иконка чемодана", + description: "Переезд", + active: false, + }, + { + image: handshakeLogo, + name: "Иконка рукопожатия", + description: "Сообщество", + active: false, + }, + { + image: brain, + name: "Иконка человеческого мозга", + description: "Развитие", + active: false, + }, ]; diff --git a/src/pages/FillOutProfilePages/model.ts b/src/pages/FillOutProfilePages/model.ts index 326b2f0..38917f8 100644 --- a/src/pages/FillOutProfilePages/model.ts +++ b/src/pages/FillOutProfilePages/model.ts @@ -1,43 +1,62 @@ -import React, { FormEvent } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { useLocalObservable } from 'mobx-react-lite'; +import React, { FormEvent } from "react"; +import { useNavigate } from "react-router-dom"; +import { useLocalObservable } from "mobx-react-lite"; -import { getMe } from '../../utils/rest/auth'; -import { updateProfile } from '../../utils/rest/updateProfile'; -import { session } from '../../models/session/Session'; +import { getMe } from "../../utils/rest/auth"; +import { updateProfile } from "../../utils/rest/updateProfile"; +import { session } from "../../models/session/Session"; -import { GenderEnum, UserProfile } from '../../utils/openapi'; -import { api } from '../../utils/constants'; -import { store } from '../../models/store'; -import { User } from '../../models/session/User'; +import { GenderEnum, UserProfile } from "../../utils/openapi"; +import { api, headersWithToken as headers } from "../../utils/constants"; +import { store } from "../../models/store"; +import { User } from "../../models/session/User"; export const useModel = () => { const navigate = useNavigate(); + const user = store.session.user; + console.log(user); const model = useLocalObservable(() => { return { - firstName: '', - birthdate: '', - gender: 'Male' as GenderEnum | null, - avatar: '', + firstName: "", + birthdate: "", + calculatedBirthday: "", + gender: "Male" as GenderEnum | null, + avatar: "", avatarFile: null as File | null, - previewAvatar: '', - country: '', - interest: '', - about: '', - errorFillOut1: { firstName: '', birthdate: '', avatar: '' }, - message: '', + previewAvatar: "", + country: "", + interest: "", + about: "", + errorFillOut1: { firstName: "", birthdate: "", avatar: "" }, + message: "", isLoading: false, isExportAvatarModal: false, isCreateAvatarModal: false, + async handleCurrentUser() { + try { + const user = await getMe(); + + if (user) { + session.updateUser(user); + model.firstName = user.first_name ?? ""; + model.gender = user.gender ?? null; + model.birthdate = user.birth_date ?? ""; + model.avatar = user.avatar ?? ""; + } + } catch (error: any) { + model.message = error.message; + } + }, + handleModalClose() { model.isExportAvatarModal = false; model.isCreateAvatarModal = false; }, handleAvatarSelection(creationWay: string) { - if (creationWay === 'Загрузить') { + if (creationWay === "Загрузить") { model.isExportAvatarModal = true; } else { model.isCreateAvatarModal = true; @@ -49,12 +68,12 @@ export const useModel = () => { value, }: { name: - | 'firstName' - | 'birthdate' - | 'avatar' - | 'country' - | 'interest' - | 'about'; + | "firstName" + | "birthdate" + | "avatar" + | "country" + | "interest" + | "about"; value: string; }) { model[name] = value; @@ -77,7 +96,7 @@ export const useModel = () => { const file = event.currentTarget.files[0]; if (file) { model.handleValue({ - name: 'avatar', + name: "avatar", value: URL.createObjectURL(file), }); @@ -90,7 +109,7 @@ export const useModel = () => { }; reader.readAsDataURL(file); - console.log('imageBase64', model.avatarFile); + console.log("imageBase64", model.avatarFile); model.handleModalClose(); console.log(model.avatar); @@ -111,47 +130,48 @@ export const useModel = () => { event.preventDefault(); model.errorFillOut1 = { - firstName: '', - birthdate: '', - avatar: '', + firstName: "", + birthdate: "", + avatar: "", }; - if (model.firstName === '') { - model.errorFillOut1.firstName = 'Пожалуйста, введите Ваше имя'; + if (model.firstName === "") { + model.errorFillOut1.firstName = "Пожалуйста, введите Ваше имя"; } - if (model.birthdate === '') { - model.errorFillOut1.birthdate = 'Пожалуйста, введите дату рождения'; + if (model.birthdate === "") { + model.errorFillOut1.birthdate = "Пожалуйста, введите дату рождения"; } if ( - model.avatar === '' || - model.avatar === '../../images/fill-out-profile-export-avatar.png' + model.avatar === "" || + model.avatar === "../../images/fill-out-profile-export-avatar.png" ) { - model.errorFillOut1.avatar = 'Пожалуйста, выберете аватар'; + model.errorFillOut1.avatar = "Пожалуйста, выберете аватар"; } if ( - model.errorFillOut1.firstName !== '' || - model.errorFillOut1.birthdate !== '' || - model.errorFillOut1.avatar !== '' + model.errorFillOut1.firstName !== "" || + model.errorFillOut1.birthdate !== "" || + model.errorFillOut1.avatar !== "" ) { return; } - model.message = ''; + model.message = ""; model.isLoading = true; try { - const user = store.session.user; - - const getUpdateUser = await api.api.usersMePartialUpdate({ - first_name: model.firstName, - avatar: model.avatarFile, - birth_date: model.birthdate, - gender: model.gender, - }); + const getUpdateUser = await api.api.usersMePartialUpdate( + { + first_name: model.firstName, + avatar: model.avatarFile, + birth_date: model.birthdate, + gender: model.gender, + }, + { headers } + ); - if (getUpdateUser) { + if (getUpdateUser && user) { store.session.updateUser({ ...user, first_name: model.firstName, @@ -162,10 +182,10 @@ export const useModel = () => { } updateProfile({}); - navigate('/fill-out-2'); + navigate("/fill-out-2"); model.isLoading = false; } catch (error: any) { - console.log('fill-out-1 error:', error); + console.log("fill-out-1 error:", error); model.isLoading = false; } }, diff --git a/src/pages/MainPage/MainPage.tsx b/src/pages/MainPage/MainPage.tsx index 8c3fb23..c82850a 100644 --- a/src/pages/MainPage/MainPage.tsx +++ b/src/pages/MainPage/MainPage.tsx @@ -1,22 +1,22 @@ -import React, { useEffect, useState } from 'react'; -import { observer } from 'mobx-react-lite'; +import React, { useEffect, useState } from "react"; +import { observer } from "mobx-react-lite"; -import { api } from '../../utils/constants'; -import { Country } from '../../utils/openapi'; -import { Language } from '../../utils/openapi'; +import { api } from "../../utils/constants"; +import { Country } from "../../utils/openapi"; +import { Language } from "../../utils/openapi"; -import Card from '../../components/Card/Card'; -import Header from '../../components/Header/Header'; -import Categories from '../../components/Categories/Categories'; -import Sort from '../../components/Sort/Sort'; -import Footer from '../../components/Footer/Footer'; +import Card from "../../components/Card/Card"; +import Header from "../../components/Header/Header"; +import Categories from "../../components/Categories/Categories"; +import Sort from "../../components/Sort/Sort"; +import Footer from "../../components/Footer/Footer"; -import MoreCards from '../../components/MoreCards/MoreCards'; +import MoreCards from "../../components/MoreCards/MoreCards"; -import styles from './MainPage.module.scss'; -import cn from 'classnames'; +import styles from "./MainPage.module.scss"; +import cn from "classnames"; -import { loggedIn } from '../../models/LoggedIn'; +import { loggedIn } from "../../models/LoggedIn"; const MainPage = () => { useEffect(() => { @@ -26,7 +26,7 @@ const MainPage = () => { const [usersList, setUsersList] = useState([]); const [cardsListLength, setCardsListLength] = useState(0); const [isUsersList, setIsUsersList] = useState(false); - const [category, setCategory] = useState({ name: 'Все', path: '' }); + const [category, setCategory] = useState({ name: "Все", path: "" }); const [sortType, setSortType] = useState({}); const [filters, setFilters] = useState({}); const [isSortPopupOpen, setSortPopupOpen] = useState(false); @@ -37,12 +37,12 @@ const MainPage = () => { const getUsersList = async (filters: any) => { try { - console.log('отправка запроса ---'); + console.log("отправка запроса ---"); const response = await api.api.usersList({ ordering: `${category.path}`, ...filters, }); - console.log('ответ получен -', response); + console.log("ответ получен -", response); setIsUsersList(true); if (response.data && response.data.results) { @@ -50,7 +50,7 @@ const MainPage = () => { console.log(response.data.results); } } catch (error) { - console.error('Ошибка при получении данных -', error); + console.error("Ошибка при получении данных -", error); setIsUsersList(false); } }; @@ -82,7 +82,7 @@ const MainPage = () => { className={cn( styles.content__cardListAndSortPopup_cardListArea_cardList, isSortPopupOpen && - styles.content__cardListAndSortPopup_cardListArea_cardList_narrow, + styles.content__cardListAndSortPopup_cardListArea_cardList_narrow )} > {isUsersList && diff --git a/src/pages/NotFoundPage/NotFoundPage.tsx b/src/pages/NotFoundPage/NotFoundPage.tsx index 4401487..2d7da27 100644 --- a/src/pages/NotFoundPage/NotFoundPage.tsx +++ b/src/pages/NotFoundPage/NotFoundPage.tsx @@ -1,8 +1,8 @@ -import React from 'react'; -import { Link, useNavigate } from 'react-router-dom'; -import Logo from '../../images/svg/logo.svg'; +import React from "react"; +import { Link, useNavigate } from "react-router-dom"; +import Logo from "../../images/svg/logo.svg"; -import './NotFoundPage.module.scss'; +import "./NotFoundPage.module.scss"; const NotFoundPage = () => { const navigate = useNavigate(); @@ -10,16 +10,16 @@ const NotFoundPage = () => { navigate(-1); } return ( -
-

404

-

+

+

404

+

Увы, такой страницы нет

- - logo + + logo

Назад diff --git a/src/pages/PolicyPage/PolicyPage.tsx b/src/pages/PolicyPage/PolicyPage.tsx index 6ad7aab..353da1a 100644 --- a/src/pages/PolicyPage/PolicyPage.tsx +++ b/src/pages/PolicyPage/PolicyPage.tsx @@ -1,9 +1,9 @@ -import { useEffect, useState } from 'react'; -import Header from '../../components/Header/Header'; -import Footer from '../../components/Footer/Footer'; +import { useEffect, useState } from "react"; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; -import styles from './PolicyPage.module.scss'; -import IProps from '../../types/types'; +import styles from "./PolicyPage.module.scss"; +import IProps from "../../types/types"; const PolicyPage = () => { return ( diff --git a/src/pages/RequireAuth/RequireAuth.tsx b/src/pages/RequireAuth/RequireAuth.tsx index 549b63e..3ae2082 100644 --- a/src/pages/RequireAuth/RequireAuth.tsx +++ b/src/pages/RequireAuth/RequireAuth.tsx @@ -1,13 +1,13 @@ -import { Navigate, Outlet } from 'react-router-dom'; -import { observer } from 'mobx-react-lite'; +import { Navigate, Outlet } from "react-router-dom"; +import { observer } from "mobx-react-lite"; -import { useModel } from './model'; +import { useModel } from "./model"; export const RequireAuth = observer(() => { const model = useModel(); if (!model.isAuthenticated) { - return ; + return ; } return ; diff --git a/src/pages/RequireAuth/model.ts b/src/pages/RequireAuth/model.ts index 52e8ed9..dd03822 100644 --- a/src/pages/RequireAuth/model.ts +++ b/src/pages/RequireAuth/model.ts @@ -1,7 +1,7 @@ -import { useLocalObservable } from 'mobx-react-lite'; -import { useLocation } from 'react-router'; +import { useLocalObservable } from "mobx-react-lite"; +import { useLocation } from "react-router"; -import { useStore } from '../../components/App/providers/withStore'; +import { useStore } from "../../components/App/providers/withStore"; export const useModel = () => { const store = useStore(); diff --git a/src/pages/ReviewsPage/ReviewsPage.tsx b/src/pages/ReviewsPage/ReviewsPage.tsx index f837806..b5b2ac0 100644 --- a/src/pages/ReviewsPage/ReviewsPage.tsx +++ b/src/pages/ReviewsPage/ReviewsPage.tsx @@ -1,8 +1,8 @@ -import { useEffect, useState } from 'react'; -import Header from '../../components/Header/Header'; -import Footer from '../../components/Footer/Footer'; +import { useEffect, useState } from "react"; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; -import styles from './ReviewsPage.module.scss'; +import styles from "./ReviewsPage.module.scss"; const ReviewsPage = () => { return ( diff --git a/src/pages/Routing.tsx b/src/pages/Routing.tsx index 77e3a2c..c9f17ab 100644 --- a/src/pages/Routing.tsx +++ b/src/pages/Routing.tsx @@ -1,58 +1,58 @@ -import { lazy } from 'react'; -import { Route, Routes } from 'react-router-dom'; +import { lazy } from "react"; +import { Route, Routes } from "react-router-dom"; -import { RequireAuth } from './RequireAuth/RequireAuth'; +import { RequireAuth } from "./RequireAuth/RequireAuth"; -const SignInPage = lazy(() => import('./SignupSigninPage/SignupSigninPage')); -const SignUpPage = lazy(() => import('./SignupSigninPage/SignupSigninPage')); -const MainPage = lazy(() => import('./MainPage/MainPage')); -const NotFoundPage = lazy(() => import('./NotFoundPage/NotFoundPage')); -const FAQPage = lazy(() => import('./FAQPage/FAQPage')); -const ReviewsPage = lazy(() => import('./ReviewsPage/ReviewsPage')); -const PolicyPage = lazy(() => import('./PolicyPage/PolicyPage')); -const RulesPage = lazy(() => import('./RulesPage/RulesPage')); -const AgreementPage = lazy(() => import('./AgreementPage/AgreementPage')); -const ProfilePage = lazy(() => import('./UserProfile/UserProfile')); +const SignInPage = lazy(() => import("./SignupSigninPage/SignupSigninPage")); +const SignUpPage = lazy(() => import("./SignupSigninPage/SignupSigninPage")); +const MainPage = lazy(() => import("./MainPage/MainPage")); +const NotFoundPage = lazy(() => import("./NotFoundPage/NotFoundPage")); +const FAQPage = lazy(() => import("./FAQPage/FAQPage")); +const ReviewsPage = lazy(() => import("./ReviewsPage/ReviewsPage")); +const PolicyPage = lazy(() => import("./PolicyPage/PolicyPage")); +const RulesPage = lazy(() => import("./RulesPage/RulesPage")); +const AgreementPage = lazy(() => import("./AgreementPage/AgreementPage")); +const ProfilePage = lazy(() => import("./UserProfile/UserProfile")); const FillOutProfilePage1 = lazy( - () => import('./FillOutProfilePages/FillOutProfilePage1'), + () => import("./FillOutProfilePages/FillOutProfilePage1") ); const FillOutProfilePage2 = lazy( - () => import('./FillOutProfilePages/FillOutProfilePage2'), + () => import("./FillOutProfilePages/FillOutProfilePage2") ); const FillOutProfilePage3 = lazy( - () => import('./FillOutProfilePages/FillOutProfilePage3'), + () => import("./FillOutProfilePages/FillOutProfilePage3") ); const FillOutProfilePage4 = lazy( - () => import('./FillOutProfilePages/FillOutProfilePage4'), + () => import("./FillOutProfilePages/FillOutProfilePage4") ); const FillOutProfilePage5 = lazy( - () => import('./FillOutProfilePages/FillOutProfilePage5'), + () => import("./FillOutProfilePages/FillOutProfilePage5") ); const FillOutProfilePage6 = lazy( - () => import('./FillOutProfilePages/FillOutProfilePage6'), + () => import("./FillOutProfilePages/FillOutProfilePage6") ); export const Routing = () => { return ( {/*}>*/} - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> {/**/} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> ); }; diff --git a/src/pages/RulesPage/RulesPage.tsx b/src/pages/RulesPage/RulesPage.tsx index 7912c3e..87814d4 100644 --- a/src/pages/RulesPage/RulesPage.tsx +++ b/src/pages/RulesPage/RulesPage.tsx @@ -1,7 +1,7 @@ -import Header from '../../components/Header/Header'; -import Footer from '../../components/Footer/Footer'; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; -import styles from './RulesPage.module.scss'; +import styles from "./RulesPage.module.scss"; const RulesPage = () => { return ( diff --git a/src/pages/SignupSigninPage/SignupSigninPage.tsx b/src/pages/SignupSigninPage/SignupSigninPage.tsx index 9c2e6fb..cb37b85 100644 --- a/src/pages/SignupSigninPage/SignupSigninPage.tsx +++ b/src/pages/SignupSigninPage/SignupSigninPage.tsx @@ -1,15 +1,15 @@ -import { Link } from 'react-router-dom'; -import { observer } from 'mobx-react-lite'; +import { Link } from "react-router-dom"; +import { observer } from "mobx-react-lite"; -import SignupSigninForm from '../../components/SignupSigninForm/SignupSigninForm'; -import PicturesBlock from '../../components/PicturesBlock/PicturesBlock'; +import SignupSigninForm from "../../components/SignupSigninForm/SignupSigninForm"; +import PicturesBlock from "../../components/PicturesBlock/PicturesBlock"; -import logo from '../../images/svg/logo.svg'; +import logo from "../../images/svg/logo.svg"; -import styles from './SignupSigninPage.module.scss'; -import { useEffect } from 'react'; +import styles from "./SignupSigninPage.module.scss"; +import { useEffect } from "react"; -import { session } from '../../models/session/Session'; +import { session } from "../../models/session/Session"; const SignupSigninPage = () => { useEffect(() => { @@ -24,7 +24,7 @@ const SignupSigninPage = () => { Логотип проекта

diff --git a/src/pages/UserProfile/About/About.tsx b/src/pages/UserProfile/About/About.tsx index 345688d..3ab70a1 100644 --- a/src/pages/UserProfile/About/About.tsx +++ b/src/pages/UserProfile/About/About.tsx @@ -1,5 +1,5 @@ -import React, { FC } from 'react'; -import styles from './About.module.scss'; +import React, { FC } from "react"; +import styles from "./About.module.scss"; interface AboutProps { isEditing: boolean; @@ -29,15 +29,15 @@ const About: FC = ({ isEditing, about, setAboutMe }) => { onChange={(event) => setAboutMe(event.target.value)} className={styles.about__input} rows={3} - placeholder='Напиши несколько предложений о себе, чтобы тебя нашли партнеры со схожими интересами или стилем жизни' + placeholder="Напиши несколько предложений о себе, чтобы тебя нашли партнеры со схожими интересами или стилем жизни" />

Я изучаю язык, чтобы

diff --git a/src/pages/UserProfile/Buttons/EditButton/EditButton.tsx b/src/pages/UserProfile/Buttons/EditButton/EditButton.tsx index 043689c..b4b2b3b 100644 --- a/src/pages/UserProfile/Buttons/EditButton/EditButton.tsx +++ b/src/pages/UserProfile/Buttons/EditButton/EditButton.tsx @@ -1,5 +1,5 @@ -import icon from '../../../../images/userProfile/edit.png'; -import styles from './EditButton.module.scss'; +import icon from "../../../../images/userProfile/edit.png"; +import styles from "./EditButton.module.scss"; const EditButton = () => { const handleButtonClick = () => { @@ -9,7 +9,7 @@ const EditButton = () => { return ( <> diff --git a/src/pages/UserProfile/Buttons/IconButton/IconButton.tsx b/src/pages/UserProfile/Buttons/IconButton/IconButton.tsx index f4ccc08..6030eb9 100644 --- a/src/pages/UserProfile/Buttons/IconButton/IconButton.tsx +++ b/src/pages/UserProfile/Buttons/IconButton/IconButton.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import IconButtonProps from '../../../../types/userProfile/types'; -import styles from './IconButton.module.scss'; +import React from "react"; +import IconButtonProps from "../../../../types/userProfile/types"; +import styles from "./IconButton.module.scss"; const IconButton: React.FC = ({ icon, @@ -14,7 +14,7 @@ const IconButton: React.FC = ({ кликабельная иконка diff --git a/src/pages/UserProfile/Certificates/Certificates.tsx b/src/pages/UserProfile/Certificates/Certificates.tsx index 490692f..60e4dcf 100644 --- a/src/pages/UserProfile/Certificates/Certificates.tsx +++ b/src/pages/UserProfile/Certificates/Certificates.tsx @@ -1,4 +1,4 @@ -import styles from './Certificates.module.scss'; +import styles from "./Certificates.module.scss"; const Certificates = () => { return ( diff --git a/src/pages/UserProfile/LevelLanguage/LevelLanguage.tsx b/src/pages/UserProfile/LevelLanguage/LevelLanguage.tsx index 4cf29f5..b6a71bd 100644 --- a/src/pages/UserProfile/LevelLanguage/LevelLanguage.tsx +++ b/src/pages/UserProfile/LevelLanguage/LevelLanguage.tsx @@ -1,4 +1,4 @@ -import styles from './LevelLanguage.module.scss'; +import styles from "./LevelLanguage.module.scss"; const LevelLanguage = () => { return
; diff --git a/src/pages/UserProfile/Reviews/Reviews.tsx b/src/pages/UserProfile/Reviews/Reviews.tsx index a19f534..1f31bb6 100644 --- a/src/pages/UserProfile/Reviews/Reviews.tsx +++ b/src/pages/UserProfile/Reviews/Reviews.tsx @@ -1,4 +1,4 @@ -import styles from './Reviews.module.scss'; +import styles from "./Reviews.module.scss"; const Reviews = () => { return ( diff --git a/src/pages/UserProfile/Topics/Topics.tsx b/src/pages/UserProfile/Topics/Topics.tsx index 4131f01..64d1aa8 100644 --- a/src/pages/UserProfile/Topics/Topics.tsx +++ b/src/pages/UserProfile/Topics/Topics.tsx @@ -1,8 +1,8 @@ -import React, { useState } from 'react'; -import IconButton from '../Buttons/IconButton/IconButton'; -import search from '../../../images/userProfile/search.svg'; -import control from '../../../images/userProfile/control.svg'; -import styles from './Topics.module.scss'; +import React, { useState } from "react"; +import IconButton from "../Buttons/IconButton/IconButton"; +import search from "../../../images/userProfile/search.svg"; +import control from "../../../images/userProfile/control.svg"; +import styles from "./Topics.module.scss"; interface TopicsProps { isEditing: boolean; @@ -18,19 +18,19 @@ const Topics: React.FC = ({ const [editedInterests, setEditedInterests] = useState([ ...interests, ]); - const [inputValue, setInputValue] = useState(''); + const [inputValue, setInputValue] = useState(""); const handleSetThemes = (event: React.ChangeEvent) => { setInputValue(event.target.value); }; const handleAddTheme = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { + if (event.key === "Enter") { const newTheme = inputValue.trim(); - if (newTheme !== '') { + if (newTheme !== "") { setEditedInterests((prevInterests) => [...prevInterests, newTheme]); setEditedData([...interests, newTheme]); - setInputValue(''); + setInputValue(""); } } }; @@ -59,14 +59,14 @@ const Topics: React.FC = ({
Поиск = ({ }) => { const handleChangeString = ( setState: React.Dispatch>, - value: string, + value: string ) => { setState(value); }; @@ -83,7 +83,7 @@ const UserCard: FC = ({ Аватар пользователя
@@ -94,12 +94,12 @@ const UserCard: FC = ({
- {location && typeof location === 'object' ? ( + {location && typeof location === "object" ? ( <> Флаг страны пользователя

{location.code}

@@ -112,7 +112,7 @@ const UserCard: FC = ({ изображение часов с циферблатом

{formattedTime} @@ -132,7 +132,7 @@ const UserCard: FC = ({ Аватар пользователя

= ({
- Button 1 + Button 1
- + @@ -167,22 +167,22 @@ const UserCard: FC = ({ setName as React.Dispatch< React.SetStateAction >, - event.target.value, + event.target.value ) } />
@@ -190,37 +190,37 @@ const UserCard: FC = ({
- +
setGender(GenderEnum.Male)} /> setGender(GenderEnum.Female)} /> = ({
handleChangeString( setLocation as React.Dispatch< React.SetStateAction >, - event.target.value, + event.target.value ) } /> diff --git a/src/pages/UserProfile/UserLanguages/UserLanguages.tsx b/src/pages/UserProfile/UserLanguages/UserLanguages.tsx index d56147c..f6faf8c 100644 --- a/src/pages/UserProfile/UserLanguages/UserLanguages.tsx +++ b/src/pages/UserProfile/UserLanguages/UserLanguages.tsx @@ -1,8 +1,8 @@ -import LevelLanguage from '../LevelLanguage/LevelLanguage'; -import help from '../../../images/userProfile/help.png'; -import IconButton from '../Buttons/IconButton/IconButton'; -import styles from './UserLanguages.module.scss'; -import add from '../../../images/userProfile/plus.svg'; +import LevelLanguage from "../LevelLanguage/LevelLanguage"; +import help from "../../../images/userProfile/help.png"; +import IconButton from "../Buttons/IconButton/IconButton"; +import styles from "./UserLanguages.module.scss"; +import add from "../../../images/userProfile/plus.svg"; interface UserLanguagesProps { isEditing: boolean; @@ -57,7 +57,7 @@ const UserLanguages: React.FC = ({ Флаг страны пользователя
) : ( @@ -109,7 +109,7 @@ const UserLanguages: React.FC = ({ Флаг страны пользователя
)} diff --git a/src/pages/UserProfile/UserProfile.tsx b/src/pages/UserProfile/UserProfile.tsx index c1c8f17..e42f793 100644 --- a/src/pages/UserProfile/UserProfile.tsx +++ b/src/pages/UserProfile/UserProfile.tsx @@ -1,45 +1,43 @@ -import { useState, useEffect } from 'react'; -// import { observer } from "mobx-react-lite"; -import { useModel } from '../../components/SignupSigninForm/model'; -import { api } from '../../utils/constants'; +import { useState, useEffect } from "react"; +import { api } from "../../utils/constants"; import { UserLanguage, SkillLevelEnum, GenderEnum, NullEnum, - Country, - Goal, -} from '../../utils/openapi'; -import Header from '../../components/Header/Header'; -import Footer from '../../components/Footer/Footer'; -import UserCard from './UserCard/UserCard'; -import IconButton from './Buttons/IconButton/IconButton'; -import UserLanguages from './UserLanguages/UserLanguages'; -import Topics from './Topics/Topics'; -import About from './About/About'; -import Reviews from './Reviews/Reviews'; -import Certificates from './Certificates/Certificates'; -import settings from '../../images/userProfile/settings.png'; -import edit from '../../images/userProfile/edit.png'; -import styles from './UserProfile.module.scss'; +} from "../../utils/openapi"; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; +import UserCard from "./UserCard/UserCard"; +import IconButton from "./Buttons/IconButton/IconButton"; +import UserLanguages from "./UserLanguages/UserLanguages"; +import Topics from "./Topics/Topics"; +import About from "./About/About"; +import Reviews from "./Reviews/Reviews"; +import Certificates from "./Certificates/Certificates"; +import settings from "../../images/userProfile/settings.png"; +import edit from "../../images/userProfile/edit.png"; +import styles from "./UserProfile.module.scss"; interface UserData { - first_name: string; - gender: GenderEnum | NullEnum | null; - country: Country | null; + username: string; + first_name?: string; avatar?: string | null; - interests: string[]; - languages: UserLanguage[]; - about: string; age: string; - username: string; slug: string | null; - goals: Goal[]; + country?: string | null; + languages?: UserLanguage[]; + gender?: GenderEnum | NullEnum | null; + goals?: string[]; + interests?: string[]; + about?: string; last_activity: string | null; is_online: boolean; gender_is_hidden: boolean; age_is_hidden: boolean; role: string; + is_blocked: boolean; + birth_date?: string | null; } interface Language { @@ -50,7 +48,7 @@ interface Language { interface EditedData { first_name: string; username: string; - avatar?: string | null; + avatar?: File | null; country: string | null; birth_date: string; languages: Language[]; @@ -66,27 +64,25 @@ const UserProfile: React.FC = () => { const [isLoading, setIsLoading] = useState(true); const [isEditing, setIsEditing] = useState(false); const [editedData, setEditedData] = useState({ - first_name: '', - avatar: '', - country: '', - birth_date: '', + first_name: "", + avatar: "", + country: "", + birth_date: "", languages: [], gender: null, goals: [], interests: [], - about: '', - username: '', - age: '', + about: "", + username: "", + age: "", }); - const model = useModel(); const [imageBase64, setImageBase64] = useState(null); const handleFileInputChange = ( - event: React.ChangeEvent, + event: React.ChangeEvent ) => { const file = event.target.files?.[0]; if (file) { - // eslint-disable-next-line no-undef const reader = new FileReader(); reader.onload = () => { const base64Data = reader.result as null; @@ -97,17 +93,14 @@ const UserProfile: React.FC = () => { }; reader.readAsDataURL(file); } - // eslint-disable-next-line no-undef - console.log('imageBase64', imageBase64); + console.log("imageBase64", imageBase64); }; const fetchUserData = async () => { try { - // eslint-disable-next-line no-undef - const token = localStorage.getItem('accessToken'); + const token = localStorage.getItem("accessToken"); if (!token) { - // eslint-disable-next-line no-undef - console.error('Токен не найден'); + console.error("Токен не найден"); setIsLoading(false); return; } @@ -116,13 +109,11 @@ const UserProfile: React.FC = () => { Authorization: `Bearer ${token}`, }, }); - // eslint-disable-next-line no-undef - console.log('Успех', response.data); + console.log("Успех", response.data); setUserData(response.data); setIsLoading(false); } catch (error) { - // eslint-disable-next-line no-undef - console.error('Ошибка при получении данных пользователя:', error); + console.error("Ошибка при получении данных пользователя:", error); setIsLoading(false); } }; @@ -131,22 +122,17 @@ const UserProfile: React.FC = () => { fetchUserData(); }, []); - useEffect(() => { - // eslint-disable-next-line no-undef - console.log(`userPage: ${model.user}`); - }, [model.user]); - const handleEditProfile = () => { if (userData) { setEditedData({ ...userData, first_name: editedData.username, - avatar: editedData.avatar || userData.avatar, + avatar: editedData.avatar, country: editedData.country, birth_date: userData.age, languages: [ { - isocode: 'En', + isocode: "En", skill_level: SkillLevelEnum.Newbie, }, ], @@ -167,29 +153,32 @@ const UserProfile: React.FC = () => { const handleSaveChanges = async () => { try { - // eslint-disable-next-line no-undef - const token = localStorage.getItem('accessToken'); + const token = localStorage.getItem("accessToken"); if (!token) { - // eslint-disable-next-line no-undef - console.error('Токен не найден'); + console.error("Токен не найден"); return; } - const updatedUserData = { ...userData, ...editedData }; + const updatedUserData: EditedData = { + ...userData, + ...editedData, + avatar: editedData.avatar ?? null, + }; + const response = await api.api.usersMePartialUpdate(updatedUserData, { headers: { Authorization: `Bearer ${token}`, }, }); - // eslint-disable-next-line no-undef - console.log('Успешно обновленные данные:', response.data); + + console.log("Успешно обновленные данные:", response.data); if (response.data !== null) { setUserData(response.data); } setIsEditing(false); } catch (error) { // eslint-disable-next-line no-undef - console.error('Ошибка при сохранении данных:', error); + console.error("Ошибка при сохранении данных:", error); } }; @@ -197,7 +186,7 @@ const UserProfile: React.FC = () => { return
Loading...
; } - console.log('editedData', editedData); + console.log("editedData", editedData); return ( <> @@ -213,11 +202,11 @@ const UserProfile: React.FC = () => { userData?.gender || GenderEnum.Male || GenderEnum.Female || null } location={ - typeof editedData.country === 'string' + typeof editedData.country === "string" ? editedData.country - : userData?.country?.code || '' + : userData?.country?.code || "" } - avatar={userData?.avatar || ''} + avatar={userData?.avatar || ""} handleFileInputChange={handleFileInputChange} setName={(value) => setEditedData((prevData) => ({ ...prevData, username: value })) @@ -289,13 +278,13 @@ const UserProfile: React.FC = () => {
setEditedData((prevData) => ({ @@ -348,7 +337,7 @@ const UserProfile: React.FC = () => {
setEditedData((prevData) => ({ ...prevData, about: value })) } diff --git a/src/types/declarations.d.ts b/src/types/declarations.d.ts index c958c05..f58d3c2 100644 --- a/src/types/declarations.d.ts +++ b/src/types/declarations.d.ts @@ -1,26 +1,26 @@ -declare module '*.scss' { +declare module "*.scss" { const content: { [className: string]: string }; export = content; } -declare module '*.css' { +declare module "*.css" { const content: { [className: string]: string }; export = content; } -declare module '*.svg' { +declare module "*.svg" { const content: string; export default content; } -declare module '*.jpg' { +declare module "*.jpg" { const content: string; export default content; } -declare module '*.png' { +declare module "*.png" { const content: string; export default content; } -declare module 'react-dom/client'; +declare module "react-dom/client"; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 6b21364..9ae8ca9 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,19 +1,19 @@ -import { Api } from './openapi'; +import { Api } from "./openapi"; export const apiConfig = { - baseUrl: 'https://lingvogo.acceleratorpracticum.ru', + baseUrl: "https://lingvogo.acceleratorpracticum.ru", }; export const api = new Api(apiConfig); const array = [ - 'Русская культура', - 'Путешествия', - 'Дизайн', - 'Кулинария', - 'Книги', - 'Языки', - 'Спорт', + "Русская культура", + "Путешествия", + "Дизайн", + "Кулинария", + "Книги", + "Языки", + "Спорт", ]; export default array; @@ -25,62 +25,62 @@ interface IQuestion { } export const headersWithToken = { - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + `${localStorage.getItem('accessToken')}`, + "Content-Type": "application/json", + Authorization: "Bearer " + `${localStorage.getItem("accessToken")}`, }; export const FAQArray: IQuestion[] = [ { - question: 'Где это я?', + question: "Где это я?", text: [ - 'Вы - на языковой платформе LinguaChat, созданной для того, чтобы учить языки вместе. Здесь делятся знаниями, общаются и повышают свои разговорные навыки.', + "Вы - на языковой платформе LinguaChat, созданной для того, чтобы учить языки вместе. Здесь делятся знаниями, общаются и повышают свои разговорные навыки.", ], id: 1, }, { - question: 'И что здесь можно делать?', + question: "И что здесь можно делать?", text: [ - 'На LinguaChat вы можете найти партнеров для изучения языка, переписываться с ними в локальных и групповых чатах, обмениваться учебными материалами. Общаться с людьми, заводить друзей, делиться книгами и ссылками, познакомиться с разными культурами и открыть для кого-то свою, повышать свой уровень и отслеживать прогресс.', + "На LinguaChat вы можете найти партнеров для изучения языка, переписываться с ними в локальных и групповых чатах, обмениваться учебными материалами. Общаться с людьми, заводить друзей, делиться книгами и ссылками, познакомиться с разными культурами и открыть для кого-то свою, повышать свой уровень и отслеживать прогресс.", ], id: 2, }, { - question: 'В чём ваша фишка?', + question: "В чём ваша фишка?", text: [ - 'У нас их три)', - 'Первая — общение с носителями (а не с учителями) или с другими людьми, которым интересен тот же язык, что и вам. Научно доказано, что изучение иностранных языков в малых группах и в парах повышает эффективность - так вы продвинетесь в выбранном языке быстрее и дальше, не теряя заинтересованности и мотивации.', - 'Вторая — формат. Здесь вы не занимаетесь скучной зубрежкой - вы отдыхаете и общаетесь с приятными людьми, пока ваш уровень владения языком и уверенность в себе растут)', - 'Третья — наша команда разработчиков. Мы стремимся создать удобную и современную платформу для наших пользователей и пользуемся ей сами, восприимчивы к рекомендациям и критике, а также регулярно изучаем платформы наших коллег, чтобы заимствовать у конкурентов лучшие практики и сделать все, чтобы на нашей платформе вам было удобно и приятно.', + "У нас их три)", + "Первая — общение с носителями (а не с учителями) или с другими людьми, которым интересен тот же язык, что и вам. Научно доказано, что изучение иностранных языков в малых группах и в парах повышает эффективность - так вы продвинетесь в выбранном языке быстрее и дальше, не теряя заинтересованности и мотивации.", + "Вторая — формат. Здесь вы не занимаетесь скучной зубрежкой - вы отдыхаете и общаетесь с приятными людьми, пока ваш уровень владения языком и уверенность в себе растут)", + "Третья — наша команда разработчиков. Мы стремимся создать удобную и современную платформу для наших пользователей и пользуемся ей сами, восприимчивы к рекомендациям и критике, а также регулярно изучаем платформы наших коллег, чтобы заимствовать у конкурентов лучшие практики и сделать все, чтобы на нашей платформе вам было удобно и приятно.", ], id: 3, }, { - question: 'Как мне найти собеседника?', + question: "Как мне найти собеседника?", text: [ - 'На главной странице сайта под верхним логотипом есть кнопка поиска собеседника. Нажмите её, укажите в фильтре нужные вам параметры и начните.', + "На главной странице сайта под верхним логотипом есть кнопка поиска собеседника. Нажмите её, укажите в фильтре нужные вам параметры и начните.", ], id: 4, }, { - question: 'Как мне начать разговор с другим пользователем?', + question: "Как мне начать разговор с другим пользователем?", text: [ - 'Наша платформа предоставляет вам удобную систему фильтров: сперва вы выбираете в ней нужный язык, а затем подбираете себе собеседника на свой вкус. Напишите тому, кто вам понравится, что-нибудь доброе и веселое - если он заинтересуется, то ответит вам. Рекомендация: заполненный профиль пользователя, где ваш собеседник сможет ознакомиться с информацией о вас, значительно повысит шансы.', + "Наша платформа предоставляет вам удобную систему фильтров: сперва вы выбираете в ней нужный язык, а затем подбираете себе собеседника на свой вкус. Напишите тому, кто вам понравится, что-нибудь доброе и веселое - если он заинтересуется, то ответит вам. Рекомендация: заполненный профиль пользователя, где ваш собеседник сможет ознакомиться с информацией о вас, значительно повысит шансы.", ], id: 5, }, { - question: 'А если я не хочу продолжать взаимодействие?', + question: "А если я не хочу продолжать взаимодействие?", text: [ - 'Можете отказаться от общения, перестать отвечать и даже заблокировать пользователя.', - 'Рекомендуем сперва написать ему - например, сообщить, что в силу занятости вы не сможете продолжить занятия или станете появляться реже - и попрощаться.', + "Можете отказаться от общения, перестать отвечать и даже заблокировать пользователя.", + "Рекомендуем сперва написать ему - например, сообщить, что в силу занятости вы не сможете продолжить занятия или станете появляться реже - и попрощаться.", ], id: 6, }, { question: - 'Могу ли я делиться обучающими материалами и книгами прямо на платформе?', + "Могу ли я делиться обучающими материалами и книгами прямо на платформе?", text: [ - 'Да, вы можете обмениваться файлами и ссылками. Пожалуйста, не нарушайте авторские права)', + "Да, вы можете обмениваться файлами и ссылками. Пожалуйста, не нарушайте авторские права)", ], id: 7, }, diff --git a/src/utils/getTime.js b/src/utils/getTime.ts similarity index 78% rename from src/utils/getTime.js rename to src/utils/getTime.ts index a943d09..ff09a39 100644 --- a/src/utils/getTime.js +++ b/src/utils/getTime.ts @@ -1,14 +1,14 @@ const currentTime = new Date(); let hours = currentTime.getHours(); let minutes = currentTime.getMinutes(); -let amOrPm = hours >= 12 ? 'PM' : 'AM'; +const amOrPm = hours >= 12 ? "PM" : "AM"; if (hours > 12) { hours -= 12; } if (minutes < 10) { - minutes = '0' + minutes; + minutes = "0" + minutes; } const formattedTime = `${hours}:${minutes} ${amOrPm}`; diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 498dcb7..350b86b 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -19,6 +19,7 @@ export interface Chat { initiator: UserShort; receiver: UserShort; messages: Message[]; + blocked_users: string[]; } /** Сериализатор для просмотра списка чатов. */ @@ -30,6 +31,11 @@ export interface ChatList { unread: string; } +/** Сериализатор для просмотра чата. */ +export interface ChatRequest { + blocked_users: string[]; +} + /** Сериализатор для создания личного чата. */ export interface ChatStart { /** @@ -99,6 +105,27 @@ export interface Goal { icon: string; } +/** Сериализатор для создания группового чата. */ +export interface GroupChatCreate { + /** + * Название + * @maxLength 128 + */ + name?: string; + initiator: UserShort; + members: (string | null)[]; +} + +/** Сериализатор для создания группового чата. */ +export interface GroupChatCreateRequest { + /** + * Название + * @maxLength 128 + */ + name?: string; + members: (string | null)[]; +} + export interface Interest { /** Название */ name: string; @@ -258,12 +285,6 @@ export interface PatchedUserProfileRequest { * @minLength 1 */ country?: string | null; - /** - * Дата рождения - * Дата рождения пользователя - * @format date - */ - birth_date?: string | null; languages?: UserLanguageRequest[]; /** * Пол @@ -281,6 +302,63 @@ export interface PatchedUserProfileRequest { * @maxLength 256 */ about?: string; + /** + * Дата рождения + * Дата рождения пользователя + * @format date + */ + birth_date?: string | null; +} + +/** + * * `pornography` - Порнография + * * `spam` - Рассылка спама + * * `fraud` - Мошенничество + * * `offensive_behavior` - Оскорбительное поведение + * * `copyright_violation` - Нарушение авторских прав + */ +export enum ReasonEnum { + Pornography = "pornography", + Spam = "spam", + Fraud = "fraud", + OffensiveBehavior = "offensive_behavior", + CopyrightViolation = "copyright_violation", +} + +/** Сериализатор жалоб */ +export interface Report { + /** + * Причина жалобы + * Выберите причину данной жалобы. + * + * * `pornography` - Порнография + * * `spam` - Рассылка спама + * * `fraud` - Мошенничество + * * `offensive_behavior` - Оскорбительное поведение + * * `copyright_violation` - Нарушение авторских прав + */ + reason: ReasonEnum; + description?: string; + /** Закрыть пользователю доступ к моей странице */ + close_user_access: boolean; +} + +/** Сериализатор жалоб */ +export interface ReportRequest { + /** + * Причина жалобы + * Выберите причину данной жалобы. + * + * * `pornography` - Порнография + * * `spam` - Рассылка спама + * * `fraud` - Мошенничество + * * `offensive_behavior` - Оскорбительное поведение + * * `copyright_violation` - Нарушение авторских прав + */ + reason: ReasonEnum; + description?: string; + /** Закрыть пользователю доступ к моей странице */ + close_user_access: boolean; } export interface SetPassword { @@ -397,6 +475,8 @@ export interface UserLanguageRequest { /** Сериализатор для заполнения профиля пользователя. */ export interface UserProfile { + /** Логин */ + username: string; /** * Имя * Имя пользователя @@ -405,17 +485,18 @@ export interface UserProfile { first_name?: string; /** @format uri */ avatar?: string | null; + age: string; + /** + * Слаг + * Слаг + * @pattern ^[-a-zA-Z0-9_]+$ + */ + slug: string | null; /** * Код * Код страны */ country?: string | null; - /** - * Дата рождения - * Дата рождения пользователя - * @format date - */ - birth_date?: string | null; languages?: UserLanguage[]; /** * Пол @@ -433,6 +514,25 @@ export interface UserProfile { * @maxLength 256 */ about?: string; + /** + * Последняя активность + * Последнее время активности пользователя + * @format date-time + */ + last_activity: string | null; + is_online: boolean; + /** Поле для скрытия/отображения пола пользователя */ + gender_is_hidden: boolean; + /** Поле для скрытия/отображения возраста пользователя */ + age_is_hidden: boolean; + role: string; + is_blocked: boolean; + /** + * Дата рождения + * Дата рождения пользователя + * @format date + */ + birth_date?: string | null; } /** Сериализатор для просмотра пользователя. */ @@ -476,12 +576,13 @@ export interface UserRepr { * @format date-time */ last_activity: string | null; - is_online: string; + is_online: boolean; /** Поле для скрытия/отображения пола пользователя */ gender_is_hidden: boolean; /** Поле для скрытия/отображения возраста пользователя */ age_is_hidden: boolean; role: string; + is_blocked: boolean; } export interface UserShort { @@ -528,16 +629,22 @@ export interface FullRequestParams extends Omit { cancelToken?: CancelToken; } -export type RequestParams = Omit; +export type RequestParams = Omit< + FullRequestParams, + "body" | "method" | "query" | "path" +>; export interface ApiConfig { baseUrl?: string; baseApiParams?: Omit; - securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; + securityWorker?: ( + securityData: SecurityDataType | null + ) => Promise | RequestParams | void; customFetch?: typeof fetch; } -export interface HttpResponse extends Response { +export interface HttpResponse + extends Response { data: D; error: E; } @@ -556,7 +663,8 @@ export class HttpClient { private securityData: SecurityDataType | null = null; private securityWorker?: ApiConfig["securityWorker"]; private abortControllers = new Map(); - private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); + private customFetch = (...fetchParams: Parameters) => + fetch(...fetchParams); private baseApiParams: RequestParams = { credentials: "same-origin", @@ -575,7 +683,9 @@ export class HttpClient { protected encodeQueryParam(key: string, value: any) { const encodedKey = encodeURIComponent(key); - return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; + return `${encodedKey}=${encodeURIComponent( + typeof value === "number" ? value : `${value}` + )}`; } protected addQueryParam(query: QueryParamsType, key: string) { @@ -589,9 +699,15 @@ export class HttpClient { protected toQueryString(rawQuery?: QueryParamsType): string { const query = rawQuery || {}; - const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); + const keys = Object.keys(query).filter( + (key) => "undefined" !== typeof query[key] + ); return keys - .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) + .map((key) => + Array.isArray(query[key]) + ? this.addArrayQueryParam(query, key) + : this.addQueryParam(query, key) + ) .join("&"); } @@ -602,8 +718,13 @@ export class HttpClient { private contentFormatters: Record any> = { [ContentType.Json]: (input: any) => - input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, - [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), + input !== null && (typeof input === "object" || typeof input === "string") + ? JSON.stringify(input) + : input, + [ContentType.Text]: (input: any) => + input !== null && typeof input !== "string" + ? JSON.stringify(input) + : input, [ContentType.FormData]: (input: any) => Object.keys(input || {}).reduce((formData, key) => { const property = input[key]; @@ -613,14 +734,17 @@ export class HttpClient { ? property : typeof property === "object" && property !== null ? JSON.stringify(property) - : `${property}`, + : `${property}` ); return formData; }, new FormData()), [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), }; - protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { + protected mergeRequestParams( + params1: RequestParams, + params2?: RequestParams + ): RequestParams { return { ...this.baseApiParams, ...params1, @@ -633,7 +757,9 @@ export class HttpClient { }; } - protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { + protected createAbortSignal = ( + cancelToken: CancelToken + ): AbortSignal | undefined => { if (this.abortControllers.has(cancelToken)) { const abortController = this.abortControllers.get(cancelToken); if (abortController) { @@ -677,15 +803,28 @@ export class HttpClient { const payloadFormatter = this.contentFormatters[type || ContentType.Json]; const responseFormat = format || requestParams.format; - return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { - ...requestParams, - headers: { - ...(requestParams.headers || {}), - ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), - }, - signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null, - body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), - }).then(async (response) => { + return this.customFetch( + `${baseUrl || this.baseUrl || ""}${path}${ + queryString ? `?${queryString}` : "" + }`, + { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData + ? { "Content-Type": type } + : {}), + }, + signal: + (cancelToken + ? this.createAbortSignal(cancelToken) + : requestParams.signal) || null, + body: + typeof body === "undefined" || body === null + ? null + : payloadFormatter(body), + } + ).then(async (response) => { const r = response as HttpResponse; r.data = null as unknown as T; r.error = null as unknown as E; @@ -722,7 +861,9 @@ export class HttpClient { * * API endpoints for LinguaChat */ -export class Api extends HttpClient { +export class Api< + SecurityDataType extends unknown, +> extends HttpClient { api = { /** * @description Takes a set of user credentials and returns an access and refresh JSON web token pair to prove the authentication of those credentials. @@ -731,7 +872,10 @@ export class Api extends HttpClient + authJwtCreateCreate: ( + data: TokenObtainPairRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/auth/jwt/create/`, method: "POST", @@ -748,7 +892,10 @@ export class Api extends HttpClient + authJwtRefreshCreate: ( + data: TokenRefreshRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/auth/jwt/refresh/`, method: "POST", @@ -765,7 +912,10 @@ export class Api extends HttpClient + authJwtVerifyCreate: ( + data: TokenVerifyRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/auth/jwt/verify/`, method: "POST", @@ -794,7 +944,7 @@ export class Api extends HttpClient this.request({ path: `/api/v1/chats/`, @@ -823,6 +973,29 @@ export class Api extends HttpClient + this.request({ + path: `/api/v1/chats/${id}/block_user/`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + /** * @description Отправить сообщение в чат * @@ -832,7 +1005,11 @@ export class Api extends HttpClient + chatsSendMessageCreate: ( + id: number, + data: MessageRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/chats/${id}/send-message/`, method: "POST", @@ -843,6 +1020,51 @@ export class Api extends HttpClient + this.request({ + path: `/api/v1/chats/${id}/unblock_user/`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description Создание группового чата. + * + * @tags chats + * @name ChatsStartGroupChatCreate + * @request POST:/api/v1/chats/start-group-chat/ + * @secure + */ + chatsStartGroupChatCreate: ( + data: GroupChatCreateRequest, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/chats/start-group-chat/`, + method: "POST", + body: data, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + /** * @description Начать чат с пользователем, отправить первое сообщение * @@ -852,7 +1074,10 @@ export class Api extends HttpClient + chatsStartPersonalChatCreate: ( + data: ChatStartRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/chats/start-personal-chat/`, method: "POST", @@ -877,7 +1102,7 @@ export class Api extends HttpClient this.request({ path: `/api/v1/countries/`, @@ -922,7 +1147,7 @@ export class Api extends HttpClient this.request({ path: `/api/v1/goals/`, @@ -951,7 +1176,7 @@ export class Api extends HttpClient this.request({ path: `/api/v1/interests/`, @@ -976,7 +1201,7 @@ export class Api extends HttpClient this.request({ path: `/api/v1/languages/`, @@ -1053,9 +1278,15 @@ export class Api extends HttpClient this.request({ path: `/api/v1/users/`, @@ -1130,10 +1361,11 @@ export class Api extends HttpClient - this.request({ + this.request({ path: `/api/v1/users/${slug}/report_user/`, method: "GET", secure: true, + format: "json", ...params, }), @@ -1146,11 +1378,18 @@ export class Api extends HttpClient - this.request({ + usersReportUserCreate: ( + slug: string, + data: ReportRequest, + params: RequestParams = {} + ) => + this.request({ path: `/api/v1/users/${slug}/report_user/`, method: "POST", + body: data, secure: true, + type: ContentType.Json, + format: "json", ...params, }), @@ -1217,7 +1456,7 @@ export class Api extends HttpClient - this.request({ + this.request({ path: `/api/v1/users/me/`, method: "GET", secure: true, @@ -1234,7 +1473,10 @@ export class Api extends HttpClient + usersMePartialUpdate: ( + data: PatchedUserProfileRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/users/me/`, method: "PATCH", @@ -1271,7 +1513,10 @@ export class Api extends HttpClient + usersSetPasswordCreate: ( + data: SetPasswordRequest, + params: RequestParams = {} + ) => this.request({ path: `/api/v1/users/set_password/`, method: "POST", diff --git a/src/utils/rest/auth.ts b/src/utils/rest/auth.ts index bfce937..86d8fd6 100644 --- a/src/utils/rest/auth.ts +++ b/src/utils/rest/auth.ts @@ -1,18 +1,16 @@ -import { loggedIn } from '../../models/LoggedIn'; +import { loggedIn } from "../../models/LoggedIn"; import { TokenObtainPairRequest, TokenObtainPair, TokenRefresh, - UserRepr, - Country, + UserProfile, GenderEnum, - Goal, NullEnum, UserLanguage, -} from '../openapi'; +} from "../openapi"; -import { api, headersWithToken as headers } from '../constants'; +import { api, headersWithToken as headers } from "../constants"; export const signInWithEmail = async ({ username, @@ -28,8 +26,8 @@ export const signInWithEmail = async ({ } if (token) { - localStorage.setItem('accessToken', token.access); - localStorage.setItem('refreshToken', token.refresh); + localStorage.setItem("accessToken", token.access); + localStorage.setItem("refreshToken", token.refresh); return { access: token.access as string, refresh: token.refresh as string, @@ -40,7 +38,7 @@ export const signInWithEmail = async ({ }; export const getAcceessToken = async ( - refresh: string, + refresh: string ): Promise => { const { data: token, error } = await api.api.authJwtRefreshCreate({ refresh: refresh, @@ -51,7 +49,7 @@ export const getAcceessToken = async ( } if (token) { - localStorage.setItem('accessToken', token.access); + localStorage.setItem("accessToken", token.access); return { access: token.access as string, }; @@ -60,7 +58,7 @@ export const getAcceessToken = async ( return null; }; -export const getMe = async (): Promise => { +export const getMe = async (): Promise => { const { data: user, error } = await api.api.usersMeRetrieve({ headers }); if (error) { @@ -81,17 +79,19 @@ export const getMe = async (): Promise => { avatar: user.avatar as string, age: user.age as string, slug: user.slug as string | null, - country: user.country as Country, + country: user.country as string | null, languages: user.languages as UserLanguage[], gender: user.gender as GenderEnum | NullEnum | null, - goals: user.goals as Goal[], + goals: user.goals as string[], interests: user.interests as string[], about: user.about as string, last_activity: user.last_activity as string | null, - is_online: user.is_online as string, + is_online: user.is_online as boolean, gender_is_hidden: user.gender_is_hidden as boolean, age_is_hidden: user.age_is_hidden as boolean, role: user.role as string, + is_blocked: user.is_blocked as boolean, + birth_date: user.birth_date as string | null, }; } diff --git a/src/utils/rest/getCountries.ts b/src/utils/rest/getCountries.ts index dbf23ef..1866e84 100644 --- a/src/utils/rest/getCountries.ts +++ b/src/utils/rest/getCountries.ts @@ -1,5 +1,5 @@ -import { Country } from '../openapi'; -import { api } from '../constants'; +import { Country } from "../openapi"; +import { api } from "../constants"; export const getCountries = async (): Promise => { const { data: countries, error } = await api.api.countriesList(); diff --git a/src/utils/rest/getInterests.ts b/src/utils/rest/getInterests.ts index de8797c..0c31a2c 100644 --- a/src/utils/rest/getInterests.ts +++ b/src/utils/rest/getInterests.ts @@ -1,5 +1,5 @@ -import { Interest } from '../openapi'; -import { api } from '../constants'; +import { Interest } from "../openapi"; +import { api } from "../constants"; export const getInterests = async (): Promise => { const { data: interests, error } = await api.api.interestsList(); diff --git a/src/utils/rest/getLanguages.ts b/src/utils/rest/getLanguages.ts index 16c568e..c433991 100644 --- a/src/utils/rest/getLanguages.ts +++ b/src/utils/rest/getLanguages.ts @@ -1,5 +1,5 @@ -import { Language } from '../openapi'; -import { api } from '../constants'; +import { Language } from "../openapi"; +import { api } from "../constants"; export const getLanguages = async (): Promise => { const { data: languages, error } = await api.api.languagesList(); diff --git a/src/utils/rest/register.ts b/src/utils/rest/register.ts index 8bf8b2c..75f79e1 100644 --- a/src/utils/rest/register.ts +++ b/src/utils/rest/register.ts @@ -1,5 +1,5 @@ -import { UserCreateRequest } from '../openapi'; -import { api } from '../constants'; +import { UserCreateRequest } from "../openapi"; +import { api } from "../constants"; export const signUp = async ({ email, diff --git a/src/utils/rest/updateProfile.ts b/src/utils/rest/updateProfile.ts index 919d427..276b233 100644 --- a/src/utils/rest/updateProfile.ts +++ b/src/utils/rest/updateProfile.ts @@ -1,6 +1,6 @@ -import { Country, GenderEnum, Goal, NullEnum, UserLanguage } from '../openapi'; -import { api } from '../constants'; -import { store } from '../../models/store'; +import { GenderEnum, NullEnum, UserLanguage } from "../openapi"; +import { api, headersWithToken as headers } from "../constants"; +import { store } from "../../models/store"; export const updateProfile = async ({ first_name, @@ -15,11 +15,11 @@ export const updateProfile = async ({ }: { first_name?: string; avatar?: string | null; - country?: Country; + country?: string | null; birth_date?: string | null; languages?: UserLanguage[]; gender?: GenderEnum | NullEnum | null; - goals?: Goal[]; + goals?: string[]; interests?: string[]; about?: string; }) => { @@ -30,17 +30,20 @@ export const updateProfile = async ({ const user = store.session.user; - await api.api.usersMePartialUpdate({ - first_name, - avatar: avatar as File | null, - country: country as string | null | undefined, - birth_date, - languages, - gender, - goals: goals as string[] | undefined, - interests, - about, - }); + await api.api.usersMePartialUpdate( + { + first_name, + avatar: avatar as File | null, + country: country as string | null | undefined, + birth_date, + languages, + gender, + goals: goals as string[] | undefined, + interests, + about, + }, + { headers } + ); store.session.updateUser({ ...user, @@ -55,6 +58,6 @@ export const updateProfile = async ({ about, }); } catch (error) { - console.log('features.updateProfile', error); + console.log("features.updateProfile", error); } }; From b89a3cadb2ac38f96119a2c7d1e5ec08057a37e5 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Wed, 30 Aug 2023 19:26:35 +0300 Subject: [PATCH 137/153] fix: UserProfile --- .../CountrySelection/CountrySelection.tsx | 6 ++--- .../FillOutProfilePage1.tsx | 20 +++++++-------- .../{ => FillOutProfilePage1}/model.ts | 25 ++++++------------- .../FillOutProfilePage2.tsx | 25 ++++++++++++------- .../FillOutProfilePage5.tsx | 2 +- .../FillOutProfilePage6.tsx | 2 +- src/pages/Routing.tsx | 4 +-- src/pages/UserProfile/UserProfile.tsx | 7 +++--- 8 files changed, 43 insertions(+), 48 deletions(-) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage1}/FillOutProfilePage1.tsx (91%) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage1}/model.ts (88%) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage2}/FillOutProfilePage2.tsx (80%) diff --git a/src/components/CountrySelection/CountrySelection.tsx b/src/components/CountrySelection/CountrySelection.tsx index 76faa0a..b035a1e 100644 --- a/src/components/CountrySelection/CountrySelection.tsx +++ b/src/components/CountrySelection/CountrySelection.tsx @@ -69,8 +69,8 @@ const CountrySelection: FC = ({ } }; - const handleSearchInputChange = (e: React.ChangeEvent) => { - const newSearchValue = e.target.value; + const handleSearchInputChange = (e: any) => { + const newSearchValue = e.value; setSearchValue(newSearchValue); if (newSearchValue) { setCountryListVisible(true); @@ -251,7 +251,7 @@ const CountrySelection: FC = ({ fontSize={pageName === "FillOutProfile2" ? "16" : "14"} isLabelHintHidden={true} placeholder="Начните вводить название" - onChange={handleSearchInputChange} + onValue={(event) => handleSearchInputChange(event)} onKeyDown={handleKeyDown} /> {isError && ( diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx similarity index 91% rename from src/pages/FillOutProfilePages/FillOutProfilePage1.tsx rename to src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx index bfe6ae0..6c90135 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx @@ -1,20 +1,20 @@ import { useEffect } from "react"; import { observer } from "mobx-react-lite"; -import Header from "../../components/Header/Header"; -import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import { Input } from "../../components/UI/Input/Input"; -import { Button } from "../../components/UI/Button/Button"; -import Gender from "../../components/Gender/Gender"; -import Modal from "../../components/Modal/Modal"; -import Avatars from "../../components/Avatars/Avatars"; +import Header from "../../../components/Header/Header"; +import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; +import { Input } from "../../../components/UI/Input/Input"; +import { Button } from "../../../components/UI/Button/Button"; +import Gender from "../../../components/Gender/Gender"; +import Modal from "../../../components/Modal/Modal"; +import Avatars from "../../../components/Avatars/Avatars"; -import avatarPlace from "../../images/fill-out-profile-export-avatar.png"; -import faceInACircle from "../../images/face-in-a-circle.png"; +import avatarPlace from "../../../images/fill-out-profile-export-avatar.png"; +import faceInACircle from "../../../images/face-in-a-circle.png"; import { useModel } from "./model"; -import styles from "./FillOutProfilePages.module.scss"; +import styles from "../FillOutProfilePages.module.scss"; import cn from "classnames"; const FillOutProfilePage1 = () => { diff --git a/src/pages/FillOutProfilePages/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts similarity index 88% rename from src/pages/FillOutProfilePages/model.ts rename to src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts index 38917f8..da109a2 100644 --- a/src/pages/FillOutProfilePages/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts @@ -2,14 +2,13 @@ import React, { FormEvent } from "react"; import { useNavigate } from "react-router-dom"; import { useLocalObservable } from "mobx-react-lite"; -import { getMe } from "../../utils/rest/auth"; -import { updateProfile } from "../../utils/rest/updateProfile"; -import { session } from "../../models/session/Session"; +import { getMe } from "../../../utils/rest/auth"; +import { updateProfile } from "../../../utils/rest/updateProfile"; +import { session } from "../../../models/session/Session"; -import { GenderEnum, UserProfile } from "../../utils/openapi"; -import { api, headersWithToken as headers } from "../../utils/constants"; -import { store } from "../../models/store"; -import { User } from "../../models/session/User"; +import { GenderEnum } from "../../../utils/openapi"; +import { api, headersWithToken as headers } from "../../../utils/constants"; +import { store } from "../../../models/store"; export const useModel = () => { const navigate = useNavigate(); @@ -25,9 +24,6 @@ export const useModel = () => { avatar: "", avatarFile: null as File | null, previewAvatar: "", - country: "", - interest: "", - about: "", errorFillOut1: { firstName: "", birthdate: "", avatar: "" }, message: "", isLoading: false, @@ -67,13 +63,7 @@ export const useModel = () => { name, value, }: { - name: - | "firstName" - | "birthdate" - | "avatar" - | "country" - | "interest" - | "about"; + name: "firstName" | "birthdate" | "avatar"; value: string; }) { model[name] = value; @@ -181,7 +171,6 @@ export const useModel = () => { }); } - updateProfile({}); navigate("/fill-out-2"); model.isLoading = false; } catch (error: any) { diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx similarity index 80% rename from src/pages/FillOutProfilePages/FillOutProfilePage2.tsx rename to src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx index 02e4a02..cdfa181 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx @@ -2,18 +2,25 @@ import React, { FormEvent, useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { observer } from "mobx-react-lite"; -import Header from "../../components/Header/Header"; -import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import LanguageModule from "../../components/LanguageModule/LanguageModule"; -import { Button } from "../../components/UI/Button/Button"; -import CountrySelection from "../../components/CountrySelection/CountrySelection"; +import Header from "../../../components/Header/Header"; +import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; +import LanguageModule from "../../../components/LanguageModule/LanguageModule"; +import { Button } from "../../../components/UI/Button/Button"; +import CountrySelection from "../../../components/CountrySelection/CountrySelection"; -import { Country, Language, SkillLevelEnum } from "../../utils/openapi"; +import { Country, Language, SkillLevelEnum } from "../../../utils/openapi"; -import styles from "./FillOutProfilePages.module.scss"; +import { useModel } from "./model"; + +import styles from "../FillOutProfilePages.module.scss"; const FillOutProfilePage2 = () => { const navigate = useNavigate(); + const model = useModel(); + + useEffect(() => { + model.handleCurrentUser(); + }, []); const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); const [selectedCountries, setSelectedCountries] = useState([]); @@ -45,7 +52,7 @@ const FillOutProfilePage2 = () => { const handleFillOutPage2 = (event: FormEvent) => { event.preventDefault(); navigate("/fill-out-3"); - console.log("FillOutPage2"); + console.log("FillOutProfilePage2"); }; return ( @@ -70,7 +77,7 @@ const FillOutProfilePage2 = () => {
diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx index c733084..5c44859 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx @@ -8,7 +8,7 @@ import { Input } from "../../components/UI/Input/Input"; import { Button } from "../../components/UI/Button/Button"; import InterestsSelection from "../../components/CountriesAndInterestsSelection/CountriesAndInterestsSelection"; -import { useModel } from "./model"; +import { useModel } from "./FillOutProfilePage1/model"; import styles from "./FillOutProfilePages.module.scss"; import { Interest } from "../../utils/openapi"; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx index 466337b..e0c698b 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage6.tsx @@ -6,7 +6,7 @@ import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; import { Button } from "../../components/UI/Button/Button"; import { Textarea } from "../../components/UI/Textarea/Textarea"; -import { useModel } from "./model"; +import { useModel } from "./FillOutProfilePage1/model"; import styles from "./FillOutProfilePages.module.scss"; import { observer } from "mobx-react-lite"; diff --git a/src/pages/Routing.tsx b/src/pages/Routing.tsx index c9f17ab..6a569b6 100644 --- a/src/pages/Routing.tsx +++ b/src/pages/Routing.tsx @@ -14,10 +14,10 @@ const RulesPage = lazy(() => import("./RulesPage/RulesPage")); const AgreementPage = lazy(() => import("./AgreementPage/AgreementPage")); const ProfilePage = lazy(() => import("./UserProfile/UserProfile")); const FillOutProfilePage1 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage1") + () => import("./FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1") ); const FillOutProfilePage2 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage2") + () => import("./FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2") ); const FillOutProfilePage3 = lazy( () => import("./FillOutProfilePages/FillOutProfilePage3") diff --git a/src/pages/UserProfile/UserProfile.tsx b/src/pages/UserProfile/UserProfile.tsx index e42f793..6856a08 100644 --- a/src/pages/UserProfile/UserProfile.tsx +++ b/src/pages/UserProfile/UserProfile.tsx @@ -65,7 +65,7 @@ const UserProfile: React.FC = () => { const [isEditing, setIsEditing] = useState(false); const [editedData, setEditedData] = useState({ first_name: "", - avatar: "", + avatar: null, country: "", birth_date: "", languages: [], @@ -177,7 +177,6 @@ const UserProfile: React.FC = () => { } setIsEditing(false); } catch (error) { - // eslint-disable-next-line no-undef console.error("Ошибка при сохранении данных:", error); } }; @@ -204,7 +203,7 @@ const UserProfile: React.FC = () => { location={ typeof editedData.country === "string" ? editedData.country - : userData?.country?.code || "" + : userData?.country || "" } avatar={userData?.avatar || ""} handleFileInputChange={handleFileInputChange} @@ -283,7 +282,7 @@ const UserProfile: React.FC = () => { gender={ userData?.gender || GenderEnum.Male || GenderEnum.Female || null } - location={userData?.country?.code || "" || null} + location={userData?.country || "" || null} avatar={userData?.avatar || ""} handleFileInputChange={handleFileInputChange} setName={(value) => From 15d18fbbe62190beee614e75e82feb212e991951 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Wed, 30 Aug 2023 19:27:10 +0300 Subject: [PATCH 138/153] fix: UserProfile --- .../FillOutProfilePage2/model.ts | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts new file mode 100644 index 0000000..e38313c --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts @@ -0,0 +1,185 @@ +import React, { FormEvent } from "react"; +import { useNavigate } from "react-router-dom"; +import { useLocalObservable } from "mobx-react-lite"; + +import { getMe } from "../../../utils/rest/auth"; +import { session } from "../../../models/session/Session"; + +import { Country, GenderEnum } from "../../../utils/openapi"; +import { api, headersWithToken as headers } from "../../../utils/constants"; +import { store } from "../../../models/store"; + +export const useModel = () => { + const navigate = useNavigate(); + const user = store.session.user; + console.log(user); + + const model = useLocalObservable(() => { + return { + countries: [] as Country[], + birthdate: "", + calculatedBirthday: "", + gender: "Male" as GenderEnum | null, + avatar: "", + avatarFile: null as File | null, + previewAvatar: "", + errorFillOut1: { firstName: "", birthdate: "", avatar: "" }, + message: "", + isLoading: false, + isExportAvatarModal: false, + isCreateAvatarModal: false, + + async handleCurrentUser() { + try { + const user = await getMe(); + + if (user) { + session.updateUser(user); + model.firstName = user.first_name ?? ""; + model.gender = user.gender ?? null; + model.birthdate = user.birth_date ?? ""; + model.avatar = user.avatar ?? ""; + } + } catch (error: any) { + model.message = error.message; + } + }, + + handleModalClose() { + model.isExportAvatarModal = false; + model.isCreateAvatarModal = false; + }, + + handleCountriesValue(countries: Country[]) { + if (countries) { + model.countries = countries; + } else { + model.countries = []; + } + console.log(model.countries); + }, + + handleValue({ + name, + value, + }: { + name: "firstName" | "birthdate" | "avatar"; + value: string; + }) { + model[name] = value; + console.log(model.firstName); + console.log(model.birthdate); + console.log(model.avatar); + }, + + handleGenderValue(gender: GenderEnum | null) { + if (gender !== null) { + model.gender = gender; + } else { + model.gender = null; + } + console.log(model.gender); + }, + + handleSetAvatarPhoto(event: React.ChangeEvent) { + if (event.currentTarget.files) { + const file = event.currentTarget.files[0]; + if (file) { + model.handleValue({ + name: "avatar", + value: URL.createObjectURL(file), + }); + + const reader = new FileReader(); + reader.onload = () => { + const base64Data = reader.result as null; + if (base64Data) { + model.avatarFile = base64Data; + } + }; + reader.readAsDataURL(file); + + console.log("imageBase64", model.avatarFile); + + model.handleModalClose(); + console.log(model.avatar); + } + } + }, + + handleSetAvatarValue(selectedAvatar: string) { + model.previewAvatar = selectedAvatar; + }, + + handleSetAvatar() { + model.avatar = model.previewAvatar; + model.handleModalClose(); + }, + + async handleFillOut1Submit(event: FormEvent) { + event.preventDefault(); + + model.errorFillOut1 = { + firstName: "", + birthdate: "", + avatar: "", + }; + + if (model.firstName === "") { + model.errorFillOut1.firstName = "Пожалуйста, введите Ваше имя"; + } + + if (model.birthdate === "") { + model.errorFillOut1.birthdate = "Пожалуйста, введите дату рождения"; + } + + if ( + model.avatar === "" || + model.avatar === "../../images/fill-out-profile-export-avatar.png" + ) { + model.errorFillOut1.avatar = "Пожалуйста, выберете аватар"; + } + + if ( + model.errorFillOut1.firstName !== "" || + model.errorFillOut1.birthdate !== "" || + model.errorFillOut1.avatar !== "" + ) { + return; + } + + model.message = ""; + model.isLoading = true; + try { + const getUpdateUser = await api.api.usersMePartialUpdate( + { + first_name: model.firstName, + avatar: model.avatarFile, + birth_date: model.birthdate, + gender: model.gender, + }, + { headers } + ); + + if (getUpdateUser && user) { + store.session.updateUser({ + ...user, + first_name: model.firstName, + avatar: model.avatar, + birth_date: model.birthdate, + gender: model.gender, + }); + } + + navigate("/fill-out-2"); + model.isLoading = false; + } catch (error: any) { + console.log("fill-out-1 error:", error); + model.isLoading = false; + } + }, + }; + }); + + return model; +}; From 85261c45fd4c1b2bba82f84025a783729aed19ad Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Thu, 31 Aug 2023 09:54:34 +0300 Subject: [PATCH 139/153] fix: different --- package.json | 1 - .../CountriesAndInterestsSelection.tsx | 4 +- .../CountrySelection/CountrySelection.tsx | 14 +- src/components/Header/Header.tsx | 78 ++++---- .../LanguageModule/LanguageModule.tsx | 2 + src/components/SignupSigninForm/model.ts | 9 +- .../FillOutProfilePage2.tsx | 57 ++---- .../FillOutProfilePage2/model.ts | 168 +++++++----------- .../FillOutProfilePage3.tsx | 14 +- .../FillOutProfilePage4.tsx | 8 +- .../{ => FillOutProfilePage4}/Goals.ts | 12 +- .../FillOutProfilePage5.tsx | 35 ++-- .../FillOutProfilePage6.tsx | 12 +- src/pages/Routing.tsx | 8 +- src/utils/getTime.ts | 2 +- 15 files changed, 182 insertions(+), 242 deletions(-) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage3}/FillOutProfilePage3.tsx (86%) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage4}/FillOutProfilePage4.tsx (92%) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage4}/Goals.ts (70%) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage5}/FillOutProfilePage5.tsx (57%) rename src/pages/FillOutProfilePages/{ => FillOutProfilePage6}/FillOutProfilePage6.tsx (82%) diff --git a/package.json b/package.json index f7cbcf6..8589dfd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "conversation-exchange-frontend", "homepage": "https://conversation-exchange.github.io/frontend", "version": "0.1.0", - "type": "module", "private": true, "dependencies": { "@types/react-dom": "^18.2.7", diff --git a/src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx b/src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx index d30a64d..253cc08 100644 --- a/src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx +++ b/src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx @@ -31,7 +31,7 @@ const CountriesAndInterestsSelection = ({ console.error("Error updating interests list:", error); }); } - }, []); + }, [itemsName]); useEffect(() => { if (itemsName === "interests") { @@ -43,7 +43,7 @@ const CountriesAndInterestsSelection = ({ console.error("Error updating countries list:", error); }); } - }, []); + }, [itemsName]); const allItemsList = [...interestsList, ...countriesList]; diff --git a/src/components/CountrySelection/CountrySelection.tsx b/src/components/CountrySelection/CountrySelection.tsx index b035a1e..df9e12e 100644 --- a/src/components/CountrySelection/CountrySelection.tsx +++ b/src/components/CountrySelection/CountrySelection.tsx @@ -11,19 +11,20 @@ import classNames from "classnames"; interface CountrySelectionProps { pageName: string; - onSelectedCountriesChange: (selectedCountries: Country[]) => void; + selectedCountries: Country[]; + setSelectedCountries: (selectedCountries: Country[]) => void; } const CountrySelection: FC = ({ pageName, - onSelectedCountriesChange, + selectedCountries, + setSelectedCountries, }) => { const [countriesData, setCountriesData] = useState([]); const [isCountryListVisible, setCountryListVisible] = useState(false); const [searchValue, setSearchValue] = useState(""); const [isError, setIsError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); - const [selectedCountries, setSelectedCountries] = useState([]); const [filteredCountries, setFilteredCountries] = useState(countriesData); const [selectedCountry, setSelectedCountry] = useState(null); @@ -63,9 +64,12 @@ const CountrySelection: FC = ({ setSelectedCountries(updatedSelectedCountries); setSelectedCountry(country); setCountryListVisible(false); - setSearchValue(""); + + pageName === "FillOutProfile2" + ? setSearchValue(country.name) + : setSearchValue(""); + // onSortCountry(country); - onSelectedCountriesChange(updatedSelectedCountries); } }; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 41dc8b8..e513813 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,17 +1,17 @@ -import React, { useEffect, useState } from "react"; -import { useLocation } from "react-router"; -import { useNavigate, Link } from "react-router-dom"; -import { observer } from "mobx-react-lite"; +import React, { useEffect, useState } from 'react'; +import { useLocation } from 'react-router'; +import { useNavigate, Link } from 'react-router-dom'; +import { observer } from 'mobx-react-lite'; -import styles from "./Header.module.scss"; -import { Button } from "../UI/Button/Button"; -import { useModel } from "../SignupSigninForm/model"; -import logo from "../../images/svg/logo.svg"; -import bell from "../../images/svg/header-bell.svg"; -import bubble from "../../images/20px.png"; -import k from "../../images/headerK.png"; -import cn from "classnames"; -import { loggedIn } from "../../models/LoggedIn"; +import styles from './Header.module.scss'; +import { Button } from '../UI/Button/Button'; +import { useModel } from '../SignupSigninForm/model'; +import logo from '../../images/svg/logo.svg'; +import bell from '../../images/svg/header-bell.svg'; +import bubble from '../../images/20px.png'; +import k from '../../images/headerK.png'; +import cn from 'classnames'; +import { loggedIn } from '../../models/LoggedIn'; const Header = observer(() => { const model = useModel(); @@ -28,16 +28,16 @@ const Header = observer(() => { localStorage.clear(); setMenuOpen(!isMenuOpen); loggedIn.setLoggedInFalse(); - model.error = ""; - model.message = ""; - model.username = ""; - model.email = ""; - model.password = ""; - model.confirmPassword = ""; + model.error = ''; + model.message = ''; + model.username = ''; + model.email = ''; + model.password = ''; + model.confirmPassword = ''; model.isModalOpen = false; model.user = {}; - if (location.pathname !== "/") { - navigate("/"); + if (location.pathname !== '/') { + navigate('/'); } }; @@ -50,12 +50,12 @@ const Header = observer(() => { }, [loggedIn.loggedIn]); useEffect(() => { - console.log("header next try"); + console.log('header next try'); console.log(isAaa); }, [isAaa]); useEffect(() => { - console.log("header"); + console.log('header'); console.log(loggedIn.loggedIn); }, [loggedIn.loggedIn]); @@ -63,53 +63,53 @@ const Header = observer(() => {
- Логотип проекта + Логотип проекта - {location.pathname !== "/signup" && - location.pathname !== "/signin" && + {location.pathname !== '/signup' && + location.pathname !== '/signin' && !loggedIn.loggedIn && (
)} - {location.pathname !== "/signup" && - location.pathname !== "/signin" && + {location.pathname !== '/signup' && + location.pathname !== '/signin' && loggedIn.loggedIn && (
Иконка чатов Иконка уведомлений Переход в профиль пользователя diff --git a/src/components/LanguageModule/LanguageModule.tsx b/src/components/LanguageModule/LanguageModule.tsx index 46b4404..2b7f0a1 100644 --- a/src/components/LanguageModule/LanguageModule.tsx +++ b/src/components/LanguageModule/LanguageModule.tsx @@ -33,6 +33,8 @@ const LanguageModule: FC = ({ }) => { const [languagesData, setLanguagesData] = useState([]); + console.log("selectedLanguageAndLevels:", selectedLanguagesAndLevels); + //Запрос массива языков const fetchLanguagesData = async () => { try { diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index cebf1ca..028a0f1 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -32,14 +32,10 @@ export const useModel = () => { model[name] = value; }, - get toMain(): string { + to() { return "/"; }, - get toFillOut(): string { - return "fill-out-1"; - }, - async handleRegister(event: FormEvent) { event.preventDefault(); @@ -99,7 +95,7 @@ export const useModel = () => { } model.isLoading = false; - navigate("fill-out-1"); + navigate("/fill-out-1"); model.isLoading = false; } catch (error: any) { model.message = error.message; @@ -142,6 +138,7 @@ export const useModel = () => { session.setAccessToken(token.access); session.setRefreshToken(token.refresh); } + navigate(model.to()); model.isLoading = false; } catch (error: any) { model.message = error.message; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx index cdfa181..a2bcc9b 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx @@ -1,5 +1,4 @@ -import React, { FormEvent, useEffect, useMemo, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import React, { useEffect, useMemo } from "react"; import { observer } from "mobx-react-lite"; import Header from "../../../components/Header/Header"; @@ -8,52 +7,25 @@ import LanguageModule from "../../../components/LanguageModule/LanguageModule"; import { Button } from "../../../components/UI/Button/Button"; import CountrySelection from "../../../components/CountrySelection/CountrySelection"; -import { Country, Language, SkillLevelEnum } from "../../../utils/openapi"; - import { useModel } from "./model"; import styles from "../FillOutProfilePages.module.scss"; +import { Language, SkillLevelEnum } from "../../../utils/openapi"; const FillOutProfilePage2 = () => { - const navigate = useNavigate(); const model = useModel(); - useEffect(() => { - model.handleCurrentUser(); - }, []); - - const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); - const [selectedCountries, setSelectedCountries] = useState([]); - const initialLanguageAndLevels = useMemo(() => { - return { language: null, skillLevels: [] }; + return { language: null, skillLevels: [] as SkillLevelEnum[] }; }, []); - const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< - { language: Language | null; skillLevels: SkillLevelEnum[] }[] - >([initialLanguageAndLevels]); - - const handleReturnButtonClick = () => { - navigate("/fill-out-1"); - }; - - const handleSubmitButtonDisabled = () => { - for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { - selectedLanguagesAndLevels[i].language === null - ? setSubmitButtonDisabled(true) - : setSubmitButtonDisabled(false); - } - }; - useEffect(() => { - handleSubmitButtonDisabled(); - }, [selectedLanguagesAndLevels]); + model.handleCurrentUser(); + }, []); - const handleFillOutPage2 = (event: FormEvent) => { - event.preventDefault(); - navigate("/fill-out-3"); - console.log("FillOutProfilePage2"); - }; + useEffect(() => { + model.handleSubmitButtonDisabled(); + }, [model.languages]); return ( <> @@ -61,7 +33,7 @@ const FillOutProfilePage2 = () => {
@@ -70,14 +42,15 @@ const FillOutProfilePage2 = () => {

Укажите страну и родной язык

- +

Страна, в которой Вы сейчас живете

@@ -87,15 +60,15 @@ const FillOutProfilePage2 = () => {
diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts index e38313c..a03fc2c 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts @@ -1,11 +1,16 @@ -import React, { FormEvent } from "react"; +import { FormEvent } from "react"; import { useNavigate } from "react-router-dom"; import { useLocalObservable } from "mobx-react-lite"; import { getMe } from "../../../utils/rest/auth"; import { session } from "../../../models/session/Session"; -import { Country, GenderEnum } from "../../../utils/openapi"; +import { + Country, + Language, + SkillLevelEnum, + UserLanguageRequest, +} from "../../../utils/openapi"; import { api, headersWithToken as headers } from "../../../utils/constants"; import { store } from "../../../models/store"; @@ -17,17 +22,19 @@ export const useModel = () => { const model = useLocalObservable(() => { return { countries: [] as Country[], - birthdate: "", - calculatedBirthday: "", - gender: "Male" as GenderEnum | null, - avatar: "", - avatarFile: null as File | null, - previewAvatar: "", - errorFillOut1: { firstName: "", birthdate: "", avatar: "" }, + languagesAndLevels: [ + { + language: {} as Language | null, + skillLevels: [] as SkillLevelEnum[], + }, + ], + languages: [ + { isocode: "", language: "", skill_level: {} as SkillLevelEnum }, + ], + error: { countries: "", languages: "" }, message: "", + isSubmitButtonDisabled: false, isLoading: false, - isExportAvatarModal: false, - isCreateAvatarModal: false, async handleCurrentUser() { try { @@ -35,128 +42,82 @@ export const useModel = () => { if (user) { session.updateUser(user); - model.firstName = user.first_name ?? ""; - model.gender = user.gender ?? null; - model.birthdate = user.birth_date ?? ""; - model.avatar = user.avatar ?? ""; + model.countries[0].name = user.country ?? ""; + model.languages = user.languages ?? []; } } catch (error: any) { model.message = error.message; } }, - handleModalClose() { - model.isExportAvatarModal = false; - model.isCreateAvatarModal = false; - }, - handleCountriesValue(countries: Country[]) { if (countries) { model.countries = countries; + console.log(model.countries); } else { model.countries = []; } console.log(model.countries); }, - handleValue({ - name, - value, - }: { - name: "firstName" | "birthdate" | "avatar"; - value: string; - }) { - model[name] = value; - console.log(model.firstName); - console.log(model.birthdate); - console.log(model.avatar); - }, - - handleGenderValue(gender: GenderEnum | null) { - if (gender !== null) { - model.gender = gender; - } else { - model.gender = null; + handleLanguagesValue( + languages: { + language: Language | null; + skillLevels: SkillLevelEnum[]; + }[] + ) { + if (languages) { + model.languagesAndLevels = languages; } - console.log(model.gender); + console.log(model.languages); }, - handleSetAvatarPhoto(event: React.ChangeEvent) { - if (event.currentTarget.files) { - const file = event.currentTarget.files[0]; - if (file) { - model.handleValue({ - name: "avatar", - value: URL.createObjectURL(file), - }); - - const reader = new FileReader(); - reader.onload = () => { - const base64Data = reader.result as null; - if (base64Data) { - model.avatarFile = base64Data; - } - }; - reader.readAsDataURL(file); - - console.log("imageBase64", model.avatarFile); - - model.handleModalClose(); - console.log(model.avatar); - } - } - }, - - handleSetAvatarValue(selectedAvatar: string) { - model.previewAvatar = selectedAvatar; + handleReturnButtonClick() { + navigate("/fill-out-1"); }, - handleSetAvatar() { - model.avatar = model.previewAvatar; - model.handleModalClose(); + handleSubmitButtonDisabled() { + for (let i = 0; i < model.languagesAndLevels.length; i++) { + model.languagesAndLevels[i].language === null + ? (model.isSubmitButtonDisabled = true) + : (model.isSubmitButtonDisabled = false); + } }, - async handleFillOut1Submit(event: FormEvent) { + async handleSubmit(event: FormEvent) { event.preventDefault(); - model.errorFillOut1 = { - firstName: "", - birthdate: "", - avatar: "", + model.error = { + countries: "", + languages: "", }; - if (model.firstName === "") { - model.errorFillOut1.firstName = "Пожалуйста, введите Ваше имя"; - } - - if (model.birthdate === "") { - model.errorFillOut1.birthdate = "Пожалуйста, введите дату рождения"; + if (model.countries === null) { + model.error.languages = "Пожалуйста, выберите язык"; } - if ( - model.avatar === "" || - model.avatar === "../../images/fill-out-profile-export-avatar.png" - ) { - model.errorFillOut1.avatar = "Пожалуйста, выберете аватар"; + if (model.languagesAndLevels === null) { + model.error.languages = "Пожалуйста, выберите язык"; } - if ( - model.errorFillOut1.firstName !== "" || - model.errorFillOut1.birthdate !== "" || - model.errorFillOut1.avatar !== "" - ) { + if (model.error.countries !== "" || model.error.languages !== "") { return; } + console.log(model.languagesAndLevels[0]); + model.message = ""; model.isLoading = true; try { const getUpdateUser = await api.api.usersMePartialUpdate( { - first_name: model.firstName, - avatar: model.avatarFile, - birth_date: model.birthdate, - gender: model.gender, + country: model.countries[0].code, + languages: [ + { + isocode: model.languagesAndLevels[0].language?.isocode || "", + skill_level: {} as SkillLevelEnum, + }, + ], }, { headers } ); @@ -164,17 +125,22 @@ export const useModel = () => { if (getUpdateUser && user) { store.session.updateUser({ ...user, - first_name: model.firstName, - avatar: model.avatar, - birth_date: model.birthdate, - gender: model.gender, + country: model.countries[0].name, + languages: [ + { + language: model.languagesAndLevels[0].language?.name || "", + isocode: model.languagesAndLevels[0].language?.isocode || "", + skill_level: {} as SkillLevelEnum, + }, + ], }); } - navigate("/fill-out-2"); + navigate("/fill-out-3"); model.isLoading = false; } catch (error: any) { - console.log("fill-out-1 error:", error); + console.log(model.languagesAndLevels[0]); + console.log("fill-out-2 error:", error); model.isLoading = false; } }, diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx similarity index 86% rename from src/pages/FillOutProfilePages/FillOutProfilePage3.tsx rename to src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx index 97db807..e706ca7 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage3.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx @@ -1,16 +1,16 @@ import React, { FormEvent, useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; -import Header from "../../components/Header/Header"; -import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import LanguageModule from "../../components/LanguageModule/LanguageModule"; -import { Button } from "../../components/UI/Button/Button"; +import Header from "../../../components/Header/Header"; +import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; +import LanguageModule from "../../../components/LanguageModule/LanguageModule"; +import { Button } from "../../../components/UI/Button/Button"; -import { Language, SkillLevelEnum } from "../../utils/openapi"; +import { Language, SkillLevelEnum } from "../../../utils/openapi"; -import styles from "./FillOutProfilePages.module.scss"; +import styles from "../FillOutProfilePages.module.scss"; import cn from "classnames"; -import LanguageLevelModal from "../../components/LanguageLevelModal/LanguageLevelModal"; +import LanguageLevelModal from "../../../components/LanguageLevelModal/LanguageLevelModal"; const FillOutProfilePage1 = () => { const navigate = useNavigate(); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx similarity index 92% rename from src/pages/FillOutProfilePages/FillOutProfilePage4.tsx rename to src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx index 2ca73dd..335f23a 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage4.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx @@ -8,12 +8,12 @@ import { } from "react"; import { useNavigate } from "react-router-dom"; -import Header from "../../components/Header/Header"; -import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import { Button } from "../../components/UI/Button/Button"; +import Header from "../../../components/Header/Header"; +import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; +import { Button } from "../../../components/UI/Button/Button"; import { Goals } from "./Goals"; -import styles from "./FillOutProfilePages.module.scss"; +import styles from "../FillOutProfilePages.module.scss"; const FillOutProfilePage4 = () => { const navigate = useNavigate(); diff --git a/src/pages/FillOutProfilePages/Goals.ts b/src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts similarity index 70% rename from src/pages/FillOutProfilePages/Goals.ts rename to src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts index 871c31a..1a4427f 100644 --- a/src/pages/FillOutProfilePages/Goals.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts @@ -1,9 +1,9 @@ -import graduationCap from "../../images/goals/graduation-cap.png"; -import business from "../../images/goals/business.png"; -import geography from "../../images/goals/geography.png"; -import product from "../../images/goals/product.png"; -import handshakeLogo from "../../images/goals/handshake-logo.png"; -import brain from "../../images/goals/brain.png"; +import graduationCap from "../../../images/goals/graduation-cap.png"; +import business from "../../../images/goals/business.png"; +import geography from "../../../images/goals/geography.png"; +import product from "../../../images/goals/product.png"; +import handshakeLogo from "../../../images/goals/handshake-logo.png"; +import brain from "../../../images/goals/brain.png"; export const Goals = [ { diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx similarity index 57% rename from src/pages/FillOutProfilePages/FillOutProfilePage5.tsx rename to src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx index 5c44859..34cb05b 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx @@ -1,17 +1,16 @@ -import { FormEvent, useEffect, useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { observer } from "mobx-react-lite"; +import { FormEvent, useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { observer } from 'mobx-react-lite'; -import Header from "../../components/Header/Header"; -import ProgressLine from "../../components/UI/ProgressLine/ProgressLine"; -import { Input } from "../../components/UI/Input/Input"; -import { Button } from "../../components/UI/Button/Button"; -import InterestsSelection from "../../components/CountriesAndInterestsSelection/CountriesAndInterestsSelection"; +import Header from '../../../components/Header/Header'; +import ProgressLine from '../../../components/UI/ProgressLine/ProgressLine'; +import { Button } from '../../../components/UI/Button/Button'; +import InterestsSelection from '../../../components/CountriesAndInterestsSelection/CountriesAndInterestsSelection'; -import { useModel } from "./FillOutProfilePage1/model"; +import { useModel } from '../FillOutProfilePage1/model'; -import styles from "./FillOutProfilePages.module.scss"; -import { Interest } from "../../utils/openapi"; +import styles from '../FillOutProfilePages.module.scss'; +import { Interest } from '../../../utils/openapi'; const FillOutProfilePage5 = () => { const model = useModel(); @@ -20,13 +19,13 @@ const FillOutProfilePage5 = () => { const [selectedInterests, setSelectedInterests] = useState([]); const handleReturnButtonClick = () => { - navigate("/fill-out-4"); + navigate('/fill-out-4'); }; const handleFillOutPage5 = (event: FormEvent) => { event.preventDefault(); - navigate("/fill-out-6"); - console.log("FillOutPage5"); + navigate('/fill-out-6'); + console.log('FillOutPage5'); }; return ( @@ -44,14 +43,14 @@ const FillOutProfilePage5 = () => {

Укажите ваши интересы

= ({ className={styles.slider_input} /> Date: Thu, 31 Aug 2023 19:46:31 +0300 Subject: [PATCH 141/153] fix: different --- package-lock.json | 13 +- package.json | 5 + src/components/Header/Header.tsx | 100 +++++------ .../InputSearchList/InputSearchList.tsx | 11 +- .../InterestsSelection.tsx} | 19 +- src/components/SignupSigninForm/model.ts | 7 + src/components/Sort/Sort.tsx | 162 +++++++++--------- src/images/goals/brain.png | Bin 4228 -> 0 bytes src/images/goals/business.png | Bin 1861 -> 0 bytes src/images/goals/geography.png | Bin 3383 -> 0 bytes src/images/goals/graduation-cap.png | Bin 2923 -> 0 bytes src/images/goals/handshake-logo.png | Bin 3684 -> 0 bytes src/images/goals/product.png | Bin 2059 -> 0 bytes .../FillOutProfilePage1.tsx | 10 +- .../FillOutProfilePage1/model.ts | 59 +++++-- .../FillOutProfilePage4.tsx | 81 ++------- .../FillOutProfilePage4/Goals.ts | 26 +-- .../FillOutProfilePage5.tsx | 49 ++---- src/pages/Routing.tsx | 3 +- .../SignupSigninPage/SignupSigninPage.tsx | 7 - 20 files changed, 265 insertions(+), 287 deletions(-) rename src/components/{CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx => InterestsSelection/InterestsSelection.tsx} (74%) delete mode 100644 src/images/goals/brain.png delete mode 100644 src/images/goals/business.png delete mode 100644 src/images/goals/geography.png delete mode 100644 src/images/goals/graduation-cap.png delete mode 100644 src/images/goals/handshake-logo.png delete mode 100644 src/images/goals/product.png diff --git a/package-lock.json b/package-lock.json index 4ee8dd0..a4cbc63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@types/react-dom": "^18.2.7", "classnames": "^2.3.2", "compose-function": "^3.0.3", + "fs": "^0.0.1-security", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -21,6 +22,7 @@ "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "*", "@types/compose-function": "^0.0.30", + "@types/node": "^20.5.7", "css-loader": "^6.8.1", "eslint": "^8.47.0", "eslint-config-prettier": "^9.0.0", @@ -4540,9 +4542,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.2.tgz", - "integrity": "sha512-5j/lXt7unfPOUlrKC34HIaedONleyLtwkKggiD/0uuMfT8gg2EOpg0dz4lCD15Ga7muC+1WzJZAjIB9simWd6Q==" + "version": "20.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", + "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -9119,6 +9121,11 @@ "node": ">= 0.6" } }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, "node_modules/fs-monkey": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", diff --git a/package.json b/package.json index 8589dfd..e044de2 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@types/react-dom": "^18.2.7", "classnames": "^2.3.2", "compose-function": "^3.0.3", + "fs": "^0.0.1-security", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -44,9 +45,13 @@ "last 1 safari version" ] }, + "browser": { + "fs": false + }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "*", "@types/compose-function": "^0.0.30", + "@types/node": "^20.5.7", "css-loader": "^6.8.1", "eslint": "^8.47.0", "eslint-config-prettier": "^9.0.0", diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index e513813..660ad29 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,61 +1,63 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation } from 'react-router'; -import { useNavigate, Link } from 'react-router-dom'; -import { observer } from 'mobx-react-lite'; +import React, { useEffect, useState } from "react"; +import { useLocation } from "react-router"; +import { useNavigate, Link } from "react-router-dom"; +import { observer } from "mobx-react-lite"; -import styles from './Header.module.scss'; -import { Button } from '../UI/Button/Button'; -import { useModel } from '../SignupSigninForm/model'; -import logo from '../../images/svg/logo.svg'; -import bell from '../../images/svg/header-bell.svg'; -import bubble from '../../images/20px.png'; -import k from '../../images/headerK.png'; -import cn from 'classnames'; -import { loggedIn } from '../../models/LoggedIn'; +import styles from "./Header.module.scss"; +import { Button } from "../UI/Button/Button"; +import { useModel } from "../SignupSigninForm/model"; +import logo from "../../images/svg/logo.svg"; +import bell from "../../images/svg/header-bell.svg"; +import bubble from "../../images/20px.png"; +import k from "../../images/headerK.png"; +import cn from "classnames"; +import { loggedIn } from "../../models/LoggedIn"; +import { session } from "../../models/session/Session"; +import { getMe } from "../../utils/rest/auth"; const Header = observer(() => { const model = useModel(); const navigate = useNavigate(); const location = useLocation(); + const [isMenuOpen, setMenuOpen] = useState(false); - const [isAaa, setAaa] = useState(false); + + useEffect(() => { + const getUser = async () => { + return await getMe(); + }; + }, [loggedIn.loggedIn]); const checkIsSignUp = () => { setMenuOpen(!isMenuOpen); }; const checkLogout = () => { + // session.signOut(); localStorage.clear(); - setMenuOpen(!isMenuOpen); + loggedIn.setLoggedInFalse(); - model.error = ''; - model.message = ''; - model.username = ''; - model.email = ''; - model.password = ''; - model.confirmPassword = ''; + model.error = ""; + model.message = ""; + model.username = ""; + model.email = ""; + model.password = ""; + model.confirmPassword = ""; model.isModalOpen = false; model.user = {}; - if (location.pathname !== '/') { - navigate('/'); + if (location.pathname !== "/") { + navigate("/signup"); } }; - useEffect(() => { - model.getCurrentUser(); - }, [loggedIn.loggedIn]); + useEffect(() => {}, [loggedIn.loggedIn]); useEffect(() => { - setAaa(loggedIn.loggedIn); + session.user; }, [loggedIn.loggedIn]); useEffect(() => { - console.log('header next try'); - console.log(isAaa); - }, [isAaa]); - - useEffect(() => { - console.log('header'); + console.log("header"); console.log(loggedIn.loggedIn); }, [loggedIn.loggedIn]); @@ -63,53 +65,53 @@ const Header = observer(() => {
- Логотип проекта + Логотип проекта - {location.pathname !== '/signup' && - location.pathname !== '/signin' && + {location.pathname !== "/signup" && + location.pathname !== "/signin" && !loggedIn.loggedIn && (
)} - {location.pathname !== '/signup' && - location.pathname !== '/signin' && + {location.pathname !== "/signup" && + location.pathname !== "/signin" && loggedIn.loggedIn && (
Иконка чатов Иконка уведомлений Переход в профиль пользователя diff --git a/src/components/InputSearchList/InputSearchList.tsx b/src/components/InputSearchList/InputSearchList.tsx index 32b3b9b..d1dc772 100644 --- a/src/components/InputSearchList/InputSearchList.tsx +++ b/src/components/InputSearchList/InputSearchList.tsx @@ -14,14 +14,16 @@ interface IPageName { pageName: string; itemsName: string; itemsList: (Country | Interest)[]; - onSelectedItemsChange: (item: (Country | Interest)[]) => void; + selectedItems: (Country | Interest)[]; + setSelectedItems: (item: (Country | Interest)[]) => void; } const InputSearchList: FC = ({ pageName, itemsName, itemsList, - onSelectedItemsChange, + selectedItems, + setSelectedItems, }: IPageName) => { const [isSearchListVisible, setSearchListVisible] = useState(false); const [searchValue, setSearchValue] = useState(""); @@ -42,9 +44,6 @@ const InputSearchList: FC = ({ >(null); const [isError, setIsError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); - const [selectedItems, setSelectedItems] = useState<(Country | Interest)[]>( - [] - ); const i = itemsName === "countries" ? (pageName === "Sort" ? 5 : 1) : 100; @@ -207,8 +206,6 @@ const InputSearchList: FC = ({ setSearchListVisible(false); setSearchValue(""); // onSortCountry(country); - - onSelectedItemsChange(updatedSelectedItems); } }; diff --git a/src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx b/src/components/InterestsSelection/InterestsSelection.tsx similarity index 74% rename from src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx rename to src/components/InterestsSelection/InterestsSelection.tsx index 253cc08..c420841 100644 --- a/src/components/CountriesAndInterestsSelection/CountriesAndInterestsSelection.tsx +++ b/src/components/InterestsSelection/InterestsSelection.tsx @@ -10,13 +10,19 @@ import { getCountries } from "../../utils/rest/getCountries"; interface CountriesAndInterestsSelectionProps { pageName: string; itemsName: string; - onSelectedItemsChange: (item: (Country | Interest)[]) => void; + selectedInterests?: Interest[]; + setSelectedInterests?: (item: Interest[]) => void; + selectedCountries?: Country[]; + setSelectedCountries?: (item: Country[]) => void; } -const CountriesAndInterestsSelection = ({ +const InterestsSelection = ({ pageName, itemsName, - onSelectedItemsChange, + selectedInterests, + setSelectedInterests, + selectedCountries, + setSelectedCountries, }: CountriesAndInterestsSelectionProps) => { const [interestsList, setInterestsList] = useState([]); const [countriesList, setCountriesList] = useState([]); @@ -52,9 +58,12 @@ const CountriesAndInterestsSelection = ({ pageName={pageName} itemsName="interests" itemsList={allItemsList} - onSelectedItemsChange={onSelectedItemsChange} + selectedInterests={selectedInterests} + setSelectedItems={ + itemsName === "countries" ? setSelectedCountries : setSelectedInterests + } /> ); }; -export default observer(CountriesAndInterestsSelection); +export default observer(InterestsSelection); diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 028a0f1..112d38c 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -5,6 +5,7 @@ import { useLocalObservable } from "mobx-react-lite"; import { getMe, signInWithEmail } from "../../utils/rest/auth"; import { signUp } from "../../utils/rest/register"; import { session } from "../../models/session/Session"; +import { api, headersWithToken as headers } from "../../utils/constants"; export const useModel = () => { const navigate = useNavigate(); @@ -78,6 +79,8 @@ export const useModel = () => { model.message = ""; model.isLoading = true; try { + session.signOut(); + await signUp({ email: model.email, username: model.username, @@ -129,12 +132,16 @@ export const useModel = () => { model.isLoading = true; try { + session.signOut(); + const token = await signInWithEmail({ username: model.email, password: model.password, }); if (token) { + localStorage.setItem("accessToken", token.access); + localStorage.setItem("refreshToken", token.refresh); session.setAccessToken(token.access); session.setRefreshToken(token.refresh); } diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 4aa67ad..9e4ba94 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -1,15 +1,15 @@ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo } from "react"; -import CountrySelection from '../CountrySelection/CountrySelection'; -import LanguageModule from '../LanguageModule/LanguageModule'; -import Gender from '../Gender/Gender'; -import MultiRangeSlider from '../MultiRangeSlider/MultiRangeSlider'; -import LanguageLevelModal from '../LanguageLevelModal/LanguageLevelModal'; -import { Button } from '../UI/Button/Button'; +import CountrySelection from "../CountrySelection/CountrySelection"; +import LanguageModule from "../LanguageModule/LanguageModule"; +import Gender from "../Gender/Gender"; +import MultiRangeSlider from "../MultiRangeSlider/MultiRangeSlider"; +import LanguageLevelModal from "../LanguageLevelModal/LanguageLevelModal"; +import { Button } from "../UI/Button/Button"; -import { Country, Language, SkillLevelEnum } from '../../utils/openapi'; +import { Country, Language, SkillLevelEnum } from "../../utils/openapi"; -import styles from '../Sort/Sort.module.scss'; +import styles from "../Sort/Sort.module.scss"; interface Filters { country: string; @@ -29,11 +29,11 @@ interface SortProps { const Sort: React.FC = ({ onChangeSort, isOpen }) => { const [selectedCountries, setSelectedCountries] = useState([]); const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< - { language: Language | null; skillLevels: SkillLevelEnum[] }[] + { language: Language | null; skillLevels: SkillLevelEnum[] }[] >([]); const [isModalOpen, setModalOpen] = useState(false); const [showDefaultLanguageModule, setShowDefaultLanguageModule] = - useState(true); + useState(true); const [selectedGender, setSelectedGender] = useState(null); const [leftValue, setLeftValue] = useState(18); const [rightValue, setRightValue] = useState(40); @@ -56,17 +56,17 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { }; const handleFindButtonClick = () => { - const ageRange = ${leftValue},${rightValue}; + const ageRange = "${leftValue}","${rightValue}"; const languageFilters = selectedLanguagesAndLevels - .filter((item) => item.language !== null && item.skillLevels.length > 0) - .map((item) => ({ - language: item.language!.name, - skill_level: item.skillLevels.join(','), - })); + .filter((item) => item.language !== null && item.skillLevels.length > 0) + .map((item) => ({ + language: item.language!.name, + skill_level: item.skillLevels.join(","), + })); const countryCodes = selectedCountries - .filter((country) => country.code !== null) - .map((country) => country.code!.toUpperCase()) - .join(','); + .filter((country) => country.code !== null) + .map((country) => country.code!.toUpperCase()) + .join(","); const filters: Filters = { age: ageRange, country: countryCodes, @@ -76,72 +76,72 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { onChangeSort(filters); }; return ( -
-
-

Страна партнера

- -
-
-

Язык партнера

-
- {showDefaultLanguageModule && ( - - )} -
-

О партнере

-
-

Пол

- -
-
-
-

Возраст

- -
-
+ {showDefaultLanguageModule && ( + - +

О партнере

+
+

Пол

+ +
+
+
+

Возраст

+
+
); }; diff --git a/src/images/goals/brain.png b/src/images/goals/brain.png deleted file mode 100644 index 7de6f42223381971e4d58a88840ace85363d768c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4228 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 V8gZAMCO!ZF002ovPDHLkV1ib^jJ5y( diff --git a/src/images/goals/handshake-logo.png b/src/images/goals/handshake-logo.png deleted file mode 100644 index e1dcdefdf041e3e75ef91967e4c9182963949b67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3684 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 p&lS6QQh002ovPDHLkV1k{Q<%|FT diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx index 6c90135..204f5bb 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1.tsx @@ -33,11 +33,7 @@ const FillOutProfilePage1 = () => {

Давайте заполним ваш профиль и начнем общаться

- +
{ labelStyles="label18" isLabelHintHidden={true} placeholder="Имя" - error={model.errorFillOut1.firstName} + error={model.error.firstName} required maxLength={12} onValue={model.handleValue} @@ -77,7 +73,7 @@ const FillOutProfilePage1 = () => { labelStyles="label18" isLabelHintHidden={true} required - error={model.errorFillOut1.birthdate} + error={model.error.birthdate} onValue={model.handleValue} />
diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts index da109a2..1d9d3e7 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts @@ -10,6 +10,8 @@ import { GenderEnum } from "../../../utils/openapi"; import { api, headersWithToken as headers } from "../../../utils/constants"; import { store } from "../../../models/store"; +import fs from "fs"; + export const useModel = () => { const navigate = useNavigate(); const user = store.session.user; @@ -24,7 +26,7 @@ export const useModel = () => { avatar: "", avatarFile: null as File | null, previewAvatar: "", - errorFillOut1: { firstName: "", birthdate: "", avatar: "" }, + error: { firstName: "", birthdate: "", avatar: "" }, message: "", isLoading: false, isExportAvatarModal: false, @@ -85,19 +87,40 @@ export const useModel = () => { if (event.currentTarget.files) { const file = event.currentTarget.files[0]; if (file) { + const fileSrc = URL.createObjectURL(file); + model.handleValue({ name: "avatar", - value: URL.createObjectURL(file), + value: fileSrc, }); - const reader = new FileReader(); - reader.onload = () => { - const base64Data = reader.result as null; - if (base64Data) { - model.avatarFile = base64Data; - } - }; - reader.readAsDataURL(file); + const imageBuffer = fs.readFileSync(fileSrc); + const base64Image = imageBuffer.toString("base64"); + + console.log(base64Image); + + // const reader = new FileReader(); + // reader.onload = () => { + // const base64Data = reader.result; + // if (base64Data) { + // model.avatarFile = base64Data; + // } + // console.log("imageBase64", reader.result); + // reader.readAsDataURL(file); + + // if (base64Data && typeof base64Data === "string") { + // // Создаем объект типа File, преобразовывая строку Base64 в Blob + // const blob = new Blob([base64Data], { + // type: "image/png" || "image/jpg" || "image/jpeg", + // }); + // const file = new File( + // [blob], + // "filename.png" || "filename.jpg" || "filename.jpeg" + // ); + // + // model.avatarFile = blob; + // } + // }; console.log("imageBase64", model.avatarFile); @@ -116,34 +139,34 @@ export const useModel = () => { model.handleModalClose(); }, - async handleFillOut1Submit(event: FormEvent) { + async handleSubmit(event: FormEvent) { event.preventDefault(); - model.errorFillOut1 = { + model.error = { firstName: "", birthdate: "", avatar: "", }; if (model.firstName === "") { - model.errorFillOut1.firstName = "Пожалуйста, введите Ваше имя"; + model.error.firstName = "Пожалуйста, введите Ваше имя"; } if (model.birthdate === "") { - model.errorFillOut1.birthdate = "Пожалуйста, введите дату рождения"; + model.error.birthdate = "Пожалуйста, введите дату рождения"; } if ( model.avatar === "" || model.avatar === "../../images/fill-out-profile-export-avatar.png" ) { - model.errorFillOut1.avatar = "Пожалуйста, выберете аватар"; + model.error.avatar = "Пожалуйста, выберете аватар"; } if ( - model.errorFillOut1.firstName !== "" || - model.errorFillOut1.birthdate !== "" || - model.errorFillOut1.avatar !== "" + model.error.firstName !== "" || + model.error.birthdate !== "" || + model.error.avatar !== "" ) { return; } diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx index 335f23a..23364e8 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4.tsx @@ -1,75 +1,28 @@ -import { - ChangeEvent, - FormEvent, - MouseEventHandler, - useEffect, - useMemo, - useState, -} from "react"; -import { useNavigate } from "react-router-dom"; +import { useEffect } from "react"; +import { observer } from "mobx-react-lite"; import Header from "../../../components/Header/Header"; import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; import { Button } from "../../../components/UI/Button/Button"; import { Goals } from "./Goals"; +import { useModel } from "./model"; + import styles from "../FillOutProfilePages.module.scss"; const FillOutProfilePage4 = () => { - const navigate = useNavigate(); - - const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); - - const [selectedGoals, setSelectedGoals] = useState< - { - image: string; - name: string; - description: string; - active: boolean; - }[] - >([]); - - console.log(selectedGoals); - console.log(isSubmitButtonDisabled); + const model = useModel(); - const handleSubmitButtonDisabled = () => { - selectedGoals.length === 0 - ? setSubmitButtonDisabled(true) - : setSubmitButtonDisabled(false); - }; + console.log(model.selectedGoals); + console.log(model.isSubmitButtonDisabled); useEffect(() => { - handleSubmitButtonDisabled(); - }, [selectedGoals]); + model.handleCurrentUser(); + }, []); - const handleReturnButtonClick = () => { - navigate("/fill-out-3"); - }; - - const handleGoalButtonClick = (event, goal, index) => { - const updatedGoals = [...selectedGoals]; - - console.log(goal.active); - - if (!goal.active) { - goal.active = true; - updatedGoals.push(goal); - setSelectedGoals(updatedGoals); - event.currentTarget.classList.add(styles.container__goals_goal_active); - } else { - goal.active = false; - const updatedGoals = selectedGoals.filter( - (selectedGoal) => selectedGoal.active - ); - setSelectedGoals(updatedGoals); - event.currentTarget.classList.remove(styles.container__goals_goal_active); - } - }; - const handleFillOutPage4 = (event: FormEvent) => { - event.preventDefault(); - navigate("/fill-out-5"); - console.log("FillOutPage4"); - }; + useEffect(() => { + model.handleSubmitButtonDisabled(); + }, [model.selectedGoals]); return ( <> @@ -77,14 +30,14 @@ const FillOutProfilePage4 = () => {

Укажите ваши цели

- +
{Goals.map((goal, index) => ( @@ -114,4 +67,4 @@ const FillOutProfilePage4 = () => { ); }; -export default FillOutProfilePage4; +export default observer(FillOutProfilePage4); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts b/src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts index 1a4427f..4aad707 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4/Goals.ts @@ -1,38 +1,38 @@ -import graduationCap from "../../../images/goals/graduation-cap.png"; -import business from "../../../images/goals/business.png"; -import geography from "../../../images/goals/geography.png"; -import product from "../../../images/goals/product.png"; -import handshakeLogo from "../../../images/goals/handshake-logo.png"; -import brain from "../../../images/goals/brain.png"; +import education from "../../../images/goals/education.svg"; +import work from "../../../images/goals/work.svg"; +import travel from "../../../images/goals/travel.svg"; +import box from "../../../images/goals/box.svg"; +import community from "../../../images/goals/community.svg"; +import brain from "../../../images/goals/brain.svg"; export const Goals = [ { - image: graduationCap, + image: education, name: "Академическая шапочка", description: "Образование", active: false, }, { - image: business, + image: work, name: "Портфель бизнесмена", description: "Карьера и бизнес", active: false, }, { - image: geography, + image: travel, name: "Модель планеты", description: "Путешествие", active: false, }, { - image: product, - name: "Иконка чемодана", + image: box, + name: "Коробка для переезда", description: "Переезд", active: false, }, { - image: handshakeLogo, - name: "Иконка рукопожатия", + image: community, + name: "Иконка сообщества: два человека", description: "Сообщество", active: false, }, diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx index 34cb05b..1fb02ca 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx @@ -1,32 +1,16 @@ -import { FormEvent, useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { observer } from 'mobx-react-lite'; +import { observer } from "mobx-react-lite"; -import Header from '../../../components/Header/Header'; -import ProgressLine from '../../../components/UI/ProgressLine/ProgressLine'; -import { Button } from '../../../components/UI/Button/Button'; -import InterestsSelection from '../../../components/CountriesAndInterestsSelection/CountriesAndInterestsSelection'; +import Header from "../../../components/Header/Header"; +import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; +import { Button } from "../../../components/UI/Button/Button"; +import CountriesAndInterestsSelection from "../../../components/InterestsSelection/InterestsSelection"; -import { useModel } from '../FillOutProfilePage1/model'; +import { useModel } from "./model"; -import styles from '../FillOutProfilePages.module.scss'; -import { Interest } from '../../../utils/openapi'; +import styles from "../FillOutProfilePages.module.scss"; const FillOutProfilePage5 = () => { const model = useModel(); - const navigate = useNavigate(); - - const [selectedInterests, setSelectedInterests] = useState([]); - - const handleReturnButtonClick = () => { - navigate('/fill-out-4'); - }; - - const handleFillOutPage5 = (event: FormEvent) => { - event.preventDefault(); - navigate('/fill-out-6'); - console.log('FillOutPage5'); - }; return ( <> @@ -34,23 +18,24 @@ const FillOutProfilePage5 = () => {
-

Укажите ваши интересы

- - Укажите Ваши интересы + +
); -}); +}; -export default Header; +export default observer(Header); diff --git a/src/components/InputSearchList/InputSearchList.tsx b/src/components/InputSearchList/InputSearchList.tsx index d1dc772..8ce1031 100644 --- a/src/components/InputSearchList/InputSearchList.tsx +++ b/src/components/InputSearchList/InputSearchList.tsx @@ -4,56 +4,45 @@ import { observer } from "mobx-react-lite"; import { Input } from "../UI/Input/Input"; import ItemsOpenedList from "../UI/ItemsOpenedList/ItemsOpenedList"; -import { Country, Interest } from "../../utils/openapi"; +import { Interest } from "../../utils/openapi"; import styles from "./InputSearchList.module.scss"; -import classNames from "classnames"; import cn from "classnames"; interface IPageName { - pageName: string; - itemsName: string; - itemsList: (Country | Interest)[]; - selectedItems: (Country | Interest)[]; - setSelectedItems: (item: (Country | Interest)[]) => void; + itemsList: Interest[]; + selectedInterests: Interest[]; + setSelectedInterests: (item: Interest[]) => void; } const InputSearchList: FC = ({ - pageName, - itemsName, itemsList, - selectedItems, - setSelectedItems, + selectedInterests, + setSelectedInterests, }: IPageName) => { const [isSearchListVisible, setSearchListVisible] = useState(false); const [searchValue, setSearchValue] = useState(""); - const [filteredItems, setFilteredItems] = useState<(Country | Interest)[]>( - [] - ); - const [selectedItem, setSelectedItem] = useState( - null - ); + const [filteredItems, setFilteredItems] = useState([]); + const [selectedItem, setSelectedItem] = useState(null); const [lastPressedLetter, setLastPressedLetter] = useState( null ); - const [suggestedItems, setSuggestedItems] = useState<(Country | Interest)[]>( - [] - ); + const [suggestedItems, setSuggestedItems] = useState([]); const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState< number | null >(null); const [isError, setIsError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); - const i = itemsName === "countries" ? (pageName === "Sort" ? 5 : 1) : 100; + const i = 100; console.log(isSearchListVisible); console.log(searchValue); console.log(i); - console.log(selectedItems); + console.log(selectedInterests); useEffect(() => { - setSelectedItems(itemsList); + setSelectedInterests(itemsList); }, [itemsList]); const handleInputValue = (event: any) => { @@ -72,7 +61,7 @@ const InputSearchList: FC = ({ const filtered = itemsList.filter( (item) => item.name.toLocaleLowerCase("ru").includes(searchValueLower) && - !selectedItems.includes(item) + !selectedInterests.includes(item) ); setFilteredItems(filtered); @@ -88,12 +77,6 @@ const InputSearchList: FC = ({ const isInvalidSearch = searchValue.length > 0 && filtered.length === 0 && suggested.length === 0; - const errorMessage = isInvalidSearch - ? itemsName === "countries" - ? "Страны не существует, возможно ошибка" - : "" - : ""; - setIsError(isInvalidSearch); setErrorMessage(errorMessage); @@ -125,13 +108,11 @@ const InputSearchList: FC = ({ }); } else if (e.key === "Enter") { e.preventDefault(); - if (itemsName === "countries" && selectedSuggestionIndex !== null) { - handleSelectItem(suggestedItems[selectedSuggestionIndex]); - } else if (itemsName === "interests") { - const inputElement = e.target as HTMLInputElement; - handleSelectItem({ name: inputElement.value, sorting: "" }); - console.log(inputElement.value); - } else if (selectedItem) { + + const inputElement = e.target as HTMLInputElement; + handleSelectItem({ name: inputElement.value, sorting: "" }); + console.log(inputElement.value); + if (selectedItem) { handleSelectItem(selectedItem); } } @@ -162,13 +143,11 @@ const InputSearchList: FC = ({ }); } else if (e.key === "Enter") { e.preventDefault(); - if (itemsName === "countries" && selectedSuggestionIndex !== null) { - handleSelectItem(suggestedItems[selectedSuggestionIndex]); - } else if (itemsName === "interests") { - const inputElement = e.target as HTMLInputElement; - handleSelectItem({ name: inputElement.value, sorting: "" }); - console.log(inputElement.value); - } else if (selectedItem) { + + const inputElement = e.target as HTMLInputElement; + handleSelectItem({ name: inputElement.value, sorting: "" }); + console.log(inputElement.value); + if (selectedItem) { handleSelectItem(selectedItem); } } @@ -198,14 +177,13 @@ const InputSearchList: FC = ({ } }; - const handleSelectItem = (item: Country | Interest) => { - if (selectedItems.length < i && !selectedItems.includes(item)) { - const updatedSelectedItems = [...selectedItems, item]; - setSelectedItems(updatedSelectedItems); + const handleSelectItem = (item: Interest) => { + if (selectedInterests.length < i && !selectedInterests.includes(item)) { + const updatedSelectedItems = [...selectedInterests, item]; + setSelectedInterests(updatedSelectedItems); setSelectedItem(item); setSearchListVisible(false); setSearchValue(""); - // onSortCountry(country); } }; @@ -220,47 +198,19 @@ const InputSearchList: FC = ({ return ( <> - {itemsName === "interests" && ( - handleInputValue(event)} - onKeyDown={handleKeyDown} - autoComplete="off" - /> - )} - {itemsName === "countries" && ( - <> - handleInputValue(event)} - onKeyDown={handleKeyDown} - autoComplete="off" - /> - {isError && ( - {errorMessage} - )} - - )} -
+ handleInputValue(event)} + onKeyDown={handleKeyDown} + autoComplete="off" + /> +
{isSearchListVisible && (
= ({ onClick={() => handleSelectItemFromList(item.name.toLocaleLowerCase("ru")) } - className={classNames(styles.items__itemsList_option, { - [styles.selected]: - (selectedItem as Country)?.code === - (item as Country).code, - [styles.suggested]: suggestedItems.includes( - item as Country - ), - })} + className={styles.items__itemsList_option} > - {`${item.name} {item.name}
) : null @@ -303,15 +237,12 @@ const InputSearchList: FC = ({
diff --git a/src/components/InterestsSelection/InterestsSelection.tsx b/src/components/InterestsSelection/InterestsSelection.tsx index c420841..c4b8cf5 100644 --- a/src/components/InterestsSelection/InterestsSelection.tsx +++ b/src/components/InterestsSelection/InterestsSelection.tsx @@ -3,65 +3,35 @@ import { observer } from "mobx-react-lite"; import InputSearchList from "../InputSearchList/InputSearchList"; -import { Country, Interest } from "../../utils/openapi"; +import { Interest } from "../../utils/openapi"; import { getInterests } from "../../utils/rest/getInterests"; -import { getCountries } from "../../utils/rest/getCountries"; -interface CountriesAndInterestsSelectionProps { - pageName: string; - itemsName: string; - selectedInterests?: Interest[]; - setSelectedInterests?: (item: Interest[]) => void; - selectedCountries?: Country[]; - setSelectedCountries?: (item: Country[]) => void; +interface InterestsSelectionProps { + selectedInterests: Interest[]; + setSelectedInterests: (item: Interest[]) => void; } const InterestsSelection = ({ - pageName, - itemsName, selectedInterests, setSelectedInterests, - selectedCountries, - setSelectedCountries, -}: CountriesAndInterestsSelectionProps) => { +}: InterestsSelectionProps) => { const [interestsList, setInterestsList] = useState([]); - const [countriesList, setCountriesList] = useState([]); useEffect(() => { - if (itemsName === "countries") { - getInterests() - .then((updatedList) => { - setInterestsList(updatedList); - }) - .catch((error) => { - console.error("Error updating interests list:", error); - }); - } - }, [itemsName]); - - useEffect(() => { - if (itemsName === "interests") { - getCountries() - .then((updatedList) => { - setCountriesList(updatedList); - }) - .catch((error) => { - console.error("Error updating countries list:", error); - }); - } - }, [itemsName]); - - const allItemsList = [...interestsList, ...countriesList]; + getInterests() + .then((updatedList) => { + setInterestsList(updatedList); + }) + .catch((error) => { + console.error("Error updating interests list:", error); + }); + }, []); return ( ); }; diff --git a/src/components/SignupSigninForm/SignupSigninForm.tsx b/src/components/SignupSigninForm/SignupSigninForm.tsx index a9911ea..75d0184 100644 --- a/src/components/SignupSigninForm/SignupSigninForm.tsx +++ b/src/components/SignupSigninForm/SignupSigninForm.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from "react"; -import { useLocation, Link } from "react-router-dom"; +import { useEffect } from "react"; +import { Link, useLocation } from "react-router-dom"; import { observer } from "mobx-react-lite"; import { Input } from "../UI/Input/Input"; @@ -17,14 +17,8 @@ const SignupSigninForm = () => { const location = useLocation(); const pathName: string = location.pathname; - const [isSignUp, setIsSignUp] = useState(false); - - const checkIsSignUp = () => { - pathName === "/signup" ? setIsSignUp(true) : setIsSignUp(false); - }; - useEffect(() => { - checkIsSignUp(); + model.checkIsSignUp(); }, [pathName]); useEffect(() => { @@ -39,13 +33,13 @@ const SignupSigninForm = () => { return (
    { { Регистрация
- {isSignUp && ( + {model.isSignUp && ( { type="email" name="email" value={model.email} - label={isSignUp ? "Введите эл.почту" : "Введите логин или эл.почту"} - labelHint={isSignUp ? "И получите письмо с подтверждением" : ""} - isLabelHintHidden={!isSignUp} + label={ + model.isSignUp ? "Введите эл.почту" : "Введите логин или эл.почту" + } + labelHint={model.isSignUp ? "И получите письмо с подтверждением" : ""} + isLabelHintHidden={!model.isSignUp} placeholder="Эл. почта" required error={model.error.email} @@ -99,9 +95,11 @@ const SignupSigninForm = () => { type="password" name="password" value={model.password} - label={isSignUp ? "Придумайте пароль" : "Введите пароль"} - labelHint={isSignUp ? "Не менее 5 символов, латинскими буквами" : ""} - isLabelHintHidden={!isSignUp} + label={model.isSignUp ? "Придумайте пароль" : "Введите пароль"} + labelHint={ + model.isSignUp ? "Не менее 5 символов, латинскими буквами" : "" + } + isLabelHintHidden={!model.isSignUp} placeholder="Пароль" required error={model.error.password} @@ -109,7 +107,7 @@ const SignupSigninForm = () => { maxLength={12} minLength={5} /> - {isSignUp && ( + {model.isSignUp && ( { minLength={5} /> )} - {isSignUp && ( + {model.isSignUp && (

Нажимая на кнопку «Продолжить», вы соглашаетесь с  @@ -134,7 +132,7 @@ const SignupSigninForm = () => {

)}
- {!isSignUp && ( + {!model.isSignUp && ( )} - {!isSignUp && ( + {!model.isSignUp && ( { variant="primary" disabled={model.isLoading} > - {model.isLoading ? "Loading" : isSignUp ? "Продолжить" : "Войти"} + {model.isLoading ? "Loading" : model.isSignUp ? "Продолжить" : "Войти"}

или diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 112d38c..0ce05c8 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -1,15 +1,17 @@ -import { FormEvent } from "react"; -import { useNavigate } from "react-router-dom"; +import { FormEvent, useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; import { useLocalObservable } from "mobx-react-lite"; import { getMe, signInWithEmail } from "../../utils/rest/auth"; import { signUp } from "../../utils/rest/register"; import { session } from "../../models/session/Session"; -import { api, headersWithToken as headers } from "../../utils/constants"; export const useModel = () => { const navigate = useNavigate(); + const location = useLocation(); + const pathName: string = location.pathname; + const model = useLocalObservable(() => { return { username: "", @@ -23,6 +25,12 @@ export const useModel = () => { access: "", isSignUp: false, + checkIsSignUp() { + pathName === "/signup" + ? (model.isSignUp = true) + : (model.isSignUp = false); + }, + handleValue({ name, value, diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 9e4ba94..1618a4c 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -56,7 +56,7 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => { }; const handleFindButtonClick = () => { - const ageRange = "${leftValue}","${rightValue}"; + const ageRange = `${leftValue},${rightValue}`; const languageFilters = selectedLanguagesAndLevels .filter((item) => item.language !== null && item.skillLevels.length > 0) .map((item) => ({ @@ -81,7 +81,8 @@ const Sort: React.FC = ({ onChangeSort, isOpen }) => {

Страна партнера

diff --git a/src/components/UI/GoBackButton/GoBackButton.module.scss b/src/components/UI/GoBackButton/GoBackButton.module.scss new file mode 100644 index 0000000..119a6d9 --- /dev/null +++ b/src/components/UI/GoBackButton/GoBackButton.module.scss @@ -0,0 +1,8 @@ +.button { + background-image: url('../../../images/svg/back-arrow-32px.svg'); + background-repeat: no-repeat; + width: 32px; + height: 32px; + border: none; + padding: 0; +} diff --git a/src/components/UI/GoBackButton/GoBackButton.tsx b/src/components/UI/GoBackButton/GoBackButton.tsx new file mode 100644 index 0000000..1ac6197 --- /dev/null +++ b/src/components/UI/GoBackButton/GoBackButton.tsx @@ -0,0 +1,23 @@ +import { FC } from "react"; +import styles from "./GoBackButton.module.scss"; +import cn from "classnames"; + +interface GoBackButtonProps { + onClick: () => void; + positionClassName?: string; +} + +const GoBackButton: FC = ({ + onClick, + positionClassName, +}) => { + return ( + + ); +}; + +export default GoBackButton; diff --git a/src/components/UI/ItemsOpenedList/ItemsOpenedList.tsx b/src/components/UI/ItemsOpenedList/ItemsOpenedList.tsx index 22470dc..95ee250 100644 --- a/src/components/UI/ItemsOpenedList/ItemsOpenedList.tsx +++ b/src/components/UI/ItemsOpenedList/ItemsOpenedList.tsx @@ -1,22 +1,20 @@ -import { Country, Interest } from "../../../utils/openapi"; +import { Interest } from "../../../utils/openapi"; import styles from "../../InputSearchList/InputSearchList.module.scss"; import cn from "classnames"; interface InterestsOpenedListProps { - itemsName: string; - selectedItems: (Country | Interest)[]; - setSelectedItems: (selectedItems: (Country | Interest)[]) => void; + selectedItems: Interest[]; + setSelectedItems: (selectedItems: Interest[]) => void; } const itemsOpenedList = ({ - itemsName, selectedItems, setSelectedItems, }: InterestsOpenedListProps) => { - const handleRemoveItem = (item: Country | Interest) => { + const handleRemoveItem = (item: Interest) => { const updatedItems = selectedItems.filter( - (c: Country | Interest) => c.name !== item.name + (c: Interest) => c.name !== item.name ); setSelectedItems(updatedItems); }; @@ -24,15 +22,13 @@ const itemsOpenedList = ({ return ( <> {selectedItems && - selectedItems.map((item: Country | Interest, index: number) => ( + selectedItems.map((item: Interest, index: number) => ( + ); +}; + +export default SearchButton; diff --git a/src/images/chat_idle_background.png b/src/images/chat_idle_background.png new file mode 100644 index 0000000000000000000000000000000000000000..9e8411e4fb858208b9c27a1c04eb423968ef02aa GIT binary patch literal 30404 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 dsv`LQ1quJ@5; literal 0 HcmV?d00001 diff --git a/src/images/goals/box.svg b/src/images/goals/box.svg new file mode 100644 index 0000000..533189c --- /dev/null +++ b/src/images/goals/box.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/goals/brain.svg b/src/images/goals/brain.svg new file mode 100644 index 0000000..06da5ba --- /dev/null +++ b/src/images/goals/brain.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/goals/business.svg b/src/images/goals/business.svg new file mode 100644 index 0000000..ce1a980 --- /dev/null +++ b/src/images/goals/business.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/goals/community.svg b/src/images/goals/community.svg new file mode 100644 index 0000000..d444227 --- /dev/null +++ b/src/images/goals/community.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/goals/education.svg b/src/images/goals/education.svg new file mode 100644 index 0000000..14ca7df --- /dev/null +++ b/src/images/goals/education.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/goals/travel.svg b/src/images/goals/travel.svg new file mode 100644 index 0000000..e773acb --- /dev/null +++ b/src/images/goals/travel.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/goals/work.svg b/src/images/goals/work.svg new file mode 100644 index 0000000..ce1a980 --- /dev/null +++ b/src/images/goals/work.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/images/svg/search_grey.svg b/src/images/svg/search_grey.svg new file mode 100644 index 0000000..c2f0eea --- /dev/null +++ b/src/images/svg/search_grey.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/pages/Chats/ActiveChat/ActiveChat.module.scss b/src/pages/Chats/ActiveChat/ActiveChat.module.scss new file mode 100644 index 0000000..d0d23ba --- /dev/null +++ b/src/pages/Chats/ActiveChat/ActiveChat.module.scss @@ -0,0 +1,37 @@ +.chat { + width: 806px; + height: 733px; + background-color: var(--color-base-white); + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + + &__greeting { + display: flex; + flex-direction: column; + text-align: center; + } + + &__welcometext { + font: var(--font-text-18-140-400); + max-width: 300px; + } + + &__searchbtn { + border: none; + padding: 0; + margin-left: 6px; + background-color: var(--color-base-white); + color: var(--accent-color-violet); + text-decoration: underline; + text-underline-offset: 4px; + } + + &__idleimg { + background-image: url(../../../images/chat_idle_background.png); + background-size: contain; + width: 336px; + height: 336px; + } +} diff --git a/src/pages/Chats/ActiveChat/ActiveChat.tsx b/src/pages/Chats/ActiveChat/ActiveChat.tsx new file mode 100644 index 0000000..f3e4ca1 --- /dev/null +++ b/src/pages/Chats/ActiveChat/ActiveChat.tsx @@ -0,0 +1,48 @@ +import { FC } from "react"; +import styles from "./ActiveChat.module.scss"; +import { useNavigate } from "react-router-dom"; +import { Input } from "../../../components/UI/Input/Input"; + +import newChatsSocket from "../Websocket/Websocket"; + +interface ActiveChatProps { + isActive: boolean; +} + +const ActiveChat: FC = ({ isActive }) => { + const navigate = useNavigate(); + + const handleButtonClick = () => { + newChatsSocket(); + }; + + const handlePartnerSearchBtn = () => { + navigate("/"); + }; + + return ( +
+ {!isActive ? ( +
+

+ Выберите чат или отправляйтесь на + +

+
+
+ ) : ( + <> +
+ ); +}; + +export default ActiveChat; diff --git a/src/pages/Chats/AllChats/AllChats.module.scss b/src/pages/Chats/AllChats/AllChats.module.scss new file mode 100644 index 0000000..92d728c --- /dev/null +++ b/src/pages/Chats/AllChats/AllChats.module.scss @@ -0,0 +1,6 @@ +.allchats { + width: 336px; + height: 733px; + background-color: var(--color-base-white); + border-radius: 20px; +} diff --git a/src/pages/Chats/AllChats/AllChats.tsx b/src/pages/Chats/AllChats/AllChats.tsx new file mode 100644 index 0000000..8a33177 --- /dev/null +++ b/src/pages/Chats/AllChats/AllChats.tsx @@ -0,0 +1,12 @@ +import styles from "./AllChats.module.scss"; +import ChatsMenu from "../ChatsMenu/ChatsMenu"; + +const ChatsList = () => { + return ( +
+ +
+ ); +}; + +export default ChatsList; diff --git a/src/pages/Chats/Buttons/MenuButton/MenuButton.module.scss b/src/pages/Chats/Buttons/MenuButton/MenuButton.module.scss new file mode 100644 index 0000000..51f4a19 --- /dev/null +++ b/src/pages/Chats/Buttons/MenuButton/MenuButton.module.scss @@ -0,0 +1,12 @@ +.button { + border: none; + padding: 0; + background-color: var(--color-base-white); + font: var(--font-text-18-100-400); + + &_active { + text-decoration: underline; + text-underline-offset: 6px; + text-decoration-color: var(--accent-color-salad); + } +} diff --git a/src/pages/Chats/Buttons/MenuButton/MenuButton.tsx b/src/pages/Chats/Buttons/MenuButton/MenuButton.tsx new file mode 100644 index 0000000..0a75254 --- /dev/null +++ b/src/pages/Chats/Buttons/MenuButton/MenuButton.tsx @@ -0,0 +1,25 @@ +import { FC } from "react"; +import cn from "classnames"; +import styles from "./MenuButton.module.scss"; + +interface MenuButtonProps { + onClick: () => void; + isActive: boolean; + title: string; +} + +const MenuButton: FC = ({ onClick, title, isActive }) => { + const buttonActiveClass = isActive ? styles.button_active : ""; + + return ( + + ); +}; + +export default MenuButton; diff --git a/src/pages/Chats/Chats.module.scss b/src/pages/Chats/Chats.module.scss new file mode 100644 index 0000000..3b44603 --- /dev/null +++ b/src/pages/Chats/Chats.module.scss @@ -0,0 +1,12 @@ +.chats { + position: relative; + display: flex; + justify-content: center; + gap: 16px; + padding: 32px 0 80px 0; + + &__backbtn { + position: absolute; + left: 24px; + } +} diff --git a/src/pages/Chats/Chats.tsx b/src/pages/Chats/Chats.tsx new file mode 100644 index 0000000..1602a8d --- /dev/null +++ b/src/pages/Chats/Chats.tsx @@ -0,0 +1,33 @@ +import { useNavigate } from "react-router-dom"; +import Header from "../../components/Header/Header"; +import Footer from "../../components/Footer/Footer"; +import GoBackButton from "../../components/UI/GoBackButton/GoBackButton"; +import AllChats from "./AllChats/AllChats"; +import ActiveChat from "./ActiveChat/ActiveChat"; +import styles from "./Chats.module.scss"; + +const Chats = () => { + const navigate = useNavigate(); + + // возврат на главную (для кнопки-стрелки) + const handleBackButtonClick = () => { + navigate("/"); + }; + + return ( + <> +
+
+ + + +
+
+ + ); +}; + +export default Chats; diff --git a/src/pages/Chats/ChatsMenu/ChatsMenu.module.scss b/src/pages/Chats/ChatsMenu/ChatsMenu.module.scss new file mode 100644 index 0000000..b55fcca --- /dev/null +++ b/src/pages/Chats/ChatsMenu/ChatsMenu.module.scss @@ -0,0 +1,13 @@ +@import 'src/vendor/_mixins.scss'; + +.menu { + @include flex-center-space-between; + padding: 16px; + border-bottom: 1px solid var(--grey-color-50); + + &__filters { + height: 34px; + display: flex; + gap: 16px; + } +} diff --git a/src/pages/Chats/ChatsMenu/ChatsMenu.tsx b/src/pages/Chats/ChatsMenu/ChatsMenu.tsx new file mode 100644 index 0000000..593d911 --- /dev/null +++ b/src/pages/Chats/ChatsMenu/ChatsMenu.tsx @@ -0,0 +1,36 @@ +import { useState } from "react"; +import SearchButton from "../../../components/UI/SearchButton/SearchButton"; +import MenuButton from "../Buttons/MenuButton/MenuButton"; +import styles from "./ChatsMenu.module.scss"; + +const ChatsMenu = () => { + const [activeFilter, setActiveFilter] = useState("Chats"); + + const handleFilterClick = (filter: string) => { + setActiveFilter(filter); + }; + + const handleSearchClick = () => { + // TO DO: Handle search + }; + + return ( +
+
+ handleFilterClick("Chats")} + isActive={activeFilter === "Chats"} + title="Чаты" + /> + handleFilterClick("Groups")} + isActive={activeFilter === "Groups"} + title="Группы" + /> +
+ +
+ ); +}; + +export default ChatsMenu; diff --git a/src/pages/Chats/Websocket/Websocket.ts b/src/pages/Chats/Websocket/Websocket.ts new file mode 100644 index 0000000..41f244f --- /dev/null +++ b/src/pages/Chats/Websocket/Websocket.ts @@ -0,0 +1,39 @@ +const newChatsSocket = () => { + const chatsSocket = new WebSocket( + "ws://lingvogo.acceleratorpracticum.ru/ws/chats/1/" + ); + + // chatsSocket.send("Here's some text that the server is urgently awaiting!"); + + console.log(chatsSocket.readyState); + + chatsSocket.onopen = (event) => { + console.log(chatsSocket.readyState); + sendText(); + }; + + function sendText() { + // Construct a msg object containing the data the server needs to process the message from the chat client. + const msg = { + type: "chat_message", + message: "document.getElementById.value", + // id: clientID, + // date: Date.now(), + }; + + // Send the msg object as a JSON-formatted string. + chatsSocket.send(JSON.stringify(msg)); + + // Blank the text input element, ready to receive the next line of text from the user. + // document.getElementById("text").value = ""; + } + + chatsSocket.onmessage = (event) => { + console.log(chatsSocket.readyState); + console.log(event.data); + }; + // + // chatsSocket.close(); +}; + +export default newChatsSocket; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts index 1d9d3e7..bb11d74 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts @@ -3,14 +3,13 @@ import { useNavigate } from "react-router-dom"; import { useLocalObservable } from "mobx-react-lite"; import { getMe } from "../../../utils/rest/auth"; -import { updateProfile } from "../../../utils/rest/updateProfile"; import { session } from "../../../models/session/Session"; import { GenderEnum } from "../../../utils/openapi"; import { api, headersWithToken as headers } from "../../../utils/constants"; import { store } from "../../../models/store"; -import fs from "fs"; +import base64js from "base64-js"; export const useModel = () => { const navigate = useNavigate(); @@ -24,6 +23,7 @@ export const useModel = () => { calculatedBirthday: "", gender: "Male" as GenderEnum | null, avatar: "", + avatarBase64: "", avatarFile: null as File | null, previewAvatar: "", error: { firstName: "", birthdate: "", avatar: "" }, @@ -94,35 +94,15 @@ export const useModel = () => { value: fileSrc, }); - const imageBuffer = fs.readFileSync(fileSrc); - const base64Image = imageBuffer.toString("base64"); - - console.log(base64Image); - - // const reader = new FileReader(); - // reader.onload = () => { - // const base64Data = reader.result; - // if (base64Data) { - // model.avatarFile = base64Data; - // } - // console.log("imageBase64", reader.result); - // reader.readAsDataURL(file); - - // if (base64Data && typeof base64Data === "string") { - // // Создаем объект типа File, преобразовывая строку Base64 в Blob - // const blob = new Blob([base64Data], { - // type: "image/png" || "image/jpg" || "image/jpeg", - // }); - // const file = new File( - // [blob], - // "filename.png" || "filename.jpg" || "filename.jpeg" - // ); - // - // model.avatarFile = blob; - // } - // }; - - console.log("imageBase64", model.avatarFile); + const reader = new FileReader(); + reader.onload = function (event) { + if (event.target) { + const base64Image = event.target.result; + console.log(base64Image); + model.avatarFile = base64Image; + } + }; + reader.readAsDataURL(file); model.handleModalClose(); console.log(model.avatar); @@ -136,6 +116,53 @@ export const useModel = () => { handleSetAvatar() { model.avatar = model.previewAvatar; + + const string = model.avatar as string; + + // const reader = new FileReader(); + // reader.onload = function (event) { + // if (event.target) { + // const base64Image = event.target.result; + // console.log(base64Image); + // model.avatarFile = base64Image; + // } + // }; + // reader.readAsDataURL(file); + + function Base64Encode(inputString: string, encoding = "utf-8") { + const encoder = new TextEncoder(); + const bytes = encoder.encode(inputString); + console.log(base64js.fromByteArray(bytes)); + model.avatarBase64 = base64js.fromByteArray(bytes); + } + + Base64Encode(string); + + const base64toBlob = ( + base64: string, + onsuccess: (blob: Blob | null) => void + ) => { + const img = new Image(); + + img.onload = function onload() { + const canvas = document.createElement("canvas"); + + canvas.height = img.height; + canvas.width = img.width; + + const ctx = canvas.getContext("2d"); + if (ctx) { + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + } + canvas.toBlob(onsuccess); + console.log(canvas.toBlob(onsuccess)); + }; + img.src = base64; + }; + + base64toBlob(model.avatarBase64, model.avatarFile); + console.log(model.avatarFile); + model.handleModalClose(); }, diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage4/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage4/model.ts new file mode 100644 index 0000000..5031dcc --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage4/model.ts @@ -0,0 +1,132 @@ +import { FormEvent } from "react"; +import { useNavigate } from "react-router-dom"; +import { useLocalObservable } from "mobx-react-lite"; +import { toJS } from "mobx"; + +import { getMe } from "../../../utils/rest/auth"; +import { session } from "../../../models/session/Session"; + +import { api, headersWithToken as headers } from "../../../utils/constants"; +import { store } from "../../../models/store"; +import styles from "../FillOutProfilePages.module.scss"; + +interface selectedGoals { + icon: string; + name: string; + description: string; + active: boolean; +} +export const useModel = () => { + const navigate = useNavigate(); + const user = store.session.user; + console.log(user); + + const model = useLocalObservable(() => { + return { + selectedGoals: [] as selectedGoals[], + commonGoals: [] as selectedGoals[], + error: { goals: "" }, + message: "", + isSubmitButtonDisabled: false, + isLoading: false, + + async handleCurrentUser() { + try { + const user = await getMe(); + + if (user) { + session.updateUser(user); + + model.commonGoals = model.selectedGoals.filter((goal) => { + if (user.goals) { + user.goals.includes(goal.name); + } + }); + console.log(toJS(model.selectedGoals)); + console.log(model.commonGoals); + } + } catch (error: any) { + model.message = error.message; + } + }, + + handleSubmitButtonDisabled() { + toJS(model.selectedGoals).length === 0 + ? (model.isSubmitButtonDisabled = true) + : (model.isSubmitButtonDisabled = false); + console.log(model.isSubmitButtonDisabled); + }, + + handleReturnButtonClick() { + navigate("/fill-out-3"); + }, + + handleGoalButtonClick(event: any, goal: any) { + const updatedGoals = [...model.selectedGoals]; + + console.log(goal.active); + + if (!goal.active) { + goal.active = true; + updatedGoals.push(goal); + model.selectedGoals = updatedGoals; + event.currentTarget.classList.add( + styles.container__goals_goal_active + ); + console.log(model.selectedGoals); + } else { + goal.active = false; + model.selectedGoals.filter((selectedGoal) => selectedGoal.active); + event.currentTarget.classList.remove( + styles.container__goals_goal_active + ); + console.log(model.selectedGoals); + } + }, + + async handleSubmit(event: FormEvent) { + event.preventDefault(); + + model.error = { + goals: "", + }; + + if (model.selectedGoals === null) { + model.error.goals = "Пожалуйста, выберите цели"; + } + + if (model.error.goals !== "") { + return; + } + + model.message = ""; + model.isLoading = true; + const goals = model.selectedGoals.map((goal) => goal.description); + try { + const getUpdateUser = await api.api.usersMePartialUpdate( + { + goals: goals, + }, + { headers } + ); + + if (getUpdateUser && user) { + store.session.updateUser({ + ...user, + goals: goals, + }); + } + + navigate("/fill-out-5"); + model.isLoading = false; + } catch (error: any) { + console.log("fill-out-4 error:", error); + console.log(goals); + model.isLoading = false; + } + }, + }; + }); + + return model; +}; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx index 1fb02ca..242c475 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite"; import Header from "../../../components/Header/Header"; import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; import { Button } from "../../../components/UI/Button/Button"; -import CountriesAndInterestsSelection from "../../../components/InterestsSelection/InterestsSelection"; +import InterestsSelection from "../../../components/InterestsSelection/InterestsSelection"; import { useModel } from "./model"; @@ -26,11 +26,9 @@ const FillOutProfilePage5 = () => {

Укажите Ваши интересы

-
- {pageName === "Sort" || - (pageName === "FillOutProfile3" && - Object.entries(skillLevelNames).map(([key, level]) => ( - - )))} + {(pageName === "Sort" || pageName === "FillOutProfile3") && + Object.entries(skillLevelNames).map(([key, level]) => ( + + ))}
); diff --git a/src/components/LanguageModule/LanguageModule.module.scss b/src/components/LanguageModule/LanguageModule.module.scss index db6d360..286bbc8 100644 --- a/src/components/LanguageModule/LanguageModule.module.scss +++ b/src/components/LanguageModule/LanguageModule.module.scss @@ -29,8 +29,5 @@ &_center { margin: 0 auto; - } - - } diff --git a/src/components/LanguageModule/LanguageModule.tsx b/src/components/LanguageModule/LanguageModule.tsx index 2b7f0a1..c2faa23 100644 --- a/src/components/LanguageModule/LanguageModule.tsx +++ b/src/components/LanguageModule/LanguageModule.tsx @@ -25,6 +25,7 @@ interface LanguageModuleProps { skillLevels: SkillLevelEnum[]; }[]) => void; } + const LanguageModule: FC = ({ pageName, initialLanguageAndLevels, @@ -32,6 +33,19 @@ const LanguageModule: FC = ({ setSelectedLanguagesAndLevels, }) => { const [languagesData, setLanguagesData] = useState([]); + const [selectedLanguage, setSelectedLanguage] = useState( + null + ); + const [selectedLanguages, setSelectedLanguages] = useState([]); + + useEffect(() => { + const languagesInUse = selectedLanguagesAndLevels.map( + (item) => item.language + ); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + setSelectedLanguages(languagesInUse); + }, [selectedLanguagesAndLevels]); console.log("selectedLanguageAndLevels:", selectedLanguagesAndLevels); @@ -52,40 +66,92 @@ const LanguageModule: FC = ({ fetchLanguagesData(); }, []); + const handleLanguageChange = (language: Language | null, index: number) => { + if (index >= 0 && index < selectedLanguagesAndLevels.length) { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels[index].language = language; + updatedLanguagesAndLevels[index].skillLevels = []; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + } + }; + + const handleSkillLevelsChange = ( + skillLevels: SkillLevelEnum[], + index: number + ) => { + if (index >= 0 && index < selectedLanguagesAndLevels.length) { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels[index].skillLevels = skillLevels; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + } + }; + + const handleReset = (index: number) => { + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels[index].language = null; + updatedLanguagesAndLevels[index].skillLevels = []; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + }; + + const handleClearFilter = () => { + const clearedLanguagesAndLevels = selectedLanguagesAndLevels.map( + (item) => ({ + language: null, + skillLevels: [], + }) + ); + setSelectedLanguagesAndLevels(clearedLanguagesAndLevels); + }; + + const handleAddLanguage = () => { + if (selectedLanguagesAndLevels.length < 3) { + const updatedLanguagesAndLevels = [ + ...selectedLanguagesAndLevels, + { language: null, skillLevels: [] }, + ]; + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); + handleSkillLevelsChange([], updatedLanguagesAndLevels.length - 1); + } + }; + const handleRemoveLanguage = (index: number) => { - setSelectedLanguagesAndLevels((prevLanguages) => { - const updatedLanguages = [...prevLanguages]; - updatedLanguages.splice(index, 1); - return updatedLanguages; - }); + const updatedLanguagesAndLevels = [...selectedLanguagesAndLevels]; + updatedLanguagesAndLevels.splice(index, 1); + setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); }; return (
+ {selectedLanguagesAndLevels.length === 0 && ( + handleLanguageChange(language, 0)} + onSkillLevelsChange={(skillLevels) => + handleSkillLevelsChange(skillLevels, 0) + } + onReset={() => handleReset(0)} + onRemoveLanguage={() => handleRemoveLanguage(0)} + /> + )} {selectedLanguagesAndLevels.map((item, index) => ( !selectedLanguages.includes(lang) + )} + selectedLanguage={item.language || selectedLanguage} initialLanguageAndLevels={initialLanguageAndLevels} selectedSkillLevels={item.skillLevels} - onLanguageChange={(language) => { - const updatedLanguagesAndLevels = [...[selectedLanguagesAndLevels]]; - updatedLanguagesAndLevels[index].language = language; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - }} - onSkillLevelsChange={(skillLevels) => { - const updatedLanguagesAndLevels = [...[selectedLanguagesAndLevels]]; - updatedLanguagesAndLevels[index].skillLevels = skillLevels; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - }} - onReset={() => { - const updatedLanguagesAndLevels = [...[selectedLanguagesAndLevels]]; - updatedLanguagesAndLevels[index].language = null; - updatedLanguagesAndLevels[index].skillLevels = []; - setSelectedLanguagesAndLevels(updatedLanguagesAndLevels); - }} + onLanguageChange={(language) => handleLanguageChange(language, index)} + onSkillLevelsChange={(skillLevels) => + handleSkillLevelsChange(skillLevels, index) + } + onReset={() => handleReset(index)} onRemoveLanguage={() => handleRemoveLanguage(index)} /> ))} diff --git a/src/components/Sort/Sort.module.scss b/src/components/Sort/Sort.module.scss index c7bf42b..6b099d0 100644 --- a/src/components/Sort/Sort.module.scss +++ b/src/components/Sort/Sort.module.scss @@ -169,9 +169,8 @@ display: flex; align-items: center; justify-content: space-between; - margin-bottom: 16px; width: 100%; - padding-top: 31px; + padding-top: 40px; gap: 16px; &__button { @@ -185,6 +184,7 @@ cursor: pointer; justify-content: center; align-items: center; + margin-bottom: 16px; } } @@ -197,6 +197,7 @@ font-size: 18px; font-weight: 500; line-height: normal; + margin-bottom: 16px; } //Кнопка добавить язык @@ -257,13 +258,14 @@ justify-content: center; } - //Блок выбора партнера + //Блок выбора партнера .partner { display: flex; flex-direction: column; margin-top: 40px; + &__gender { display: flex; justify-content: space-between; @@ -311,7 +313,7 @@ } } - //Блок выбора возраста + //Блок выбора возраста .age { border-radius: 8px; diff --git a/src/components/Sort/Sort.tsx b/src/components/Sort/Sort.tsx index 1618a4c..e91a807 100644 --- a/src/components/Sort/Sort.tsx +++ b/src/components/Sort/Sort.tsx @@ -1,5 +1,5 @@ import React, { useState, useMemo } from "react"; - +import styles from "../Sort/Sort.module.scss"; import CountrySelection from "../CountrySelection/CountrySelection"; import LanguageModule from "../LanguageModule/LanguageModule"; import Gender from "../Gender/Gender"; @@ -9,8 +9,6 @@ import { Button } from "../UI/Button/Button"; import { Country, Language, SkillLevelEnum } from "../../utils/openapi"; -import styles from "../Sort/Sort.module.scss"; - interface Filters { country: string; languages: { diff --git a/src/features/authenticate/authenticate.ts b/src/features/authenticate/authenticate.ts index dce1054..86055a0 100644 --- a/src/features/authenticate/authenticate.ts +++ b/src/features/authenticate/authenticate.ts @@ -15,6 +15,7 @@ export const authenticate = async (): Promise => { try { const refreshToken = localStorage.getItem("refreshToken"); if (refreshToken) { + store.session.setRefreshToken(refreshToken); const accessToken = await getAcceessToken(refreshToken); if (accessToken) { store.session.setAccessToken(accessToken.access); diff --git a/src/images/error-modal-image.png b/src/images/error-modal-image.png new file mode 100644 index 0000000000000000000000000000000000000000..558b3d3806b47b8a4fee09cc25021a74870bc136 GIT binary patch literal 10114 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 { const model = useModel(); @@ -198,6 +199,12 @@ const FillOutProfilePage1 = () => { Отменить изменения + +
diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts index bb11d74..b8dfce5 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage1/model.ts @@ -9,8 +9,6 @@ import { GenderEnum } from "../../../utils/openapi"; import { api, headersWithToken as headers } from "../../../utils/constants"; import { store } from "../../../models/store"; -import base64js from "base64-js"; - export const useModel = () => { const navigate = useNavigate(); const user = store.session.user; @@ -24,13 +22,15 @@ export const useModel = () => { gender: "Male" as GenderEnum | null, avatar: "", avatarBase64: "", - avatarFile: null as File | null, + avatarFile: "" as string | ArrayBuffer | null, previewAvatar: "", error: { firstName: "", birthdate: "", avatar: "" }, message: "", isLoading: false, isExportAvatarModal: false, isCreateAvatarModal: false, + isErrorModalOpen: false, + errorMessage: "", async handleCurrentUser() { try { @@ -51,6 +51,7 @@ export const useModel = () => { handleModalClose() { model.isExportAvatarModal = false; model.isCreateAvatarModal = false; + model.isErrorModalOpen = false; }, handleAvatarSelection(creationWay: string) { @@ -116,53 +117,6 @@ export const useModel = () => { handleSetAvatar() { model.avatar = model.previewAvatar; - - const string = model.avatar as string; - - // const reader = new FileReader(); - // reader.onload = function (event) { - // if (event.target) { - // const base64Image = event.target.result; - // console.log(base64Image); - // model.avatarFile = base64Image; - // } - // }; - // reader.readAsDataURL(file); - - function Base64Encode(inputString: string, encoding = "utf-8") { - const encoder = new TextEncoder(); - const bytes = encoder.encode(inputString); - console.log(base64js.fromByteArray(bytes)); - model.avatarBase64 = base64js.fromByteArray(bytes); - } - - Base64Encode(string); - - const base64toBlob = ( - base64: string, - onsuccess: (blob: Blob | null) => void - ) => { - const img = new Image(); - - img.onload = function onload() { - const canvas = document.createElement("canvas"); - - canvas.height = img.height; - canvas.width = img.width; - - const ctx = canvas.getContext("2d"); - if (ctx) { - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - } - canvas.toBlob(onsuccess); - console.log(canvas.toBlob(onsuccess)); - }; - img.src = base64; - }; - - base64toBlob(model.avatarBase64, model.avatarFile); - console.log(model.avatarFile); - model.handleModalClose(); }, @@ -204,7 +158,7 @@ export const useModel = () => { const getUpdateUser = await api.api.usersMePartialUpdate( { first_name: model.firstName, - avatar: model.avatarFile, + avatar: model.avatarFile as string, birth_date: model.birthdate, gender: model.gender, }, @@ -225,6 +179,13 @@ export const useModel = () => { model.isLoading = false; } catch (error: any) { console.log("fill-out-1 error:", error); + const secondError = Object.values(error)[1]; + const ErrorString = Object.values( + secondError as { [s: string]: unknown } + ).join("\n"); + console.log(ErrorString); + model.errorMessage = ErrorString; + model.isErrorModalOpen = true; model.isLoading = false; } }, diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx index a2bcc9b..5f362bd 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2.tsx @@ -10,7 +10,8 @@ import CountrySelection from "../../../components/CountrySelection/CountrySelect import { useModel } from "./model"; import styles from "../FillOutProfilePages.module.scss"; -import { Language, SkillLevelEnum } from "../../../utils/openapi"; +import { SkillLevelEnum } from "../../../utils/openapi"; +import ErrorModal from "../../../components/ErrorModal/ErrorModal"; const FillOutProfilePage2 = () => { const model = useModel(); @@ -73,6 +74,12 @@ const FillOutProfilePage2 = () => { Продолжить + +
diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts index a03fc2c..fa432b2 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage2/model.ts @@ -5,12 +5,7 @@ import { useLocalObservable } from "mobx-react-lite"; import { getMe } from "../../../utils/rest/auth"; import { session } from "../../../models/session/Session"; -import { - Country, - Language, - SkillLevelEnum, - UserLanguageRequest, -} from "../../../utils/openapi"; +import { Country, Language, SkillLevelEnum } from "../../../utils/openapi"; import { api, headersWithToken as headers } from "../../../utils/constants"; import { store } from "../../../models/store"; @@ -35,6 +30,8 @@ export const useModel = () => { message: "", isSubmitButtonDisabled: false, isLoading: false, + isErrorModalOpen: false, + errorMessage: "", async handleCurrentUser() { try { @@ -50,6 +47,10 @@ export const useModel = () => { } }, + handleModalClose() { + model.isErrorModalOpen = false; + }, + handleCountriesValue(countries: Country[]) { if (countries) { model.countries = countries; @@ -93,10 +94,10 @@ export const useModel = () => { }; if (model.countries === null) { - model.error.languages = "Пожалуйста, выберите язык"; + model.error.countries = "Пожалуйста, выберите страну"; } - if (model.languagesAndLevels === null) { + if (model.languagesAndLevels[0].language === null) { model.error.languages = "Пожалуйста, выберите язык"; } @@ -111,11 +112,19 @@ export const useModel = () => { try { const getUpdateUser = await api.api.usersMePartialUpdate( { - country: model.countries[0].code, + country: model.countries[0].name, languages: [ { isocode: model.languagesAndLevels[0].language?.isocode || "", - skill_level: {} as SkillLevelEnum, + skill_level: SkillLevelEnum.Native, + }, + { + isocode: model.languagesAndLevels[1].language?.isocode || "", + skill_level: SkillLevelEnum.Native, + }, + { + isocode: model.languagesAndLevels[2].language?.isocode || "", + skill_level: SkillLevelEnum.Native, }, ], }, @@ -130,7 +139,17 @@ export const useModel = () => { { language: model.languagesAndLevels[0].language?.name || "", isocode: model.languagesAndLevels[0].language?.isocode || "", - skill_level: {} as SkillLevelEnum, + skill_level: SkillLevelEnum.Native, + }, + { + language: model.languagesAndLevels[1].language?.name || "", + isocode: model.languagesAndLevels[1].language?.isocode || "", + skill_level: SkillLevelEnum.Native, + }, + { + language: model.languagesAndLevels[2].language?.name || "", + isocode: model.languagesAndLevels[2].language?.isocode || "", + skill_level: SkillLevelEnum.Native, }, ], }); @@ -141,6 +160,13 @@ export const useModel = () => { } catch (error: any) { console.log(model.languagesAndLevels[0]); console.log("fill-out-2 error:", error); + const secondError = Object.values(error)[1]; + const ErrorString = Object.values( + secondError as { [s: string]: unknown } + ).join("\n"); + console.log(ErrorString); + model.errorMessage = ErrorString; + model.isErrorModalOpen = true; model.isLoading = false; } }, diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx index e706ca7..7ac039a 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx @@ -1,22 +1,21 @@ -import React, { FormEvent, useEffect, useMemo, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import React, { useEffect, useMemo, useState } from "react"; +import { observer } from "mobx-react-lite"; import Header from "../../../components/Header/Header"; import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; import LanguageModule from "../../../components/LanguageModule/LanguageModule"; import { Button } from "../../../components/UI/Button/Button"; +import LanguageLevelModal from "../../../components/LanguageLevelModal/LanguageLevelModal"; import { Language, SkillLevelEnum } from "../../../utils/openapi"; +import { useModel } from "./model"; + import styles from "../FillOutProfilePages.module.scss"; import cn from "classnames"; -import LanguageLevelModal from "../../../components/LanguageLevelModal/LanguageLevelModal"; - -const FillOutProfilePage1 = () => { - const navigate = useNavigate(); - const [isModalOpen, setModalOpen] = useState(false); - const [isSubmitButtonDisabled, setSubmitButtonDisabled] = useState(true); +const FillOutProfilePage3 = () => { + const model = useModel(); const initialLanguageAndLevels = useMemo(() => { return { language: null, skillLevels: [] }; @@ -26,32 +25,9 @@ const FillOutProfilePage1 = () => { { language: Language | null; skillLevels: SkillLevelEnum[] }[] >([initialLanguageAndLevels]); - const handleSubmitButtonDisabled = () => { - for (let i = 0; i < selectedLanguagesAndLevels.length; i++) { - selectedLanguagesAndLevels[i].language === null || - selectedLanguagesAndLevels[i].skillLevels.toString() === [].toString() - ? setSubmitButtonDisabled(true) - : setSubmitButtonDisabled(false); - } - }; - useEffect(() => { - handleSubmitButtonDisabled(); - }, [selectedLanguagesAndLevels]); - - const handleReturnButtonClick = () => { - navigate("/fill-out-2"); - }; - - const handleHelpButtonClick = () => { - setModalOpen(true); - }; - - const handleFillOutPage3 = (event: FormEvent) => { - event.preventDefault(); - navigate("/fill-out-4"); - console.log("FillOutPage3"); - }; + model.handleSubmitButtonDisabled(); + }, [model.languagesAndLevels]); return ( <> @@ -59,14 +35,14 @@ const FillOutProfilePage1 = () => {

Выберите изучаемые языки

-
+
{ /> @@ -99,8 +75,8 @@ const FillOutProfilePage1 = () => {
@@ -108,4 +84,4 @@ const FillOutProfilePage1 = () => { ); }; -export default FillOutProfilePage1; +export default observer(FillOutProfilePage3); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts new file mode 100644 index 0000000..67bf559 --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts @@ -0,0 +1,146 @@ +import { FormEvent } from "react"; +import { useNavigate } from "react-router-dom"; +import { useLocalObservable } from "mobx-react-lite"; + +import { getMe } from "../../../utils/rest/auth"; +import { session } from "../../../models/session/Session"; + +import { Language, SkillLevelEnum } from "../../../utils/openapi"; +import { api, headersWithToken as headers } from "../../../utils/constants"; +import { store } from "../../../models/store"; + +export const useModel = () => { + const navigate = useNavigate(); + const user = store.session.user; + console.log(user); + + const model = useLocalObservable(() => { + return { + languagesAndLevels: [ + { + language: {} as Language | null, + skillLevels: [] as SkillLevelEnum[], + }, + ], + languages: [ + { isocode: "", language: "", skill_level: {} as SkillLevelEnum }, + ], + error: { languages: "" }, + message: "", + isSubmitButtonDisabled: false, + isLoading: false, + isHelpModalOpen: false, + errorMessage: "", + + async handleCurrentUser() { + try { + const user = await getMe(); + + if (user) { + session.updateUser(user); + model.languages = user.languages ?? []; + } + } catch (error: any) { + model.message = error.message; + } + }, + + handleHelpButtonClick() { + model.isHelpModalOpen = true; + }, + + handleModalClose() { + model.isHelpModalOpen = false; + }, + + handleLanguagesValue( + languages: { + language: Language | null; + skillLevels: SkillLevelEnum[]; + }[] + ) { + if (languages) { + model.languagesAndLevels = languages; + } + console.log(model.languages); + }, + + handleReturnButtonClick() { + navigate("/fill-out-2"); + }, + + handleSubmitButtonDisabled() { + for (let i = 0; i < model.languagesAndLevels.length; i++) { + model.languagesAndLevels[i].language === null + ? (model.isSubmitButtonDisabled = true) + : (model.isSubmitButtonDisabled = false); + } + }, + + async handleSubmit(event: FormEvent) { + event.preventDefault(); + + model.error = { + languages: "", + }; + + if (model.languagesAndLevels === null) { + model.error.languages = "Пожалуйста, выберите язык"; + } + + if (model.error.languages !== "") { + return; + } + + model.message = ""; + model.isLoading = true; + + try { + const getUpdateUser = await api.api.usersMePartialUpdate( + { + languages: [ + { + isocode: model.languagesAndLevels[0].language?.isocode || "", + skill_level: model.languagesAndLevels[0].skillLevels[0], + }, + ], + }, + { headers } + ); + + if (getUpdateUser && user) { + store.session.updateUser({ + ...user, + languages: [ + { + language: model.languagesAndLevels[0].language?.name || "", + isocode: model.languagesAndLevels[0].language?.isocode || "", + skill_level: model.languagesAndLevels[0].skillLevels[0], + }, + { + language: model.languagesAndLevels[1].language?.name || "", + isocode: model.languagesAndLevels[1].language?.isocode || "", + skill_level: model.languagesAndLevels[1].skillLevels[0], + }, + { + language: model.languagesAndLevels[2].language?.name || "", + isocode: model.languagesAndLevels[2].language?.isocode || "", + skill_level: model.languagesAndLevels[2].skillLevels[0], + }, + ], + }); + } + + navigate("/fill-out-4"); + model.isLoading = false; + } catch (error: any) { + console.log(model.languagesAndLevels[0]); + console.log("fill-out-3 error:", error); + model.isLoading = false; + } + }, + }; + }); + + return model; +}; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage5/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage5/model.ts index ee20f98..7ccf2aa 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage5/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage5/model.ts @@ -2,9 +2,6 @@ import { FormEvent } from "react"; import { useNavigate } from "react-router-dom"; import { useLocalObservable } from "mobx-react-lite"; -import { getMe } from "../../../utils/rest/auth"; -import { session } from "../../../models/session/Session"; - import { Interest } from "../../../utils/openapi"; import { api, headersWithToken as headers } from "../../../utils/constants"; import { store } from "../../../models/store"; @@ -12,7 +9,6 @@ import { store } from "../../../models/store"; export const useModel = () => { const navigate = useNavigate(); const user = store.session.user; - console.log(user); const model = useLocalObservable(() => { return { @@ -22,19 +18,6 @@ export const useModel = () => { isSubmitButtonDisabled: false, isLoading: false, - async handleCurrentUser() { - try { - const user = await getMe(); - - if (user) { - session.updateUser(user); - model.selectedInterests = user.interests ?? ""; - } - } catch (error: any) { - model.message = error.message; - } - }, - setSelectedInterests(selectedInterests: Interest[]) { if (selectedInterests) { model.selectedInterests = selectedInterests; @@ -74,12 +57,20 @@ export const useModel = () => { console.log(model.selectedInterests); + const finalInterests = model.selectedInterests.map((item) => ({ + name: item.name, + })); + + const finalInterestsArray = finalInterests.map((item) => item.name); + + console.log(finalInterestsArray); + model.message = ""; model.isLoading = true; try { const getUpdateUser = await api.api.usersMePartialUpdate( { - interests: model.selectedInterests, + interests: finalInterestsArray, }, { headers } ); @@ -87,11 +78,11 @@ export const useModel = () => { if (getUpdateUser && user) { store.session.updateUser({ ...user, - interests: model.selectedInterests, + interests: finalInterestsArray, }); } - navigate("/fill-out-3"); + navigate("/fill-out-6"); model.isLoading = false; } catch (error: any) { console.log(model.selectedInterests); diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage6/FillOutProfilePage6.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage6/FillOutProfilePage6.tsx index e1c9ee6..d4f1e1b 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage6/FillOutProfilePage6.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage6/FillOutProfilePage6.tsx @@ -1,31 +1,25 @@ -import { FormEvent } from "react"; -import { useNavigate } from "react-router-dom"; +import { observer } from "mobx-react-lite"; import Header from "../../../components/Header/Header"; import ProgressLine from "../../../components/UI/ProgressLine/ProgressLine"; import { Button } from "../../../components/UI/Button/Button"; import { Textarea } from "../../../components/UI/Textarea/Textarea"; -import { useModel } from "../FillOutProfilePage1/model"; +import { useModel } from "./model"; import styles from "../FillOutProfilePages.module.scss"; -import { observer } from "mobx-react-lite"; +import { useEffect } from "react"; const FillOutProfilePage6 = () => { const model = useModel(); - const navigate = useNavigate(); - - console.log(model.about); - const handleReturnButtonClick = () => { - navigate("/fill-out-5"); - }; + useEffect(() => { + model.handleCurrentUser(); + }, []); - const handleFillOutPage6 = (event: FormEvent) => { - event.preventDefault(); - navigate("/"); - console.log("FillOutPage6"); - }; + useEffect(() => { + model.handleSubmitButtonDisabled(); + }, [model.about]); return ( <> @@ -33,14 +27,14 @@ const FillOutProfilePage6 = () => {

Расскажите о себе

- + diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage6/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage6/model.ts new file mode 100644 index 0000000..749d16c --- /dev/null +++ b/src/pages/FillOutProfilePages/FillOutProfilePage6/model.ts @@ -0,0 +1,98 @@ +import { FormEvent } from "react"; +import { useNavigate } from "react-router-dom"; +import { useLocalObservable } from "mobx-react-lite"; + +import { getMe } from "../../../utils/rest/auth"; +import { session } from "../../../models/session/Session"; + +import { api, headersWithToken as headers } from "../../../utils/constants"; +import { store } from "../../../models/store"; + +export const useModel = () => { + const navigate = useNavigate(); + const user = store.session.user; + console.log(user); + + const model = useLocalObservable(() => { + return { + about: "", + error: { about: "" }, + message: "", + isSubmitButtonDisabled: false, + isLoading: false, + + async handleCurrentUser() { + try { + const user = await getMe(); + + if (user) { + session.updateUser(user); + model.about = user.about ?? ""; + } + } catch (error: any) { + model.message = error.message; + } + }, + + handleValue({ name, value }: { name: "about"; value: string }) { + model[name] = value; + }, + + handleReturnButtonClick() { + navigate("/fill-out-5"); + }, + + handleSubmitButtonDisabled() { + model.about.length < 1 + ? (model.isSubmitButtonDisabled = true) + : (model.isSubmitButtonDisabled = false); + }, + + to() { + return "/"; + }, + + async handleSubmit(event: FormEvent) { + event.preventDefault(); + + model.error = { + about: "", + }; + + if (model.about === "") { + model.error.about = "Пожалуйста, расскажите о себе"; + } + + if (model.error.about !== "") { + return; + } + + model.message = ""; + model.isLoading = true; + try { + const getUpdateUser = await api.api.usersMePartialUpdate( + { + about: model.about, + }, + { headers } + ); + + if (getUpdateUser && user) { + store.session.updateUser({ + ...user, + about: model.about, + }); + } + + navigate(model.to()); + model.isLoading = false; + } catch (error: any) { + console.log("fill-out-6 error:", error); + model.isLoading = false; + } + }, + }; + }); + + return model; +}; diff --git a/src/pages/UserProfile/UserProfile.tsx b/src/pages/UserProfile/UserProfile.tsx index 6856a08..1e2e4a9 100644 --- a/src/pages/UserProfile/UserProfile.tsx +++ b/src/pages/UserProfile/UserProfile.tsx @@ -48,8 +48,8 @@ interface Language { interface EditedData { first_name: string; username: string; - avatar?: File | null; - country: string | null; + avatar?: string | null | undefined; + country: string | undefined; birth_date: string; languages: Language[]; gender?: GenderEnum | NullEnum | null; diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index a5cb5ce..a2a3a87 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -278,7 +278,7 @@ export interface PatchedUserProfileRequest { */ first_name?: string; /** @format binary */ - avatar?: File | null; + avatar?: string | null; /** * Название * Наименование diff --git a/src/utils/rest/auth.ts b/src/utils/rest/auth.ts index 79afda8..50f36b8 100644 --- a/src/utils/rest/auth.ts +++ b/src/utils/rest/auth.ts @@ -1,5 +1,3 @@ -import { loggedIn } from "../../models/LoggedIn"; - import { TokenObtainPairRequest, TokenObtainPair, @@ -11,7 +9,6 @@ import { } from "../openapi"; import { api, headersWithToken as headers } from "../constants"; -import { session } from "../../models/session/Session"; export const signInWithEmail = async ({ username, @@ -77,7 +74,7 @@ export const getMe = async (): Promise => { avatar: user.avatar as string, age: user.age as string, slug: user.slug as string | null, - country: user.country as string | null, + country: user.country as string | undefined, languages: user.languages as UserLanguage[], gender: user.gender as GenderEnum | NullEnum | null, goals: user.goals as string[], diff --git a/src/utils/rest/updateProfile.ts b/src/utils/rest/updateProfile.ts index 276b233..5600e0d 100644 --- a/src/utils/rest/updateProfile.ts +++ b/src/utils/rest/updateProfile.ts @@ -33,8 +33,8 @@ export const updateProfile = async ({ await api.api.usersMePartialUpdate( { first_name, - avatar: avatar as File | null, - country: country as string | null | undefined, + avatar: avatar as string | null | undefined, + country: country as string | undefined, birth_date, languages, gender, From ede9dfd1a00cd98115d71bf008b89bd081bb8a87 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Fri, 1 Sep 2023 16:52:52 +0300 Subject: [PATCH 145/153] fix: final --- package.json | 1 - src/pages/Chats/ChatsMenu/ChatsMenu.tsx | 4 ++-- .../FillOutProfilePages/FillOutProfilePage3/model.ts | 10 ---------- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 8589dfd..9681092 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "conversation-exchange-frontend", - "homepage": "https://conversation-exchange.github.io/frontend", "version": "0.1.0", "private": true, "dependencies": { diff --git a/src/pages/Chats/ChatsMenu/ChatsMenu.tsx b/src/pages/Chats/ChatsMenu/ChatsMenu.tsx index 593d911..a73f15f 100644 --- a/src/pages/Chats/ChatsMenu/ChatsMenu.tsx +++ b/src/pages/Chats/ChatsMenu/ChatsMenu.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import SearchButton from "../../../components/UI/SearchButton/SearchButton"; +// import SearchButton from "../../../components/UI/SearchButton/SearchButton"; import MenuButton from "../Buttons/MenuButton/MenuButton"; import styles from "./ChatsMenu.module.scss"; @@ -28,7 +28,7 @@ const ChatsMenu = () => { title="Группы" />
- + {/**/}
); }; diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts b/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts index 67bf559..dfcc12f 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3/model.ts @@ -117,16 +117,6 @@ export const useModel = () => { isocode: model.languagesAndLevels[0].language?.isocode || "", skill_level: model.languagesAndLevels[0].skillLevels[0], }, - { - language: model.languagesAndLevels[1].language?.name || "", - isocode: model.languagesAndLevels[1].language?.isocode || "", - skill_level: model.languagesAndLevels[1].skillLevels[0], - }, - { - language: model.languagesAndLevels[2].language?.name || "", - isocode: model.languagesAndLevels[2].language?.isocode || "", - skill_level: model.languagesAndLevels[2].skillLevels[0], - }, ], }); } From a26e07c11253e992e468eab5a70c9b2196c85e7d Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Fri, 1 Sep 2023 17:10:27 +0300 Subject: [PATCH 146/153] fix: final --- .eslintrc.json => eslintrc.json | 0 package-lock.json | 36 +++++++++++++++++++++++++-------- package.json | 3 ++- 3 files changed, 30 insertions(+), 9 deletions(-) rename .eslintrc.json => eslintrc.json (100%) diff --git a/.eslintrc.json b/eslintrc.json similarity index 100% rename from .eslintrc.json rename to eslintrc.json diff --git a/package-lock.json b/package-lock.json index 680298a..c3269b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,8 @@ "@types/react-dom": "^18.2.7", "classnames": "^2.3.2", "compose-function": "^3.0.3", + "eslint": "^8.48.0", + "eslint-plugin": "^1.0.1", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -22,7 +24,6 @@ "@babel/plugin-proposal-private-property-in-object": "*", "@types/compose-function": "^0.0.30", "css-loader": "^6.8.1", - "eslint": "^8.47.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.2", @@ -2455,9 +2456,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -7869,14 +7870,14 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -8018,6 +8019,17 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin/-/eslint-plugin-1.0.1.tgz", + "integrity": "sha512-ervp8C09On0fLA258TvE08AqAr/bhRYgHVZd3BrJjD4JfOA2JGANDLGs06j51oWqfPd7Feoo3OoqHD+fuI2sFQ==", + "dependencies": { + "requireindex": "~1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint-plugin-flowtype": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", @@ -16920,6 +16932,14 @@ "node": ">=0.10.0" } }, + "node_modules/requireindex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", + "integrity": "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==", + "engines": { + "node": ">=0.10.5" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", diff --git a/package.json b/package.json index 9681092..32f9bd0 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "@types/react-dom": "^18.2.7", "classnames": "^2.3.2", "compose-function": "^3.0.3", + "eslint": "^8.48.0", + "eslint-plugin": "^1.0.1", "mobx-react": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -47,7 +49,6 @@ "@babel/plugin-proposal-private-property-in-object": "*", "@types/compose-function": "^0.0.30", "css-loader": "^6.8.1", - "eslint": "^8.47.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.2", From 6c4b4b89edf2b11ee0b23ea4002040b1c5412ba0 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Fri, 1 Sep 2023 17:10:53 +0300 Subject: [PATCH 147/153] fix: final --- eslintrc.json => .eslintrc.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eslintrc.json => .eslintrc.json (100%) diff --git a/eslintrc.json b/.eslintrc.json similarity index 100% rename from eslintrc.json rename to .eslintrc.json From 1083f0fde8a3a3917b6c556bff7210273f2e80b2 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Fri, 1 Sep 2023 18:27:38 +0300 Subject: [PATCH 148/153] fix: final --- .prettierrc.json | 3 ++- src/components/SignupSigninForm/model.ts | 2 ++ .../FillOutProfilePage3/FillOutProfilePage3.tsx | 8 ++------ .../FillOutProfilePages/FillOutProfilePage3/model.ts | 1 + src/pages/MainPage/MainPage.tsx | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index fed3783..4cdb3d8 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -4,5 +4,6 @@ "useTabs": false, "semi": true, "trailingComma": "es5", - "bracketSpacing": true + "bracketSpacing": true, + "error": null } diff --git a/src/components/SignupSigninForm/model.ts b/src/components/SignupSigninForm/model.ts index 0ce05c8..8fad515 100644 --- a/src/components/SignupSigninForm/model.ts +++ b/src/components/SignupSigninForm/model.ts @@ -101,6 +101,8 @@ export const useModel = () => { }); if (token) { + localStorage.setItem("accessToken", token.access); + localStorage.setItem("refreshToken", token.refresh); session.setAccessToken(token.access); session.setRefreshToken(token.refresh); } diff --git a/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx b/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx index 7ac039a..9979501 100644 --- a/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx +++ b/src/pages/FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3.tsx @@ -21,10 +21,6 @@ const FillOutProfilePage3 = () => { return { language: null, skillLevels: [] }; }, []); - const [selectedLanguagesAndLevels, setSelectedLanguagesAndLevels] = useState< - { language: Language | null; skillLevels: SkillLevelEnum[] }[] - >([initialLanguageAndLevels]); - useEffect(() => { model.handleSubmitButtonDisabled(); }, [model.languagesAndLevels]); @@ -52,8 +48,8 @@ const FillOutProfilePage3 = () => {
- {/**/} +
@@ -90,7 +90,7 @@ const MainPage = () => { className={cn( styles.content__cardListAndSortPopup_cardListArea_cardList, isSortPopupOpen && - styles.content__cardListAndSortPopup_cardListArea_cardList_narrow + styles.content__cardListAndSortPopup_cardListArea_cardList_narrow, )} > {isUsersList && diff --git a/src/pages/Routing.tsx b/src/pages/Routing.tsx index 0d40a06..86c4dab 100644 --- a/src/pages/Routing.tsx +++ b/src/pages/Routing.tsx @@ -1,58 +1,58 @@ -import { lazy } from "react"; -import { Route, Routes } from "react-router-dom"; +import { lazy } from 'react'; +import { Route, Routes } from 'react-router-dom'; -import { RequireAuth } from "./RequireAuth/RequireAuth"; +import { RequireAuth } from './RequireAuth/RequireAuth'; -const SignInPage = lazy(() => import("./SignupSigninPage/SignupSigninPage")); -const SignUpPage = lazy(() => import("./SignupSigninPage/SignupSigninPage")); -const MainPage = lazy(() => import("./MainPage/MainPage")); -const NotFoundPage = lazy(() => import("./NotFoundPage/NotFoundPage")); -const FAQPage = lazy(() => import("./FAQPage/FAQPage")); -const ReviewsPage = lazy(() => import("./ReviewsPage/ReviewsPage")); -const PolicyPage = lazy(() => import("./PolicyPage/PolicyPage")); -const RulesPage = lazy(() => import("./RulesPage/RulesPage")); -const AgreementPage = lazy(() => import("./AgreementPage/AgreementPage")); -const ProfilePage = lazy(() => import("./UserProfile/UserProfile")); +const SignInPage = lazy(() => import('./SignupSigninPage/SignupSigninPage')); +const SignUpPage = lazy(() => import('./SignupSigninPage/SignupSigninPage')); +const MainPage = lazy(() => import('./MainPage/MainPage')); +const NotFoundPage = lazy(() => import('./NotFoundPage/NotFoundPage')); +const FAQPage = lazy(() => import('./FAQPage/FAQPage')); +const ReviewsPage = lazy(() => import('./ReviewsPage/ReviewsPage')); +const PolicyPage = lazy(() => import('./PolicyPage/PolicyPage')); +const RulesPage = lazy(() => import('./RulesPage/RulesPage')); +const AgreementPage = lazy(() => import('./AgreementPage/AgreementPage')); +const ProfilePage = lazy(() => import('./UserProfile/UserProfile')); const FillOutProfilePage1 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1") + () => import('./FillOutProfilePages/FillOutProfilePage1/FillOutProfilePage1'), ); const FillOutProfilePage2 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2") + () => import('./FillOutProfilePages/FillOutProfilePage2/FillOutProfilePage2'), ); const FillOutProfilePage3 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3") + () => import('./FillOutProfilePages/FillOutProfilePage3/FillOutProfilePage3'), ); const FillOutProfilePage4 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4") + () => import('./FillOutProfilePages/FillOutProfilePage4/FillOutProfilePage4'), ); const FillOutProfilePage5 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5") + () => import('./FillOutProfilePages/FillOutProfilePage5/FillOutProfilePage5'), ); const FillOutProfilePage6 = lazy( - () => import("./FillOutProfilePages/FillOutProfilePage6/FillOutProfilePage6") + () => import('./FillOutProfilePages/FillOutProfilePage6/FillOutProfilePage6'), ); export const Routing = () => { return ( {/*}>*/} - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> {/**/} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> ); }; diff --git a/src/utils/getTime.ts b/src/utils/getTime.ts index 4fd0d5a..b9ccc76 100644 --- a/src/utils/getTime.ts +++ b/src/utils/getTime.ts @@ -1,7 +1,7 @@ const currentTime = new Date(); let hours = currentTime.getHours(); let minutes = currentTime.getMinutes(); -const amOrPm = hours >= 12 ? "PM" : "AM"; +const amOrPm = hours >= 12 ? 'PM' : 'AM'; if (hours > 12) { hours -= 12; From 7921cabd6c242a9d5e2ae5002b0053e2dba81fa4 Mon Sep 17 00:00:00 2001 From: BerezinaMariya Date: Mon, 4 Sep 2023 16:32:59 +0300 Subject: [PATCH 150/153] fix: prettier, "singleQuote": true --- .prettierrc.json | 12 ++- src/components/App/App.tsx | 6 +- src/components/App/providers/index.tsx | 8 +- src/components/App/providers/withAuth.ts | 16 +-- src/components/App/providers/withRouter.tsx | 8 +- src/components/App/providers/withStore.tsx | 8 +- src/components/Avatars/Avatars.tsx | 12 +-- src/components/Avatars/AvatarsIcons.ts | 20 ++-- src/components/Card/Card.tsx | 24 ++--- src/components/Categories/Categories.tsx | 12 +-- .../CountrySelection/CountrySelection.tsx | 98 ++++++++--------- src/components/ErrorModal/ErrorModal.tsx | 10 +- src/components/Footer/Footer.tsx | 22 ++-- src/components/Gender/Gender.tsx | 44 ++++---- src/components/Header/Header.tsx | 82 +++++++------- .../InputSearchList/InputSearchList.tsx | 68 ++++++------ .../InterestsSelection/InterestsSelection.tsx | 12 +-- .../LanguageLevel/LanguageLevel.tsx | 75 ++++++------- .../LanguageLevelModal/LanguageLevelModal.tsx | 10 +- src/components/LanguageLevelModal/Levels.ts | 36 +++---- .../LanguageModule/LanguageModule.tsx | 44 ++++---- src/components/Modal/Modal.tsx | 16 +-- src/components/ModalOverlay/ModalOverlay.tsx | 16 +-- src/components/MoreCards/MoreCards.tsx | 10 +- .../MultiRangeSlider/MultiRangeSlider.tsx | 14 +-- .../PicturesBlock/PicturesBlock.tsx | 52 ++++----- .../QuestionBlock/QuestionBlock.tsx | 10 +- .../SignupSigninForm/SignupSigninForm.tsx | 74 ++++++------- src/components/SignupSigninForm/model.ts | 100 ++++++++--------- src/components/Sort/Sort.tsx | 44 ++++---- src/components/UI/Button/Button.tsx | 24 ++--- src/components/UI/CountryIcon/CountryIcon.tsx | 8 +- .../UI/GenderAndAgeIcon/GenderAndAgeIcon.tsx | 16 +-- .../UI/GoBackButton/GoBackButton.tsx | 8 +- src/components/UI/Input/Input.tsx | 28 ++--- .../UI/ItemsOpenedList/ItemsOpenedList.tsx | 12 +-- .../UI/LanguagesTag/LanguagesTag.tsx | 24 ++--- src/components/UI/Preloader/Preloader.tsx | 4 +- .../UI/ProgressLine/ProgressLine.tsx | 8 +- .../UI/SearchButton/SearchButton.tsx | 6 +- src/components/UI/Select/Select.tsx | 20 ++-- src/components/UI/Textarea/Textarea.tsx | 8 +- .../UserStatusIsOnline/UserStatusIsOnline.tsx | 12 +-- src/features/authenticate/authenticate.ts | 10 +- src/index.tsx | 12 +-- src/models/LoggedIn.ts | 2 +- src/models/session/Session.ts | 6 +- src/models/session/User.ts | 4 +- src/models/store.ts | 2 +- src/pages/AgreementPage/AgreementPage.tsx | 12 +-- src/pages/Chats/ActiveChat/ActiveChat.tsx | 14 +-- src/pages/Chats/AllChats/AllChats.tsx | 4 +- .../Chats/Buttons/MenuButton/MenuButton.tsx | 10 +- src/pages/Chats/Chats.tsx | 16 +-- src/pages/Chats/ChatsMenu/ChatsMenu.tsx | 20 ++-- src/pages/Chats/Websocket/Websocket.ts | 6 +- src/pages/FAQPage/FAQPage.tsx | 12 +-- .../FillOutProfilePage1.tsx | 101 +++++++++--------- .../FillOutProfilePage1/model.ts | 100 +++++++++-------- .../FillOutProfilePage2.tsx | 30 +++--- .../FillOutProfilePage2/model.ts | 70 ++++++------ .../FillOutProfilePage3.tsx | 34 +++--- .../FillOutProfilePage3/model.ts | 48 ++++----- .../FillOutProfilePage4.tsx | 22 ++-- .../FillOutProfilePage4/Goals.ts | 36 +++---- .../FillOutProfilePage4/model.ts | 42 ++++---- .../FillOutProfilePage5.tsx | 18 ++-- .../FillOutProfilePage5/model.ts | 32 +++--- .../FillOutProfilePage6.tsx | 24 ++--- .../FillOutProfilePage6/model.ts | 42 ++++---- src/pages/MainPage/MainPage.tsx | 38 +++---- src/pages/NotFoundPage/NotFoundPage.tsx | 20 ++-- src/pages/PolicyPage/PolicyPage.tsx | 10 +- src/pages/RequireAuth/RequireAuth.tsx | 8 +- src/pages/RequireAuth/model.ts | 6 +- src/pages/ReviewsPage/ReviewsPage.tsx | 8 +- src/pages/Routing.tsx | 74 ++++++------- src/pages/RulesPage/RulesPage.tsx | 6 +- .../SignupSigninPage/SignupSigninPage.tsx | 14 +-- src/pages/UserProfile/About/About.tsx | 10 +- .../Buttons/EditButton/EditButton.tsx | 6 +- .../Buttons/IconButton/IconButton.tsx | 8 +- .../UserProfile/Certificates/Certificates.tsx | 2 +- .../LevelLanguage/LevelLanguage.tsx | 2 +- src/pages/UserProfile/Reviews/Reviews.tsx | 2 +- src/pages/UserProfile/Topics/Topics.tsx | 28 ++--- src/pages/UserProfile/UserCard/UserCard.tsx | 94 ++++++++-------- .../UserLanguages/UserLanguages.tsx | 14 +-- src/pages/UserProfile/UserProfile.tsx | 82 +++++++------- src/types/declarations.d.ts | 12 +-- src/utils/constants.ts | 58 +++++----- src/utils/getTime.ts | 2 +- src/utils/rest/auth.ts | 12 +-- src/utils/rest/getCountries.ts | 4 +- src/utils/rest/getInterests.ts | 4 +- src/utils/rest/getLanguages.ts | 4 +- src/utils/rest/register.ts | 4 +- src/utils/rest/updateProfile.ts | 10 +- 98 files changed, 1214 insertions(+), 1198 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 4cdb3d8..8067c0c 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,9 +1,13 @@ { + "semi": true, + "trailingComma": "all", + "singleQuote": true, "printWidth": 80, "tabWidth": 2, + + "parser": "typescript", "useTabs": false, - "semi": true, - "trailingComma": "es5", - "bracketSpacing": true, - "error": null + + "jsxSingleQuote": true, + "arrowParens": "always" } diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 605da86..9757ebb 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -1,6 +1,6 @@ -import React from "react"; -import { Routing } from "../../pages/Routing"; -import { withProviders } from "./providers"; +import React from 'react'; +import { Routing } from '../../pages/Routing'; +import { withProviders } from './providers'; function Component() { return ; diff --git a/src/components/App/providers/index.tsx b/src/components/App/providers/index.tsx index aacf44a..9e5b020 100644 --- a/src/components/App/providers/index.tsx +++ b/src/components/App/providers/index.tsx @@ -1,7 +1,7 @@ -import compose from "compose-function"; +import compose from 'compose-function'; -import { withStore } from "./withStore"; -import { withRouter } from "./withRouter"; -import { withAuth } from "./withAuth"; +import { withStore } from './withStore'; +import { withRouter } from './withRouter'; +import { withAuth } from './withAuth'; export const withProviders = compose(withStore, withRouter, withAuth); diff --git a/src/components/App/providers/withAuth.ts b/src/components/App/providers/withAuth.ts index d52d758..77e50d7 100644 --- a/src/components/App/providers/withAuth.ts +++ b/src/components/App/providers/withAuth.ts @@ -1,10 +1,10 @@ -import type { ReactNode } from "react"; -import { useEffect } from "react"; +import type { ReactNode } from 'react'; +import { useEffect } from 'react'; -import { authenticate } from "../../../features/authenticate/authenticate"; -import { store } from "../../../models/store"; -import { session } from "../../../models/session/Session"; -import { GenderEnum, NullEnum, UserLanguage } from "../../../utils/openapi"; +import { authenticate } from '../../../features/authenticate/authenticate'; +import { store } from '../../../models/store'; +import { session } from '../../../models/session/Session'; +import { GenderEnum, NullEnum, UserLanguage } from '../../../utils/openapi'; export const withAuth = (component: () => ReactNode) => () => { useEffect(() => { @@ -12,8 +12,8 @@ export const withAuth = (component: () => ReactNode) => () => { await authenticate(); })(); - const accessToken = localStorage.getItem("accessToken"); - const refreshToken = localStorage.getItem("refreshToken"); + const accessToken = localStorage.getItem('accessToken'); + const refreshToken = localStorage.getItem('refreshToken'); if (accessToken) { if (session?.user) { diff --git a/src/components/App/providers/withRouter.tsx b/src/components/App/providers/withRouter.tsx index 1ebc751..1481c41 100644 --- a/src/components/App/providers/withRouter.tsx +++ b/src/components/App/providers/withRouter.tsx @@ -1,7 +1,7 @@ -import React from "react"; -import { ReactNode, Suspense } from "react"; -import { BrowserRouter } from "react-router-dom"; -import Preloader from "../../UI/Preloader/Preloader"; +import React from 'react'; +import { ReactNode, Suspense } from 'react'; +import { BrowserRouter } from 'react-router-dom'; +import Preloader from '../../UI/Preloader/Preloader'; export const withRouter = (component: () => ReactNode) => () => ( diff --git a/src/components/App/providers/withStore.tsx b/src/components/App/providers/withStore.tsx index 71b6193..25cb842 100644 --- a/src/components/App/providers/withStore.tsx +++ b/src/components/App/providers/withStore.tsx @@ -1,10 +1,10 @@ -import { ReactNode, createContext, useContext } from "react"; -import { configure } from "mobx"; +import { ReactNode, createContext, useContext } from 'react'; +import { configure } from 'mobx'; -import { store } from "../../../models/store"; +import { store } from '../../../models/store'; configure({ - enforceActions: "always", + enforceActions: 'always', computedRequiresReaction: true, reactionRequiresObservable: true, observableRequiresReaction: true, diff --git a/src/components/Avatars/Avatars.tsx b/src/components/Avatars/Avatars.tsx index 967e394..6021316 100644 --- a/src/components/Avatars/Avatars.tsx +++ b/src/components/Avatars/Avatars.tsx @@ -1,9 +1,9 @@ -import React from "react"; +import React from 'react'; -import { avatarList } from "./AvatarsIcons"; +import { avatarList } from './AvatarsIcons'; -import styles from "./Avatars.module.scss"; -import cn from "classnames"; +import styles from './Avatars.module.scss'; +import cn from 'classnames'; interface AvatarProps { selectedAvatar: string; @@ -27,12 +27,12 @@ const Avatars = ({ selectedAvatar, setSelectedAvatar }: AvatarProps) => { styles.avatarsItems_avatarItem, selectedAvatar === avatar ? styles.avatarsItems_avatarItem_selected - : "" + : '', )} onClick={handleSetAvatar} key={i} > - Вариант изображения аватара + Вариант изображения аватара ) ); diff --git a/src/components/Avatars/AvatarsIcons.ts b/src/components/Avatars/AvatarsIcons.ts index 9d9bffe..d083638 100644 --- a/src/components/Avatars/AvatarsIcons.ts +++ b/src/components/Avatars/AvatarsIcons.ts @@ -1,13 +1,13 @@ -import avatarMale1 from "../../images/avatars/avatar-male-1.png"; -import avatarMale2 from "../../images/avatars/avatar-male-2.png"; -import avatarMale3 from "../../images/avatars/avatar-male-3.png"; -import avatarMale4 from "../../images/avatars/avatar-male-4.png"; -import avatarMale5 from "../../images/avatars/avatar-male-5.png"; -import avatarFemale1 from "../../images/avatars/avatar-female-1.png"; -import avatarFemale2 from "../../images/avatars/avatar-female-2.png"; -import avatarFemale3 from "../../images/avatars/avatar-female-3.png"; -import avatarFemale4 from "../../images/avatars/avatar-female-4.png"; -import avatarFemale5 from "../../images/avatars/avatar-female-5.png"; +import avatarMale1 from '../../images/avatars/avatar-male-1.png'; +import avatarMale2 from '../../images/avatars/avatar-male-2.png'; +import avatarMale3 from '../../images/avatars/avatar-male-3.png'; +import avatarMale4 from '../../images/avatars/avatar-male-4.png'; +import avatarMale5 from '../../images/avatars/avatar-male-5.png'; +import avatarFemale1 from '../../images/avatars/avatar-female-1.png'; +import avatarFemale2 from '../../images/avatars/avatar-female-2.png'; +import avatarFemale3 from '../../images/avatars/avatar-female-3.png'; +import avatarFemale4 from '../../images/avatars/avatar-female-4.png'; +import avatarFemale5 from '../../images/avatars/avatar-female-5.png'; export const avatarList = [ avatarMale1, diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 56cf70e..a4776b0 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,20 +1,20 @@ -import { FC } from "react"; +import { FC } from 'react'; -import CountryIcon from "../UI/CountryIcon/CountryIcon"; -import UserStatusIsOnline from "../UI/UserStatusIsOnline/UserStatusIsOnline"; -import GenderAndAgeIcon from "../UI/GenderAndAgeIcon/GenderAndAgeIcon"; -import LanguagesTag from "../UI/LanguagesTag/LanguagesTag"; +import CountryIcon from '../UI/CountryIcon/CountryIcon'; +import UserStatusIsOnline from '../UI/UserStatusIsOnline/UserStatusIsOnline'; +import GenderAndAgeIcon from '../UI/GenderAndAgeIcon/GenderAndAgeIcon'; +import LanguagesTag from '../UI/LanguagesTag/LanguagesTag'; -import arrows from "../../images/svg/card-arrows-parallel.svg"; +import arrows from '../../images/svg/card-arrows-parallel.svg'; import { UserLanguage, Country, GenderEnum, NullEnum, -} from "../../utils/openapi"; +} from '../../utils/openapi'; -import styles from "./Card.module.scss"; +import styles from './Card.module.scss'; interface ICards { first_name: string; @@ -51,7 +51,7 @@ const Card: FC = ({ Аватар пользователя
@@ -70,7 +70,7 @@ const Card: FC = ({ {languages && languages.map((languages: UserLanguage) => { return ( - languages.skill_level === "Native" && ( + languages.skill_level === 'Native' && ( = ({ Параллельные стрелки между изученными и изучаемыми языками
    {languages && languages.map((languages: UserLanguage) => { return ( - languages.skill_level !== "Native" && ( + languages.skill_level !== 'Native' && ( = memo( ({ value, onChangeCategory }) => { const categories: Category[] = [ - { name: "Все", path: "" }, - { name: "Сейчас онлайн", path: "True" }, - { name: "Новые пользователи", path: "-date_joined" }, + { name: 'Все', path: '' }, + { name: 'Сейчас онлайн', path: 'True' }, + { name: 'Новые пользователи', path: '-date_joined' }, ]; const setActiveStyle = (item: { name: string; path: string }) => { @@ -48,7 +48,7 @@ const Categories: React.FC = memo(
); - } + }, ); export default Categories; diff --git a/src/components/CountrySelection/CountrySelection.tsx b/src/components/CountrySelection/CountrySelection.tsx index d8628f3..5462edc 100644 --- a/src/components/CountrySelection/CountrySelection.tsx +++ b/src/components/CountrySelection/CountrySelection.tsx @@ -1,13 +1,13 @@ -import { FC, useEffect, useState } from "react"; +import { FC, useEffect, useState } from 'react'; -import { Button } from "../UI/Button/Button"; -import { Input } from "../UI/Input/Input"; +import { Button } from '../UI/Button/Button'; +import { Input } from '../UI/Input/Input'; -import { Country } from "../../utils/openapi"; -import { api } from "../../utils/constants"; +import { Country } from '../../utils/openapi'; +import { api } from '../../utils/constants'; -import styles from "./CountrySelection.module.scss"; -import classNames from "classnames"; +import styles from './CountrySelection.module.scss'; +import classNames from 'classnames'; interface CountrySelectionProps { pageName: string; @@ -24,9 +24,9 @@ const CountrySelection: FC = ({ }) => { const [countriesData, setCountriesData] = useState([]); const [isCountryListVisible, setCountryListVisible] = useState(false); - const [searchValue, setSearchValue] = useState(""); + const [searchValue, setSearchValue] = useState(''); const [isError, setIsError] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); + const [errorMessage, setErrorMessage] = useState(''); const [filteredCountries, setFilteredCountries] = useState(countriesData); const [selectedCountry, setSelectedCountry] = useState(null); @@ -35,14 +35,14 @@ const CountrySelection: FC = ({ number | null >(null); const [lastPressedLetter, setLastPressedLetter] = useState( - null + null, ); const fetchCountriesData = async () => { try { - console.log("отправка запроса ---"); + console.log('отправка запроса ---'); const response = await api.api.countriesList(); - console.log("ответ получен -", response); + console.log('ответ получен -', response); const countries = response.data.map((country) => ({ code: country.code, name: country.name, @@ -50,7 +50,7 @@ const CountrySelection: FC = ({ })); setCountriesData(countries); } catch (error) { - console.error("Ошибка при получении данных о странах:", error); + console.error('Ошибка при получении данных о странах:', error); } }; @@ -58,7 +58,7 @@ const CountrySelection: FC = ({ fetchCountriesData(); }, []); - const i = pageName === "Sort" ? 5 : 1; + const i = pageName === 'Sort' ? 5 : 1; const handleSelectCountry = (country: Country) => { if (selectedCountries.length < i && !selectedCountries.includes(country)) { @@ -67,9 +67,9 @@ const CountrySelection: FC = ({ setSelectedCountry(country); setCountryListVisible(false); - pageName === "FillOutProfile2" + pageName === 'FillOutProfile2' ? setSearchValue(country.name) - : setSearchValue(""); + : setSearchValue(''); // onSortCountry(country); } @@ -84,16 +84,16 @@ const CountrySelection: FC = ({ setCountryListVisible(false); } - const searchValueLower = newSearchValue.toLocaleLowerCase("ru"); + const searchValueLower = newSearchValue.toLocaleLowerCase('ru'); const filtered = countriesData.filter( (country) => - country.name.toLocaleLowerCase("ru").includes(searchValueLower) && - !selectedCountries.includes(country) + country.name.toLocaleLowerCase('ru').includes(searchValueLower) && + !selectedCountries.includes(country), ); setFilteredCountries(filtered); const suggested = countriesData.filter((country) => - country.name.toLocaleLowerCase("ru").startsWith(searchValueLower) + country.name.toLocaleLowerCase('ru').startsWith(searchValueLower), ); setSuggestedCountries(suggested); @@ -106,8 +106,8 @@ const CountrySelection: FC = ({ filtered.length === 0 && suggested.length === 0; const errorMessage = isInvalidSearch - ? "Страны не существует, возможно ошибка" - : ""; + ? 'Страны не существует, возможно ошибка' + : ''; setIsError(isInvalidSearch); setErrorMessage(errorMessage); @@ -117,7 +117,7 @@ const CountrySelection: FC = ({ }; const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "ArrowDown") { + if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedSuggestionIndex((prevIndex) => { if (prevIndex === null) { @@ -128,7 +128,7 @@ const CountrySelection: FC = ({ return prevIndex; } }); - } else if (e.key === "ArrowUp") { + } else if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedSuggestionIndex((prevIndex) => { if (prevIndex === null) { @@ -139,7 +139,7 @@ const CountrySelection: FC = ({ return prevIndex; } }); - } else if (e.key === "Enter") { + } else if (e.key === 'Enter') { e.preventDefault(); if (selectedSuggestionIndex !== null) { handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); @@ -151,13 +151,13 @@ const CountrySelection: FC = ({ const handleRemoveCountry = (country: Country) => { const updatedCountries = selectedCountries.filter( - (c) => c.code !== country.code + (c) => c.code !== country.code, ); setSelectedCountries(updatedCountries); }; const handleDropdownKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "ArrowDown") { + if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedSuggestionIndex((prevIndex) => { if (prevIndex === null) { @@ -168,7 +168,7 @@ const CountrySelection: FC = ({ return prevIndex; } }); - } else if (e.key === "ArrowUp") { + } else if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedSuggestionIndex((prevIndex) => { if (prevIndex === null) { @@ -179,7 +179,7 @@ const CountrySelection: FC = ({ return prevIndex; } }); - } else if (e.key === "Enter") { + } else if (e.key === 'Enter') { e.preventDefault(); if (selectedSuggestionIndex !== null) { handleSelectCountry(suggestedCountries[selectedSuggestionIndex]); @@ -190,25 +190,25 @@ const CountrySelection: FC = ({ }; const popularCountryCodes = [ - "cn", - "es", - "england", - "sa", - "bd", - "pt", - "ru", - "jp", - "pc", - "my", + 'cn', + 'es', + 'england', + 'sa', + 'bd', + 'pt', + 'ru', + 'jp', + 'pc', + 'my', ]; const sortCountriesByLastLetter = () => { if (lastPressedLetter) { const popularCountries = filteredCountries.filter((country) => - popularCountryCodes.includes(country.name) + popularCountryCodes.includes(country.name), ); const otherCountries = filteredCountries.filter( - (country) => !popularCountryCodes.includes(country.name) + (country) => !popularCountryCodes.includes(country.name), ); popularCountries.sort((a, b) => a.name.localeCompare(b.name)); @@ -238,7 +238,7 @@ const CountrySelection: FC = ({ const handleSelectCountryFromList = (countryName: string) => { const selectedCountry = countriesData.find( - (country) => country.name.toLocaleLowerCase("ru") === countryName + (country) => country.name.toLocaleLowerCase('ru') === countryName, ); if (selectedCountry) { handleSelectCountry(selectedCountry); @@ -249,14 +249,14 @@ const CountrySelection: FC = ({
handleSearchInputChange(event)} onKeyDown={handleKeyDown} /> @@ -287,7 +287,7 @@ const CountrySelection: FC = ({ key={country.code} onClick={() => handleSelectCountryFromList( - country.name.toLocaleLowerCase("ru") + country.name.toLocaleLowerCase('ru'), ) } className={classNames(styles.country__countryList_option, { @@ -302,7 +302,7 @@ const CountrySelection: FC = ({ /> {country.name}
- ) : null + ) : null, )}
)} diff --git a/src/components/ErrorModal/ErrorModal.tsx b/src/components/ErrorModal/ErrorModal.tsx index 0547936..1a898e3 100644 --- a/src/components/ErrorModal/ErrorModal.tsx +++ b/src/components/ErrorModal/ErrorModal.tsx @@ -1,10 +1,10 @@ -import { observer } from "mobx-react-lite"; +import { observer } from 'mobx-react-lite'; -import Modal from "../Modal/Modal"; +import Modal from '../Modal/Modal'; -import errorImage from "../../images/error-modal-image.png"; +import errorImage from '../../images/error-modal-image.png'; -import styles from "./ErrorModal.module.scss"; +import styles from './ErrorModal.module.scss'; interface ErrorModalProps { isOpen: boolean; @@ -18,7 +18,7 @@ const ErrorModal = ({ isOpen, onClose, errorMessage }: ErrorModalProps) => { Думающий человек

{errorMessage}

diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 981ebc5..166eec1 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,9 +1,9 @@ -import React from "react"; -import styles from "./Footer.module.scss"; -import logo from "../../images/svg/footerlogo.svg"; -import tlg from "../../images/svg/telegram.svg"; -import vk from "../../images/svg/vk.svg"; -import { Link } from "react-router-dom"; +import React from 'react'; +import styles from './Footer.module.scss'; +import logo from '../../images/svg/footerlogo.svg'; +import tlg from '../../images/svg/telegram.svg'; +import vk from '../../images/svg/vk.svg'; +import { Link } from 'react-router-dom'; function Footer() { const currentDate = new Date(); @@ -13,14 +13,14 @@ function Footer() {