From 48a6768035b33b60303a76fc75a9884e000c5205 Mon Sep 17 00:00:00 2001 From: nocTKpunTyM Date: Wed, 20 Dec 2023 00:14:12 +0300 Subject: [PATCH 001/141] =?UTF-8?q?=D0=94=D0=BE=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BB=20=D1=81=D0=BE=D0=B3=D0=BB=D0=B0=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5,=20=D1=81=D0=B2=D0=B5=D1=80=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BB=20=D1=83=D1=81=D0=BB=D0=BE=D0=B2=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=81=D1=82=D0=B0=D0=B2=D0=BA=D0=B8,=20=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=B8=D0=B2=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20?= =?UTF-8?q?=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D1=83=20=D0=BD=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B8=D1=85=20=D0=B2=20=D1=84=D1=83=D1=82=D0=B5=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 + src/data/constants.ts | 1 + src/layouts/footer/index.tsx | 4 +- src/pages/agreement/agreement.module.scss | 10 +- src/pages/agreement/index.tsx | 108 +++++++++- .../delivery-conditions.module.scss | 1 + src/pages/delivery-conditions/index.tsx | 192 ++++++++++++++++++ 7 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 src/pages/delivery-conditions/delivery-conditions.module.scss create mode 100644 src/pages/delivery-conditions/index.tsx diff --git a/src/App.tsx b/src/App.tsx index 63ed8053..65f20804 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,6 +22,7 @@ import NotFound from '@pages/not_found/not-found.tsx'; import { CartProvider } from '@contexts/cart-context.tsx'; import RecipeList from '@pages/recipe-list/index.tsx'; import Agreement from '@pages/agreement/index.tsx'; +import DeliveryConditions from '@pages/delivery-conditions/index.tsx'; // импорт временных массивов для отображения каталогов и продуктов // временное решение для верстки, потом удалить @@ -59,6 +60,7 @@ function App() { } /> } /> + } /> } /> diff --git a/src/data/constants.ts b/src/data/constants.ts index 18f2f764..e00b08e5 100644 --- a/src/data/constants.ts +++ b/src/data/constants.ts @@ -6,4 +6,5 @@ export const URLS = { PROFILE: '/profile', CATALOG: '/catalog', AGREEMENT: '/agreement', + DELIVERY_COND: '/delivery-conditions', }; diff --git a/src/layouts/footer/index.tsx b/src/layouts/footer/index.tsx index feff2e29..cbee4cc7 100644 --- a/src/layouts/footer/index.tsx +++ b/src/layouts/footer/index.tsx @@ -19,7 +19,9 @@ const Footer: React.FC = () => {
  • О нас
  • -
  • Условия доставки
  • + +
  • Условия доставки
  • +
  • Оплата
  • Контакты
  • diff --git a/src/pages/agreement/agreement.module.scss b/src/pages/agreement/agreement.module.scss index 4008805f..57650e47 100644 --- a/src/pages/agreement/agreement.module.scss +++ b/src/pages/agreement/agreement.module.scss @@ -17,18 +17,24 @@ } .agreement_title { - margin: 80px 0 40px; + margin: 80px 0 0; @media screen and (width <= 768px) { - margin: 20px 0; + margin: 20px 0 0; font-size: 24px; } } .agreement_subtitle { + margin-top: 40px; text-align: start; @media screen and (width <= 768px) { + margin-top: 30px; font-size: 15px; } } + +.agreement_p { + width: 100%; +} \ No newline at end of file diff --git a/src/pages/agreement/index.tsx b/src/pages/agreement/index.tsx index f13549ef..4d960b13 100644 --- a/src/pages/agreement/index.tsx +++ b/src/pages/agreement/index.tsx @@ -83,10 +83,110 @@ const Agreement: React.FC = () => { «Сайт») и/или мобильного приложения Оператора (далее - «Приложение»). Далее по тексту «Сайт» и «Приложение» при совместном упоминание именуются «Сервис».

    -

    ...

    -

    -

    -

    +

    1.5 Настоящая Политика определяет порядок и условия осуществления обработки персональных данных Субъектов, передавших свои персональные данные для обработки Оператору с использованием и без использования средств автоматизации, устанавливает процедуры, направленные на предотвращение нарушений законодательства Российской Федерации, связанных с обработкой персональных данных.

    +

    1.6 Политика разработана с целью обеспечения защиты прав Субъектов при обработке их персональных данных, а также продвижения товаров, работ, услуг Оператора/исполнителя/доставщика путем осуществления прямых контактов с потенциальным потребителем с помощью средств связи.

    +

    1.7 Оператор осуществляет обработку общедоступную и иную категорию персональных данных Субъекта (не относящуюся к биометрической или специальной):

    +

    - фамилия, имя, отчество;

    +

    - номер телефона;

    +

    - адрес электронной почты;

    +

    - почтовый адрес;

    +

    - данные об оказанных и оказываемых Субъекту услугах, история заказов Субъекта, обращений Субъекта + к Оператору посредством использования Сервиса;

    +

    1.8. При использовании Сервиса Субъектом персональных данных, Оператор получает и обрабатывает данные, получаемые через программные средства Яндекс. Метрика, Google tag manager, Google analytics, Google Firebase и иные обезличенные данные, которые автоматически передаются в процессе использования Сервиса посредством установленного на техническом устройстве Субъекта программного обеспечения:

    +

    - IP-адрес;

    +

    - идентификаторы мобильных устройств;

    +

    - данные файлов cookie;

    +

    - количество просмотров;

    +

    - посещение страниц;

    +

    - поиск по сайту;

    +

    - поиск в приложении;

    +

    - точки входа (сторонние сайты, с которых пользователь по ссылке переходит на сайты Компании);

    +

    - точки выхода (ссылки на сайтах Компании, по которым пользователь переходит на сторонние сайты);

    +

    - страна пользователя;

    +

    - регион пользователя;

    +

    - провайдер пользователя;

    +

    - браузер пользователя;

    +

    - системные языки пользователя;

    +

    - ОС пользователя;

    +

    Мы используем такую информацию для понимания и анализа тенденций, администрирования сайта, изучения поведения пользователей на сайте и сбора демографической информации о нашем основном контингенте пользователей в целом. Компания может использовать такую информацию в своих маркетинговых целях.

    +

    1.9 Оператором на принципах:

    +

    - законности целей и способов обработки персональных данных, добросовестности и справедливости + в деятельности Оператора;

    +

    - ограничения обработки персональных данных достижением конкретных, заранее определенных +и законных целей;

    +

    - достоверности персональных данных, их достаточности для целей обработки, недопустимости обработки персональных данных, избыточных по отношению к целям, заявленным при сборе персональных данных;

    +

    - обработки только персональных данных, которые отвечают целям их обработки.Недопустима обработка персональных данных, несовместимая с целями сбора персональных данных;

    +

    - соответствия содержания и объема обрабатываемых персональных данных заявленным целям обработки. Обрабатываемые персональные данные не должны быть избыточными по отношению +к заявленным целям их обработки;

    +

    - недопустимости объединения баз данных, содержащих персональные данные, обработка которых осуществляется в целях, не совместимых между собой;

    +

    - обеспечения точности персональных данных, их достаточности, а в необходимых случаях и актуальности по отношению к целям обработки персональных данных. Оператор принимает необходимые меры либо обеспечивает их принятие по удалению или уточнению неполных или неточных данных;

    +

    - хранения персональных данных в форме, позволяющей определить субъекта персональных данных, +не дольше, чем этого требуют цели обработки персональных данных, если срок хранения персональных данных не установлен федеральным законом, договором, стороной которого, выгодоприобретателем +или поручителем по которому является субъект персональных данных. Обрабатываемые персональные данные подлежат уничтожению либо обезличиванию по достижении целей обработки или в случае утраты необходимости в достижении этих целей, если иное не предусмотрено федеральным законом.

    +

    2. Цели сбора персональных данных

    +

    2.1. Оператор осуществляет обработку персональных данных Субъектов путем ведения баз данных автоматизированным, механическим, ручным способами в целях:

    +

    2.1.1. обработки заказов, запросов или других действий Субъекта, связанных с регистрацией, авторизацией на Сервисе, осуществлением заказов через Сервис;

    +

    2.1.2. оповещения об изменении пользовательского соглашения об условиях доставки, порядка оказания услуг, меню, перечня проводимых Оператором акций, скидок и иных маркетинговых мероприятий.

    +

    2.1.3. в иных целях в случае, если соответствующие действия Оператора не противоречат действующему законодательству, деятельности Оператора, и на проведение указанной обработки получено согласие Субъекта персональных данных.

    +

    2.2. Данные, указанные в п. 1.7. настоящей Политики, обрабатываются в целях осуществления аналитики Сервиса, принципов использования Сервиса, совершенствования функционирования Сервиса, решения технических проблем Сервиса, разработки новых продуктов, расширения услуг, выявления популярности мероприятий и определения эффективности рекламных кампаний; обеспечения безопасности +и предотвращения мошенничества, предоставления эффективной клиентской поддержки.

    +

    2.3. Обработка персональных данных Субъекта осуществляется Оператором с момента получения согласия Субъекта персональных данных. Согласие на обработку персональных данных может быть дано Субъектом персональных данных в любой форме, позволяющей подтвердить факт получения согласия, если иное не установлено федеральным законом: в письменной, устной или иной форме, предусмотренной действующим законодательством, в том числе посредством совершения Субъектом персональных данных конклюдентных действий (акцепта размещенной на Сервисе оферты – пользовательского соглашения об условиях доставки). В случае отсутствия согласия Субъекта персональных данных на обработку его персональных данных, такая обработка не осуществляется.

    +

    +

    2.4. Персональные данные Субъектов получаются Оператором:

    +

    2.4.1. посредством личной передачи Субъектом персональных данных при внесении сведений в формы +в электронном виде на Сервисе Оператора;

    +

    2.4.2. посредством личной передачи Субъектом персональных данных посредством сообщения в устной форме по телефону в процессе оформления заказа;

    +

    2.4.3. иными способами, не противоречащими законодательству Российской Федерации и требованиям международного законодательства о защите персональных данных.

    +

    2.5. Согласие на обработку персональных данных считается предоставленным посредством совершения Субъектом персональных данных любого действия или совокупности следующих действий:

    +

    2.5.1. оформления заказа на Сервисе Оператора;

    +

    2.5.2. проставления на Сервисе в соответствующей форме отметки о согласии на обработку +персональных данных;

    +

    2.5.3. отправки посредством смс кода (сообщения) персональных данных при авторизации/регистрации, оформления Заказа на Сервисе Оператора.

    +

    3. Правовые основания обработки персональных данных

    +

    3.1. Оператор обрабатывает персональные данные Субъекта на основании:

    +

    3.1.1. Пользовательского соглашения об условиях доставки, размещенного на Сервисе Оператора;

    +

    3.1.2. Устава Оператора.

    +

    3.2. Оператор обрабатывает персональные данные Субъекта только в случае их отправки Субъектом через соответствующие электронные формы, расположенные на Сервисе Оператора или переданные лично субъектом посредством телефонной связи при оформлении Заказа. Отправляя свои персональные данные Оператору, Субъект выражает свое согласие с настоящей Политикой.

    +

    3.3. Оператор обрабатывает обезличенные данные о Пользователе в случае, если Пользователь разрешил это в настройках браузера (включено сохранение файлов «cookie» и использование технологии JavaScript) в соответствии с положениями настоящей Политики.

    +

    4. Порядок и условия обработки персональных данных

    +

    4.1. Оператор осуществляет обработку персональных данных посредством совершения любого действия (операции) или совокупности действий (операций), включая следующие: сбор; запись; систематизация; накопление; хранение; уточнение (обновление, изменение); извлечение; использование; передача; обезличивание; блокирование; удаление; уничтожение.

    +

    4.2. В соответствии с настоящей Политикой Оператор может осуществлять обработку персональных данных самостоятельно, а также с привлечением третьих лиц, которые привлекаются по поручению Оператора и осуществляют обработку для выполнения указанных в настоящей Политики целей.

    +

    4.3. В случае поручения обработки персональных данных третьему лицу, объем передаваемых третьему лицу для обработки персональных данных и количество используемых этим лицом способов обработки должны быть минимально необходимым и для выполнения им своих обязанностей перед Оператором. +В отношении обработки персональных данных третьим лицом устанавливается обязанность такого лица соблюдать конфиденциальность персональных данных и обеспечивать безопасность персональных данных при их обработке.

    +

    4.4. В процессе предоставления услуг, при осуществлении внутрихозяйственной деятельности Оператор использует автоматизированную, с применением средств вычислительной техники, так +и неавтоматизированную, с применением бумажного документооборота, обработку +персональных данных.

    +

    4.5. В отношении персональных данных Субъекта сохраняется конфиденциальность, кроме случаев добровольного предоставления Субъектом своих персональных данных неограниченному кругу лиц. +В данном случае Субъект соглашается с тем, что часть его персональных данных +становится общедоступной.

    +

    5. Сведения о реализуемых требованиях к защите персональных данных

    +

    5.1. Оператор защищает персональные данные Субъекта в соответствии с требованиями, предъявляемыми к защите такого рода информации, и несет ответственность за использование безопасных методов защиты такой информации.

    +

    5.2. Для защиты персональных данных Субъекта, обеспечения их надлежащего использования +и предотвращения несанкционированного и/или случайного доступа к ним третьими лицами, Оператор применяет необходимые и достаточные технические и административные меры. Предоставляемые Субъектом персональные данные хранятся на серверах с ограниченным доступом, расположенных +в охраняемых помещениях.

    +

    5.3. В случае подтверждения факта неточности персональных данных или неправомерности их обработки, персональные данные подлежат их актуализации Оператором, а обработка должна быть прекращена, соответственно.

    +

    5.4. Субъект может в любой момент отозвать свое согласие на обработку персональных данных при условии, что подобная процедура не нарушает требований законодательства Российской Федерации.

    +

    5.5. Согласие Субъекта персональных данных считается полученным в установленном действующем законодательством и настоящей Политикой порядке и действует до момента направления Субъектом персональных данных соответствующего заявления (уведомления) о прекращении обработки персональных данных по юридическому адресу Оператора: юридический адрес

    +

    5.5.1. В случае отзыва Субъектом персональных данных согласия на обработку его персональных данных, Оператор должен прекратить их обработку или обеспечить прекращение такой обработки (если обработка осуществляется другим лицом, действующим по поручению Оператора) и в случае, если сохранение персональных данных более не требуется для целей их обработки, уничтожить персональные данные или обеспечить их уничтожение (если обработка персональных данных осуществляется третьим лицом, действующим по поручению Оператора) в срок, не превышающий 30 (Тридцати) дней с даты поступления указанного отзыва, если иное не предусмотрено договором, стороной которого, выгодоприобретателем или поручителем по которому является Субъект, иным соглашением между Оператором и Субъектом персональных данных, либо если Оператор не вправе осуществлять обработку персональных данных без согласия Субъекта персональных данных на основаниях, предусмотренных Федеральным законом № 152-ФЗ «О персональных данных» от 27.07.2006 г. или другими +федеральными законами.

    +

    6. Согласие на получение рекламной информации

    +

    6.1. Согласие Субъекта персональных данных на получение рекламной информации, +подтверждается посредством:

    +

    6.1.1. оформления заказа на Сервисе Оператора;

    +

    6.1.2. проставления на Сервисе в соответствующей форме отметки о согласии на обработку +персональных данных;

    +

    6.1.3. сообщения персональных данных в устной форме, при обращении по телефону Оператора +в процессе оформлении заказа, означает согласие Субъекта персональных данных на получение +от Оператора и привлеченных Оператором третьих лиц, по сетям электросвязи (по предоставленным номеру мобильного телефона и адресу электронной почты) информационных сообщений, а в том числе информации коммерческого рекламного характера (рекламы).

    +

    6.2. Давая согласие, указанное в п. 6.1. настоящей Политики, Субъект персональных данных подтверждает, что действует по своей воле и в своем интересе, а также то, что указанные персональные данные являются достоверными.

    +

    7. Возрастные ограничения

    +

    7.1.В соответствии с ГК РФ несовершеннолетние в возрасте от четырнадцати до восемнадцати лет вправе самостоятельно, без согласия родителей, усыновителей и попечителей, распоряжаться своими заработком, стипендией и иными доходами, а также совершать мелкие бытовые сделки и иные сделки, предусмотренные Гражданским кодексом Российской Федерации.

    +

    7.2.Только родители могут разрешать или запрещать ребенку предоставлять доступ к своему аккаунту приложениям и сайтам, включив функцию родительского контроля на устройстве.

    +

    7.3. В случае, если мы получим достоверные сведения о возрасте Пользователя - до 14 лет мы ограничим несовершеннолетнего от совершения им сделок, не предусмотренных законодательством РФ, путем блокировки возможности оформления заказов.

    +

    8. Заключительные положения

    +

    8.1. Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью телефонного номера номер поддержки, либо на почтовый адрес Оператора: юридический адресюридический адрес

    +

    8.2. В настоящем документе будут отражены любые изменения Политики обработки персональных данных Оператором.

    +

    8.3. Настоящая Политика размещена по адресу: https://goodfood.acceleratorpracticum.ru/

diff --git a/src/pages/delivery-conditions/delivery-conditions.module.scss b/src/pages/delivery-conditions/delivery-conditions.module.scss new file mode 100644 index 00000000..40adcbbc --- /dev/null +++ b/src/pages/delivery-conditions/delivery-conditions.module.scss @@ -0,0 +1 @@ +@use '@pages/agreement/agreement.module.scss' as *; \ No newline at end of file diff --git a/src/pages/delivery-conditions/index.tsx b/src/pages/delivery-conditions/index.tsx new file mode 100644 index 00000000..22e10036 --- /dev/null +++ b/src/pages/delivery-conditions/index.tsx @@ -0,0 +1,192 @@ +import styles from './delivery-conditions.module.scss'; + +const DeliveryConditions: React.FC = () => { + return ( + <> +
+
+

+ Пользовательское соглашение об условиях доставки +

+

Термины и определения

+

+ В настоящем соглашении, если из контекста не следует иное, нижеприведенные термины имеют следующие значения и являются её составной неотъемлемой частью: «Соглашение» - настоящий документ, размещенный в сети Интернет по адресу: https://goodfood.acceleratorpracticum.ru/, являющееся открытым и общедоступным документом. +

+

+ Действующая редакция Соглашения располагается в сети Интернет по адресу: https://goodfood.acceleratorpracticum.ru/delivery-conditions +

+

+ «Пользователь» - любое физическое лицо, надлежащим образом присоединившееся к настоящему Соглашению для использования сайта/приложения Компании и/или оформления Заказа. +

+

+ «Компания» - Общество с ограниченной ответственностью «ГудФуд» (ОГРН номер, ИНН номер), оказывающая услуги Пользователю по осуществлению Заказа Товара и Доставки на условиях, предусмотренных в Пользовательском соглашении посредством Сайта: https://goodfood.acceleratorpracticum.ru/ (далее – Сайт) и/или мобильного приложения. +

+

+ «Исполнитель» - юридическое лицо или индивидуальный предприниматель, осуществляющее(ий) приготовление/реализацию Товара для Пользователя. +

+

+ «Доставщик» - лица, осуществляющие доставку Товара, Заказ которого оформлен Пользователем (за исключением случая осуществления доставки Товара Компанией Исполнителем). +

+

+ «Товар» - пищевая продукция и напитки (за исключением алкогольных), приготовление которых для Пользователей осуществляет Исполнитель в результате оформления Пользователем Заказа на Сайте и/или Приложении Компании. При упоминании в настоящем соглашении или иных документах Товара, имеется в виду как один Товар, так и несколько Товаров, если иное не следует из соглашения или соответствующего документа. +

+

+ «Приложение» - мобильное приложение Компании, разработанное для удобства Пользователя с целью выбора Товара и оформления Заказа, размещенного на Сайте. +

+

+ «Сайт» - официальная веб-страница Компании, расположенная по адресу: https://goodfood.acceleratorpracticum.ru/ посредством которой Пользователь получает информацию о Товаре и оформляет Заказ. +

+

+ «Сервис» - Сайт и мобильное приложение Компании. +

+

+ «Регистрация» - процедура внесения Пользователем своих Персональных данных в определённую электронную форму на Сервисе с целью получения доступа к услугам в соответствии с положениями настоящего Соглашения. Регистрация может быть совершена Пользователем как отдельно, так и совместно с созданием Заказа на Сервисе. +

+

+ «Заказ» - оформленный Пользователем на Сервисе Заказ Товара и его Доставки, в результате которого Пользователь заключает договор о приготовлении Товара с Исполнителем, и договор доставки данного Товара на условиях, размещенных на Сервисе. +

+

+ «Договор» - договор о приготовлении/ реализации Товара и/или его доставки, заключаемый между Пользователем и Исполнителем в результате оформления Заказа. +

+

+ «Доставка» - услуга доставки Товара до адреса, указанного Пользователем, Заказ которого Пользователь оформил на Сайте или в Приложении,. Заключение с Пользователем договора о доставке осуществляет Доставщик, либо, непосредственно Исполнитель посредством оформления Пользователем Заказа на сервисе. Исполнитель или Доставщик имеют право привлекать третьих лиц для осуществления доставки Товара до Пользователя. +

+

+ «Промокод» - определенная последовательность символов, при условии активации которой и соблюдении иных условий использования Промокода Пользователю предоставляется скидка на стоимость Товара и/или Доставки. +

+

«Персональные данные» – личная информация (включая фамилию, имя, отчество, дату рождения, почтовый адрес, контактный телефон и адрес электронной почты, а также другую информацию, которую Пользователь сообщает, используя Сервис), данную информацию Пользователь предоставляет осознанно и добровольно в момент Регистрации на Сервисе и/или оформления Заказа на Сервисе в соответствии с положениями настоящего Соглашения и Политики хранения и обработки персональных данных.

+

«Пользовательское соглашение» — настоящий документ, размещенный в сети Интернет по адресу: ссылка на страницу с данным соглашением

+

«Политика обработки и хранения персональных данных» - документ, определяющий порядок и условия осуществления обработки персональных данных Пользователей, размещенный в сети Интернет по адресу: https://goodfood.acceleratorpracticum.ru/

+

1. Общие положения. Предмет соглашения

+

1.1. Перед тем как начать использовать Сервис Пользователь обязан ознакомиться с настоящим Соглашением и присоединиться к нему. Пользователь подтверждает, что прочитал, понял и полностью согласен соблюдать настоящее Соглашение.

+

Обращаем Ваше внимание, что родители несут полную ответственность за жизнь и здоровье своих детей (согласно ст.63, 65 Семейного кодекса РФ, ст.5.35. КоАП РФ.). Употребление содержащихся в составе блюд морепродуктов, морской капусты, а также имбиря и вассаби должно соответствовать определенному этапу формирования детского организма, с учетом возможных аллергических реакций. Рекомендуем до 7-8 летнего возраста приобретать для детей блюда из раздела "Детское меню".

+

1.2. Сервис предлагает Пользователю доступ к поиску и Заказу готовой еды на условиях, предусмотренных настоящим Соглашением. В соответствии с настоящим соглашением Пользователь при оформлении Заказа заключает Договор с Компанией, в связи с чем возникают прямые договорные отношения с Исполнителем в части приготовления и/или реализации Товара и осуществления Доставки.

+

1.3. В соответствии со статьей 437 Гражданского Кодекса Российской Федерации (далее по тексту соглашения - ГК РФ) настоящее соглашение является публичной офертой, адресованной физическим лицам, и в случае принятия изложенных условий в настоящем соглашении, физическое лицо обязуется произвести оплату Товара на условиях, изложенных в настоящем соглашении.

+

В соответствии с пунктом 3 статьи 438 ГК РФ, момент окончательного подтверждения Заказа Пользователем является акцептом оферты, что является равносильным заключению Договора розничной купли-продажи Товара на условиях, установленных в настоящем соглашении и на Сервисе.

+

1.3.1. Компания или Исполнитель вправе отказаться от заключения публичного договора при отсутствии технической (поломки оборудования, в том числе при принятии/подтверждении заказа), физической (удаленность Потребителя от территории оказания услуг) и иной возможности предоставить потребителю товары, услуги надлежащего качества.

+

1.4. Принимая условия настоящего Соглашения, Пользователь подтверждает свое информированное и добровольное согласие на обработку его персональных данных, предоставленных при регистрации, в том числе, но не ограничиваясь: для ответа на обращения Пользователя в службу технической поддержки Сервиса, для разрешения возможных претензий, для участия в стимулирующих, рекламных, маркетинговых и иных мероприятиях, направленных на продвижение услуг Компании, партнеров Исполнителя и иных третьих лиц. Компания вправе направлять Пользователю информацию о функционировании Сервиса на адрес электронной почты, на номер телефона, указанные Пользователем, а также направлять собственные или принадлежащие Компании информационные, рекламные или иные сообщения, или размещать соответствующую информацию в самом сервисе. Также Пользователь подтверждает свое согласие на передачу указанных выше персональных данных Компании и их обработку Компанией в целях исполнения настоящего Соглашения и реализации функционирования Сервиса, а также разрешения претензий, связанных с исполнением настоящего Соглашения.

+

Принимая условия настоящего Соглашения, Пользователь в том числе принимает условия Политики хранения и обработки персональных данных.

+

1.5. Настоящее соглашение может быть изменено Компанией в одностороннем порядке. Уведомление пользователей, при внесении изменений в Соглашение, происходит путем размещения новой редакции Соглашения по постоянному адресу https://goodfood.acceleratorpracticum.ru/delivery-conditions не позднее, чем за 10 дней до вступления в силу соответствующих изменений. Предыдущие редакции Соглашения хранятся в архиве документации Компании Заказчика. При этом продолжение использования сервиса после внесения изменений и/или дополнений в настоящее Соглашение, означает согласие Пользователя с такими изменениями и/или дополнениями, в связи с чем Пользователь обязуется регулярно отслеживать изменения в соответствующем разделе и в Соглашении, размещенном на сайте https://goodfood.acceleratorpracticum.ru/delivery-conditions Пользователь вправе отказаться от принятия изменений Соглашения, что означает отказ Пользователя от использования сервиса.

+

2. Права и обязанности Пользователя

+

2.1. Пользователь обязуется надлежащим образом соблюдать условия настоящего Соглашения.

+

2.2. При заказе посредством Сервиса Пользователь обязуется сообщать достоверную информацию о себе для надлежащего исполнения обязательств, предусмотренных настоящим соглашением.

+

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

+

2.4. Пользователь обязуется не использовать сервис для любых иных целей, кроме как для целей, связанных с личным некоммерческим использованием.

+

2.5. Пользователь обязуется, пользуясь сервисом, не вводить в заблуждение других Пользователей и третьих лиц.

+

2.6. Пользователь обязуется не использовать стороннее программное обеспечение и другие технические средства, влияющие на работу сервиса и связанную с ней систему.

+

3. Права и обязанности Компании

+

3.1. Компания вправе заблокировать доступ Пользователя к Сервису в случае обнаружения нарушений Пользователем обязанностей, указанных в разделе 2 настоящего Соглашения.

+

3.2. Компания оставляет за собой право в любой момент расторгнуть настоящее Соглашение по организационным или техническим причинам в одностороннем порядке, заблокировав возможность его использования.

+

3.3. Компания осуществляет обработку персональных данных пользователя в целях исполнения и на условиях настоящего Соглашения.

+

4. Оплата Товара и/или Доставки

+

4.1. Оплата Товара и/или Доставки в рамках оформленного Пользователем Заказа, может быть произведена Пользователем:

+

4.1.1. Непосредственно Исполнителю наличными денежными средствами или посредством банковской карты через терминал оплаты в момент доставки Товара. Указанный вид оплаты осуществляется без участия Компании и не регулируется положениям настоящего Соглашения.

+

4.1.2. Пользователю может быть доступна функция оплаты Доставщику (либо третьему лицу, привлекаемому Доставщиком, в случае, если Доставщик привлекает такое третье лицо для осуществления Доставки Товара до Пользователя) наличными денежными средствами в случае осуществления Доставки Товара Доставщиком. В этом случае Доставщик (либо привлекаемое им третье лицо) в части приема денежных средств за Товар действует по поручению Исполнителя.

+

4.1.3. Пользователю может быть доступна функция безналичной оплаты в интерфейсе Сервиса с Привязанной банковской карты (п. 4.3. настоящего соглашения). В этом случае оплата происходит через авторизационный сервер Процессингового центра Банка с использованием Банковских кредитных карт следующих платежных систем: МИР VISA InternationalMasterCard World Wide К оплате принимаются все виды платежных карточек VISA, за исключением Visa Electron. В большинстве случаев карта Visa Electron не применима для оплаты через интернет, за исключением карт, выпущенных отдельными банками. О возможность оплаты картой Visa Electron вам нужно выяснять у банка-эмитента вашей карты.

+

Доставщика с привлечением уполномоченного оператора по приему платежей или оператора электронных денежных средств и является получателем платежа в качестве агента Исполнителя и/или Доставщика (далее – «безналичная оплата»). Компания не гарантирует отсутствие ошибок и сбоев в работе сервиса в отношении предоставления возможности безналичной оплаты. Компания вправе

+

4.1.3.2. Для оплаты Заказа Покупатель перенаправляется на платежный шлюз Банка для ввода реквизитов карты. Соединение с платежным шлюзом и передача информации осуществляется в защищенном режиме с использованием протокола шифрования SSL.

+

В случае если банк Покупателя поддерживает технологию безопасного проведения интернет-платежей Verified By Visa или MasterCard Secure Code для проведения платежа также может потребоваться ввод специального пароля. Проведение платежей по банковским картам осуществляется в строгом соответствии с требованиями платежных систем Visa Int. и MasterCard Europe Sprl. Введенная информация не будет предоставлена третьим лицам за исключением случаев, предусмотренных законодательством РФ.

+

Способы и возможность получения паролей для совершения интернет-платежей Пользователь может уточнить в банке, выпустившем карту.

+

4.1.4. Компания не гарантирует отсутствие ошибок и сбоев в работе Сервиса в отношении предоставления возможности оплаты наличными или безналичными денежными средствами. Выбор соответствующей формы оплаты производится Пользователем в интерфейсе Сервиса. При этом, доступный Пользователю в конкретный момент времени способ оплаты Товара и/или Доставки определяется с учетом технических, временных, материальных и/или иных факторов.

+

4.2. Прием денежных средств Компанией в случае, предусмотренном п. 4.1.3. настоящего соглашения, осуществляется исключительно в связи с тем, что Компания посредством предоставления возможности оформления Заказа на сервисе участвует в реализации Исполнителем Пользователю Товаров, в оплату которых принимаются денежные средства. При этом прием денежных средств, в случае, предусмотренном п. 4.1.2. настоящего соглашения, осуществляется Доставщиком, привлекаемым Исполнителем (либо третьим лицом, привлекаемым Доставщиком), в связи с доставкой Товара Пользователю.Компания и Доставщик не являются платежными агентами при проведении расчетов в соответствии с настоящим соглашением согласно пп. 1, 4 ч. 2 ст. 1 Федерального закона от 03.06.2009 № 103-ФЗ «О деятельности по приему платежей физических лиц, осуществляемой платежными агентами».

+

4.3. Привязанная банковская карта может указываться Пользователем в интерфейсе сервиса, при этом Пользователь указывает следующие данные:

+

- Hаименование владельца банковской карты;

+

- номер банковской карты;

+

- срок окончания действия банковской карты, месяц/год;

+

- CVV код для карт Visa / CVC код для Master Card.

+

Если на банковской карте код CVC / CVV отсутствует, то, возможно, карта не пригодна для CNP транзакций (т.е. таких транзакций, при которых сама карта не присутствует, а используются её реквизиты), и в данном случае следует обратиться в банк для получения подробной информации.

+

Если данные банковской карты верны, действительны и использование данной карты в рамках сервиса технически возможно, указанная банковская карта приобретает статус Привязанной и может быть использована для безналичной оплаты. Все Привязанные карты отображаются в интерфейсе сервиса.

+

4.4. Безналичная оплата осуществляется Пользователем с участием уполномоченного оператора по приему платежей или оператора электронных денежных средств и регулируется правилами международных платежных систем, банков (в том числе банка-эмитента Привязанной карты) и других участников расчетов.

+

4.5. При указании своих данных согласно п. 4.3. настоящего соглашения и дальнейшем использовании Привязанной карты Пользователь подтверждает и гарантирует указание им достоверной и полной информации о действительной банковской карте, выданной на его имя, соблюдение им правил международных платежных систем и требований банка-эмитента, выпустившего Привязанную карту, в том числе в отношении порядка проведения безналичных расчетов.

+

4.6. Пользователь понимает и соглашается, что все действия, совершенные в рамках сервиса после авторизации с помощью логина и пароля, присвоенных ему при регистрации на Сервисе, в том числе по безналичной оплате с использованием Привязанной банковской карты, считаются совершенными Пользователем.

+

4.7. В случае несогласия Пользователя с фактом и/или суммой безналичной оплаты, а также получения некачественного, некомплектного Товара, либо несоответствия полученного Товара заказанному, Пользователь вправе обратиться к Компании по реквизитам, указанным в настоящем соглашении, в течение 2 календарных дней с указанием событий, послуживших причиной обращения.

+

При получении Товара Пользователь проверяет его соответствие Заказу, комплектность и отсутствие претензий к внешнему виду доставленного Товара. В случае выявления несоответствий Пользователь вправе потребовать замены на Товары надлежащего качества сразу в момент получения, уведомив об этом Доставщика, либо в течение 5 минут после получения Товара, уведомив об этом Компанию; либо потребовать произвести возврат Пользователю ранее оплаченных денежных средств за данный Товар из Заказа.

+

Возврат денежных средств Пользователю производится на банковскую карту Пользователя в течение 5 (Пяти) банковских дней, начиная со следующего банковского дня с момента выставления законного и обоснованного требования о возврате денежных средств.

+

Компания/Исполнитель вправе отказать Пользователю в обмене Товара или возврате денежных средств по своему усмотрению, если будет иметь доказательства неправомерных действий со стороны Пользователя. Также Компания/Исполнитль вправе привлечь Пользователя к ответственности в судебном порядке.

+

4.8. По вопросам, связанным с оплатой Товара и/или Доставки способами, предусмотренными пп. 4.1.1., 4.1.2. настоящего соглашения, Пользователь вправе обратиться к Исполнителю и/или к Доставщику.

+

4.9. Компания оставляет за собой право в любой момент потребовать от Пользователя подтверждения данных, указанных им в рамках Сервиса, в том числе данных Привязанной карты, и запросить в связи с этим подтверждающие документы (в частности, документы, удостоверяющие личность), непредставление которых, по усмотрению Компании, может быть приравнено к предоставлению недостоверной информации и повлечь последствия, предусмотренные настоящим соглашением.

+

5. Гарантии и ответственность сторон

+

5.1. Пользователь гарантирует, что не будет предпринимать каких-либо действий, направленных на причинение ущерба Исполнителю, Компании, операторам сотовой мобильной связи, правообладателям или иным лицам.

+

5.2. В случае нарушения правил использования сервиса, указанных в разделе 2 настоящего Соглашения, Пользователь обязуется возместить вред, причиненный такими действиями.

+

5.3. Если Пользователем не доказано обратное, любые действия, совершенные с использованием его мобильного устройства, считаются совершенными соответствующим Пользователем.

+

5.4. Компания не является уполномоченной организацией по смыслу Закона РФ от 07.02.1992 г. № 2300-1 «О защите прав потребителей», и не осуществляет рассмотрение и удовлетворение претензий Пользователей в отношении Товара и/или Доставки ненадлежащего качества, Заказ которого (которых) оформлен Пользователем на Сервисе.При обращении Пользователя к Компании по вопросам, касающимся Договора, заключаемого в результате оформления Заказа, в том числе с претензиями относительно исполнения данного Договора, Компания вправе передать соответствующую информацию Исполнителю и/или Доставщику, а также передать Пользователю информацию, полученную от Исполнителя и/или Доставщика по данным вопросам.

+

5.5. Пользователь при оформлении Заказа, в соответствии с условиями настоящего Соглашения, подтверждает личное ознакомление с ингредиентами, входящими в состав Заказа, которые могут быть несовместимыми с организмом Пользователя в силу индивидуальных особенностей, в частности, могут вызвать отторжение или аллергическую реакцию.

+

Пользователь при наступлении указанного в настоящем пункте случая - индивидуальной несовместимости Продукции с организмом Пользователя, подтверждает, что такой случай индивидуальной несовместимости не связан с качеством продукции и соблюдения всех норм, хранения, транспортировки и приготовления Заказа, а является индивидуальной особенностью организма Пользователя о которой Исполнителю не могло быть известно, в связи с чем, Исполнитель не несет ответственности при наступлении такого случая.

+

6. Сайты третьих лиц

+

6.1. Сервис может содержать ссылки или представлять доступ на другие сайты в сети Интернет (сайты третьих лиц) и размещенный на данных сайтах контент, являющийся результатом интеллектуальной деятельности третьих лиц и охраняемый в соответствии с законодательством Российской Федерации. Указанные сайты и размещенный на них контент не проверяются Компанией на соответствие требованиям законодательства Российской Федерации.

+

6.2. Компания не несет ответственность за любую информацию или контент, размещенные на сайтах третьих лиц, к которым Пользователь получает доступ посредством сервиса, включая, в том числе, любые мнения или утверждения, выраженные на сайтах третьих лиц.

+

6.3. Пользователь подтверждает, что с момента перехода Пользователя по ссылке, содержащейся на сервисе на сайт третьего лица, взаимоотношения Компании и Пользователя прекращаются, настоящее Соглашение в дальнейшем не распространяется на Пользователя, и Компания не несет ответственность за использование Пользователем контента, правомерность такого использования и качество контента, размещенного на сайтах третьих лиц.

+

7. Конфиденциальность и обработка персональных данных Пользователя.

+

7.1. Персональные данные Пользователя обрабатываются в соответствии Федеральным законом от 27.07.2006 г. № 152-ФЗ «О персональных данных» и Политикой хранения и обработки персональных данных.

+

7.2. При регистрации/оформлении заказа Пользователь предоставляет следующие данные: имя, адрес электронной почты, номер контактного телефона, адрес доставки заказа.

+

7.3. В целях исполнения настоящего соглашения Компания развивает, оптимизирует и внедряет новый функционал сервиса (включая продукты информационного, рекламного, развлекательного и иного характера), в т.ч. с участием аффилированных лиц и/или партнеров.

+

Для обеспечения реализации указанных целей, а также в целях информирования Пользователей о своих услугах, продвижения товаров и услуг, проведения электронных и sms опросов, Получения Пользователем персонализированной (таргетированной) рекламы, контроля маркетинговых акций, клиентской поддержки, организации доставки товара Пользователям, проведения розыгрышей призов среди Пользователей, контроля удовлетворенности Пользователя и качества услуг, проверки, исследования и анализа таких данных Пользователь при регистрации/оформлении заказа соглашается и поручает Компании осуществлять с соблюдением применимого законодательства обработку данных, в т.ч. результатов автоматизированной обработки таких данных в виде целочисленных и/или текстовых значений и идентификаторов, их передачу аффилированным лицам и/или Исполнителям/Доставщикам во исполнение такого поручения на обработку, а также осуществлять сбор (получение) данных Пользователя и иных связанных с Пользователем данных от аффилированных лиц и/или Исполнителей.

+

7.4. Под данными, связанными с Пользователем, понимается информация о технических средствах (устройствах) и способах технологического взаимодействия с сервисом и/или сервисами аффилированных лиц и/или партнеров (в т. ч. IP-адрес хоста, вид операционной системы, тип браузера, географическое положение, данные о провайдере и иное), об активности Пользователя, а также иные данные, получаемые указанными способами.

+

7.5. Под обработкой данных понимается любое действие (операция) или совокупность действий (операций), совершаемых с использованием средств автоматизации или без использования таких средств с персональными данными Пользователя, включая сбор, запись, систематизацию, накопление, хранение, уточнение (обновление, изменение), сопоставление, извлечение, использование, передача аффилированным лицам Компании и/или Исполнителя в целях и на условиях настоящего Соглашения, обезличивание, блокирование, удаление, уничтожение.

+

7.6. Компания имеет право отправлять Пользователю от своего имени самостоятельно или с привлечением технических партнеров информационные, в том числе сервисные и рекламные сообщения, на электронную почту Пользователя, мобильный телефон (смс, телефонные звонки) или через используемые им сервисы Компании (социальные сети, мессенджеры и иные). Пользователь вправе отказаться от получения рекламной и другой информации без объяснения причин отказа. Сервисные сообщения, информирующие Пользователя о заказе и этапах его обработки, отправляются автоматически и не могут быть отклонены Пользователем.

+

7.7. Компания вправе использовать технологию «cookies». «Cookies» не содержат конфиденциальную информацию, и Компания вправе передавать информацию о «cookies» Исполнителям, агентам и третьим лицам, имеющим заключенные с Компанией договоры, для исполнения обязательств перед Пользователем и для целей статистики и оптимизации рекламных сообщений.

+

7.8. Компания получает информацию об ip-адресе посетителя Сайта https://goodfood.acceleratorpracticum.ru/. Данная информация не используется для установления личности посетителя.

+

7.9. Компания не несет ответственности за сведения, предоставленные Пользователем на Сайте в общедоступной форме.

+

7.10. Компания вправе осуществлять записи телефонных разговоров с Пользователем. При этом Компания обязуется предотвращать попытки несанкционированного доступа к информации, полученной в ходе телефонных переговоров, и/или передачу ее третьим лицам, не имеющим непосредственного отношения к исполнению заказов в соответствие с п. 4 ст. 16 Федерального закона «Об информации, информационных технологиях и защите информации».

+

8. Регистрация на Сервисе, пароль и безопасность

+

8.1. Для получения права использования Пользователем сервиса Пользователю рекомендуется осуществить регистрацию учетной записи Пользователя на сервисе.

+

8.2. Регистрация Пользователя осуществляется следующим образом:

+

а) ввести в форму сервиса абонентский номер телефона в федеральном формате (89ХХХХХХХХХ), указанный Пользователем при регистрации абонентский номер телефона будет использоваться в качестве имени Пользователя (логин) при использовании сервиса;

+

б) ввести пароль, который придет на указанный номер мобильного телефона в виде sms-сообщения. В последующем пароль может быть изменен Пользователем в личном кабинете своего профиля;

+

в) принять лицензионное соглашение – в случае работы с Приложением;

+

г) принять настоящее соглашение-оферту, согласившись с её условиями;

+

д) при желании дать свое согласие на получение информации (рекламы) о проводимых акциях Компании в соответствии с условиями настоящего соглашения.

+

8.3. Совершая действия по регистрации учетной записи Пользователя в Сервисе, Пользователь принимает условия настоящего Соглашения, в полном объеме и без каких-либо изъятий.

+

8.4. Регистрация Пользователя позволяет избежать несанкционированных действий третьих лиц от имени Пользователя и открывает последнему доступ к дополнительным сервисам. Передача Пользователем логина и пароля третьим лицам не допускается.

+

8.5. Заказ Товара осуществляется Пользователем как через сервис, так и по телефону.

+

9. Оформление и сроки выполнения заказа

+

9.1. Заказ Пользователя может быть оформлен по телефону и/или посредством заполнения электронной формы Заказа на Сервисе.

+

9.1.1. При оформлении Заказа Пользователь сам подтверждает, что ознакомлен с условиями настоящего соглашения и обязуется предоставить всю информацию, необходимую для надлежащего оформления и исполнения Заказа.

+

9.1.2. При оформлении Заказа через Сервис Пользователь заполняет электронную форму Заказа и отправляет сформированный Заказ путем подтверждения Заказа в электронной форме.

+

9.1.3. Для приема в обработку Заказа, который был оформлен Пользователем через Сервис Компании необходимо подтверждение Компании посредством телефонного звонка на контактный номер Пользователя в том, что данный Заказ получен, принят и передан в обработку Исполнителю. Заказ считается принятым в обработку, начиная с момента его подтверждения.

+

9.1.4. Если Заказ, который был оформлен Пользователем через сервис Компании, не был подтвержден со стороны Компании Пользователю, то Пользователь должен самостоятельно убедиться по телефону 8(812)383-0-383 в том, что его Заказ был получен, принят и передан в обработку Исполнителю.

+

9.2. Пользователь может заказать только те Товары, которые есть в наличии у Исполнителя в момент оформления Заказа.

+

9.2.1. Если у Исполнителя отсутствует необходимое количество или ассортимент заказанного Пользователем Товара, Компания информирует об этом Пользователя по телефону в течение 30 минут после получения Заказа от Пользователя. Пользователь вправе согласиться принять Товар в ином количестве или ассортименте, либо аннулировать свой Заказ. В случае неполучения ответа Пользователя Заказ Пользователя аннулируется в полном объеме.

+

9.3. Пользователь не имеет право изменить состав Заказа.

+

9.4. В случае возникновения у Пользователя дополнительных вопросов, касающихся характеристик Товара, перед оформлением Заказа, Пользователь должен обратиться к Компании по телефону 8(812)383-0-383 для получения необходимой информации.

+

9.5. Исполнитель получает информацию о Заказе Пользователя в течение 5 минут с момента приема Заказа Компанией. Исполнитель приступает к выполнению Заказа в порядке очередности всех Заказов, находящихся у него на исполнении.

+

9.6. Компания при оформлении Заказа от Пользователя информирует последнего о планируемом времени доставки Заказа по адресу Пользователя. Если при оформлении Заказа Пользователь не аннулировал Заказ и данный Заказ был оформлен Компанией, соответственно Пользователь согласен с обозначенным ему временем доставки Заказа и Заказ будет передан Исполнителю для выполнения.

+

10. Доставка Товара

+

10.1. В случае если Товар не был передан Пользователю по вине последнего, отказа Пользователя от приемки и/или оплаты им Товара, ложного вызова, логин Пользователя (абонентский номер телефона) подлежит блокированию и в дальнейшем Заказы от данного Пользователя по телефону и/или через сервис не принимаются.

+

10.2. Доставка Товара осуществляется по фактическому адресу, указанному Пользователем при оформлении Заказа через сервис и/или по телефону Компании.

+

10.2.1. Средний срок доставки оформленного Заказа составляет 45-60 минут. Данное время может быть увеличено в виду погодных условий, ситуации на дороге, загруженностью на кухне Исполнителя.

+

10.2.2. Возможность доставки Товара за пределы зоны доставки Пользователь обязан предварительно согласовать с Исполнителем. Исполнитель вправе отказать в доставке Заказа, если он не входит в пределы зоны доставки.

+

10.2.3. Компания не несет ответственности за соблюдение/несоблюдение Исполнителем и/или Доставщиком своих обязательств перед Пользователями, а также за достоверность информации, предоставленной такими службами. Компания со своей стороны способствует урегулированию различных ситуаций, возникающих между Пользователем и Исполнителем и/или Доставкой, но не гарантирует положительное и окончательное их решение для той, или иной Стороны.

+

10.3. Доставка осуществляется при условии, что Пользователь сделает Заказ на сумму минимального заказа. Сумма минимального заказа определяется Исполнителем и в одностороннем порядке и указывается на Сервисе Компанией.

+

11. Форс-мажор

+

11. Любая из Сторон в соответствии с настоящим соглашением, освобождается от ответственности за полное или частичное неисполнение своих обязательств по настоящему соглашению, если это неисполнение было вызвано обстоятельствами непреодолимой силы. Обстоятельства непреодолимой силы означают чрезвычайные события и обстоятельства, которые Стороны не могли ни предвидеть, ни предотвратить разумными средствами. Такие чрезвычайные события или обстоятельства включают в себя, в частности: забастовки, наводнения, пожары, землетрясения и иные стихийные бедствия, войны, военные действия и т.д.

+

12. Допущения при производстве продукции, согласно технологическим картам.

+

12.1. Запеченные роллы: запекание – это процесс приготовления роллов и суши, и не является его температурной характеристикой;

+

12.2. Роллы с крабовым мясом: допускается попадание кусочков хитина/осколков панциря не более 5 мм;

+

12.3. Лосось: допускаются разные оттенки лосося с широкими прожилками при смене поставщиков на производстве;

+

12.4. Темный цвет у авокадо: цвет у авокадо зависит от окисляемости продукта, по стандартам изготовления цвет не влияет на вкусовые ощущения;

+

12.5. Мидии: допускается попадание мяса краба в блюда с мидиями и кусочков жемчуга;

+

12.6. Неочищенный картофель: при приготовлении некоторых блюд, в том числе супов, используется молодой картофель в мундире;

+

12.7. Мидии в сливочном соусе: допускается попадание закрытых мидий не более 10% от общего количества;

+

12.8. Французский томатный суп с морепродуктами: допускается попадание закрытых мидий;

+

12.9. Допускается попадание кожуры от семечек в блюдах с ростками подсолнуха;

+

13. Заключительные положения

+

13. Настоящее Соглашение вступает в силу для Пользователя с момента регистрации на Сервисе и/или оформления Заказа и действует до тех пор, пока не будет изменено или расторгнуто по инициативе Пользователя или Компании.

+

13.1. Настоящее Соглашение составлено на русском языке.

+

13.2. Если какое-либо из положений настоящего Соглашения будет признано недействительным, это не оказывает влияния на действительность или применимость остальных положений настоящего Соглашения.

+

13.3. Дальнейшее использование сервиса означает, что Пользователь принял на себя ответственность за безусловное соблюдение настоящего Соглашения.

+

14. Информация о Компании

+

ООО «ГудФуд»

+

ИНН/КПП:

+

ОГРН:

+

Телефон: 8-800-000-444-333

+

Адрес для переписки:

+

Дата публикации последней редакции Соглашения об условиях доставки 20.11.2023 г.

+

Дата вступления в силу последней редакции Соглашения об условиях доставки 20.11.2023 г.

+
+
+ + ); +}; + +export default DeliveryConditions; From 914dabe51bc33f96c0aaa28d131b713e9ebebb24 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Wed, 20 Dec 2023 15:48:46 +0300 Subject: [PATCH 002/141] feat: change workflow file --- .github/workflows/good_food_frontend_workflow.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/good_food_frontend_workflow.yaml b/.github/workflows/good_food_frontend_workflow.yaml index a656dbbd..249396ac 100644 --- a/.github/workflows/good_food_frontend_workflow.yaml +++ b/.github/workflows/good_food_frontend_workflow.yaml @@ -8,7 +8,19 @@ jobs: check_codestyle: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 + - name: send start message + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_TO }} + token: ${{ secrets.TELEGRAM_TOKEN }} + message: | + ${{ github.workflow }} started! + + Repository: ${{ github.repository }}. + Branch name: ${{ github.ref_name }}. + Commit author: ${{ github.actor }}. + Commit message: ${{ github.event.commits[0].message }}. - name: Use Node.js uses: actions/setup-node@v2 with: From b546aa917cfc5521659879224d692c75dd0c53ee Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Wed, 20 Dec 2023 10:48:46 +0300 Subject: [PATCH 003/141] fix: values in counter --- src/components/popups/popup-recipe/index.tsx | 2 +- .../ingredients-list-popup/index.tsx | 4 +- .../ingredients-list/index.tsx | 7 +++- .../products-list-popup/index.tsx | 38 +++++++++--------- src/pages/recipe/index.tsx | 39 ++++++++++--------- 5 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/components/popups/popup-recipe/index.tsx b/src/components/popups/popup-recipe/index.tsx index 1d481ce2..7226ac8c 100644 --- a/src/components/popups/popup-recipe/index.tsx +++ b/src/components/popups/popup-recipe/index.tsx @@ -12,7 +12,7 @@ type RecipeIngredientsProps = { measure_unit: string; quantity: number; ingredient_photo: string; - photo?: string; + amount_of_pack: number; amount?: number; price?: number; }[]; diff --git a/src/components/recipes-components/ingredients-list-popup/index.tsx b/src/components/recipes-components/ingredients-list-popup/index.tsx index 2106822f..4443fb8b 100644 --- a/src/components/recipes-components/ingredients-list-popup/index.tsx +++ b/src/components/recipes-components/ingredients-list-popup/index.tsx @@ -7,8 +7,10 @@ type RecipeIngredientsProps = { name: string; measure_unit: string; quantity: number; - photo?: string; + ingredient_photo: string; + amount_of_pack: number; amount?: number; + price?: number; }[]; }; diff --git a/src/components/recipes-components/ingredients-list/index.tsx b/src/components/recipes-components/ingredients-list/index.tsx index 430f7998..10cc02c7 100644 --- a/src/components/recipes-components/ingredients-list/index.tsx +++ b/src/components/recipes-components/ingredients-list/index.tsx @@ -9,7 +9,8 @@ type RecipeIngredientsProps = { name: string; measure_unit: string; quantity: number; - ingredient_photo?: string; + ingredient_photo: string; + amount_of_pack: number; amount?: number; price?: number; }[]; @@ -45,7 +46,9 @@ const IngredientsList: React.FC = ({ ingredients }) => { alt={ingredient.name as string} /> -

{ingredient.name}

+

{`${ingredient.name}, ${ingredient.amount}${ingredient.measure_unit}`}

{`${ingredient?.quantity} ${ingredient.measure_unit}`}

diff --git a/src/components/recipes-components/products-list-popup/index.tsx b/src/components/recipes-components/products-list-popup/index.tsx index be2af4c6..b1070f54 100644 --- a/src/components/recipes-components/products-list-popup/index.tsx +++ b/src/components/recipes-components/products-list-popup/index.tsx @@ -11,7 +11,8 @@ type ReceipeIngredient = { name: string; measure_unit: string; quantity: number; - photo?: string; + ingredient_photo: string; + amount_of_pack: number; amount?: number; price?: number; }; @@ -32,7 +33,7 @@ const ProductsListPopup: React.FC = ({ ingredients }) => setProducts( products?.map((product, i) => { if (i === index) { - product.amount = Math.min(Math.max(newAmount, 1), 20); + product.amount_of_pack = Math.min(Math.max(newAmount, 1), 20); } return product; }) @@ -45,12 +46,7 @@ const ProductsListPopup: React.FC = ({ ingredients }) => return; } - setProducts( - ingredients.map((i) => { - i.amount = 1; - return i; - }) - ); + setProducts(ingredients); }, [ingredients]); return ( @@ -64,33 +60,37 @@ const ProductsListPopup: React.FC = ({ ingredients }) => key={product.name} >
- {product.name} + {product.name}
-

{product.name}

+

{`${product.name}, ${product.amount}${product.measure_unit}`}

-

{`${product.amount} уп.`}

+

{`${product.amount_of_pack} уп.`}

- {product.price && product.amount && ( + {product.price && product.amount_of_pack && (

{`${ - product.price * product.amount + product.price * product.amount_of_pack } руб.`}

)} + ) ); From d0427fbd37f7c6d205f71669c1d1aa602116fef0 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 21 Dec 2023 14:00:54 +0300 Subject: [PATCH 007/141] feat: implement button adding recipe products to cart --- .../products-list-popup/index.tsx | 19 +++++- .../products-list-popup.module.scss | 32 ++++++++- src/contexts/cart-context.tsx | 67 ++++++++++++++++++- src/pages/recipe/index.tsx | 9 ++- 4 files changed, 120 insertions(+), 7 deletions(-) diff --git a/src/components/recipes-components/products-list-popup/index.tsx b/src/components/recipes-components/products-list-popup/index.tsx index 4645b31e..d299aa1d 100644 --- a/src/components/recipes-components/products-list-popup/index.tsx +++ b/src/components/recipes-components/products-list-popup/index.tsx @@ -23,13 +23,15 @@ type RecipeIngredientsProps = { const ProductsListPopup: React.FC = ({ ingredients }) => { const [products, setProducts] = useState(Array); - const { updateCart } = useCart(); + const { updateCart, error, reset, successText, cartUpdating } = useCart(); const filterProducts = (index: number) => { setProducts((prev) => prev.filter((_, i) => i !== index)); }; const changeAmount = (index: number) => { + reset(); + return (newAmount: number) => { setProducts( products?.map((product, i) => { @@ -111,7 +113,20 @@ const ProductsListPopup: React.FC = ({ ingredients }) => ))} - diff --git a/src/components/recipes-components/products-list-popup/products-list-popup.module.scss b/src/components/recipes-components/products-list-popup/products-list-popup.module.scss index eaa76c03..3e49c9c4 100644 --- a/src/components/recipes-components/products-list-popup/products-list-popup.module.scss +++ b/src/components/recipes-components/products-list-popup/products-list-popup.module.scss @@ -36,7 +36,18 @@ line-height: 1.4; cursor: pointer; color: #fff; - background: $form-button-color; + background-color: $form-button-color; + transition: + background-color 0.2s ease-in-out, + color 0.2s ease-in-out; + } + + &__button:disabled { + background-color: $gray-button; + color: $footer-background-color; + transition: + background-color 0.2s ease-in-out, + color 0.2s ease-in-out; } } @@ -75,6 +86,25 @@ background-color: transparent; border: none; } + + &__resultText { + color: $error-color; + font-size: 13px; + font-weight: 300; + line-height: 140%; + min-height: 30px; + margin: 0; + } + + &__error { + color: $error-color; + font-weight: 300; + } + + &__success { + color: $accent-color-bright-green; + font-weight: 400; + } } .counter { diff --git a/src/contexts/cart-context.tsx b/src/contexts/cart-context.tsx index c9e71644..d6dae529 100644 --- a/src/contexts/cart-context.tsx +++ b/src/contexts/cart-context.tsx @@ -34,6 +34,18 @@ type CartDataItem = { type CartContextType = { cartData: CartDataItem; loading: boolean; + cartUpdating: boolean; + error: { + loadCartData: string; + updateCart: string; + deleteCart: string; + }; + successText: { + loadCartData: string; + updateCart: string; + deleteCart: string; + }; + reset: () => void; loadCartData: () => void; updateCart: (data: Product[]) => void; deleteCart: (id: number) => void; @@ -48,6 +60,18 @@ const CartContext = createContext({ total_price: 0, }, loading: true, + error: { + loadCartData: '', + updateCart: '', + deleteCart: '', + }, + successText: { + loadCartData: '', + updateCart: '', + deleteCart: '', + }, + cartUpdating: false, + reset: () => {}, loadCartData: () => {}, updateCart: () => {}, deleteCart: () => {}, @@ -63,17 +87,31 @@ export const CartProvider: React.FC = ({ children }) => { }); const [cartUpdating, setCartUpdating] = useState(false); const [loading, setLoading] = useState(true); + const [successText, setSuccessText] = useState({ + loadCartData: '', + updateCart: '', + deleteCart: '', + }); + const [error, setError] = useState({ + loadCartData: '', + updateCart: '', + deleteCart: '', + }); const loadCartData = () => { + setCartUpdating(true); + setLoading(true); + api .usersShoppingCartList() .then((data) => { setCartData(data); - setCartUpdating(true); - setLoading(true); }) .catch((error) => { console.error('Error loading cart data:', error); + setError((prev) => { + return { ...prev, loadCartData: error.errors[0].detail }; + }); }) .finally(() => { setCartUpdating(false); @@ -82,18 +120,25 @@ export const CartProvider: React.FC = ({ children }) => { }; const updateCart = (data: Product[]) => { + reset(); const updatedCartItem: ShoppingCartItem = { products: data, }; + setCartUpdating(true); api .usersShoppingCartCreate(updatedCartItem) .then((data) => { setCartData(data); - setCartUpdating(true); + setSuccessText((prev) => { + return { ...prev, updateCart: 'Продукты успешно добавлены в корзину' }; + }); }) .catch((error) => { console.error('Error updating cart:', error); + setError((prev) => { + return { ...prev, updateCart: error.errors[0].detail }; + }); }) .finally(() => { setCartUpdating(false); @@ -108,6 +153,9 @@ export const CartProvider: React.FC = ({ children }) => { }) .catch((error) => { console.error('Error deleting cart item:', error); + setError((prev) => { + return { ...prev, deleteCart: error.errors[0].detail }; + }); }); }; @@ -136,6 +184,15 @@ export const CartProvider: React.FC = ({ children }) => { } }; + const reset = () => { + setError((prev) => { + return { ...prev, updateCart: '' }; + }); + setSuccessText((prev) => { + return { ...prev, updateCart: '' }; + }); + }; + const removeItemFromCart = (productId: number) => { if (!cartUpdating) { const existingItemIndex = cartData.products.findIndex( @@ -165,6 +222,10 @@ export const CartProvider: React.FC = ({ children }) => { value={{ cartData, loading, + error, + successText, + cartUpdating, + reset, loadCartData, updateCart, deleteCart, diff --git a/src/pages/recipe/index.tsx b/src/pages/recipe/index.tsx index f833ad87..f5ef4883 100644 --- a/src/pages/recipe/index.tsx +++ b/src/pages/recipe/index.tsx @@ -8,6 +8,7 @@ import api from '@services/api.ts'; import Preloader from '@components/preloader'; import { useEffect } from 'react'; import { useParams } from 'react-router'; +import { useCart } from '@hooks/use-cart-context'; import RecipeInfo from '@components/recipes-components/recipe-info'; import { usePopup } from '@hooks/use-popup'; import PopupRecipe from '@components/popups/popup-recipe'; @@ -48,6 +49,7 @@ const Recipe: React.FC = () => { const [recipeInfo, setRecipeInfo] = useState(Object); const [recipeByLines, setRecipeByLines] = useState(['']); const [numeralizeWord, setNumeralizeWord] = useState(''); + const { reset } = useCart(); useEffect(() => { if (!id) { @@ -91,6 +93,11 @@ const Recipe: React.FC = () => { fetchReceiptAndProducts().finally(() => setIsLoading(false)); }, [id]); + const handleAddToCart = () => { + reset(); + handleOpenPopup('openPopupRecipe'); + }; + return (
{isLoading ? ( @@ -113,7 +120,7 @@ const Recipe: React.FC = () => { From 49012e2adecc5424f14e41d8cbe1f341ee0a9e1a Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 21 Dec 2023 17:27:43 +0300 Subject: [PATCH 008/141] fix: fix error in case of empty response when deleting --- src/contexts/cart-context.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/contexts/cart-context.tsx b/src/contexts/cart-context.tsx index d6dae529..cba45942 100644 --- a/src/contexts/cart-context.tsx +++ b/src/contexts/cart-context.tsx @@ -153,9 +153,11 @@ export const CartProvider: React.FC = ({ children }) => { }) .catch((error) => { console.error('Error deleting cart item:', error); - setError((prev) => { - return { ...prev, deleteCart: error.errors[0].detail }; - }); + if (error?.errors) { + setError((prev) => { + return { ...prev, deleteCart: error.errors[0]?.detail }; + }); + } }); }; From 9b1f1e4991a580daa42938c01601870d32077b2a Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Fri, 22 Dec 2023 10:57:50 +0300 Subject: [PATCH 009/141] fix: padding in category --- src/pages/category/category.module.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/category/category.module.scss b/src/pages/category/category.module.scss index 0373995c..c1ed940d 100644 --- a/src/pages/category/category.module.scss +++ b/src/pages/category/category.module.scss @@ -66,8 +66,10 @@ .category__sorting { width: 328px; margin-right: 20px; + padding-top: 58px; @media screen and (width <= 768px) { + padding-top: 0; margin-right: 0; max-width: 280px; } From 4e75d4b658b03e2706a228240e2b57bbb397f9c2 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Fri, 22 Dec 2023 11:17:16 +0300 Subject: [PATCH 010/141] fix: delete package name from counters --- .../recipes-components/products-list-popup/index.tsx | 2 +- src/components/shopping-item/index.tsx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/recipes-components/products-list-popup/index.tsx b/src/components/recipes-components/products-list-popup/index.tsx index b1070f54..37421e05 100644 --- a/src/components/recipes-components/products-list-popup/index.tsx +++ b/src/components/recipes-components/products-list-popup/index.tsx @@ -76,7 +76,7 @@ const ProductsListPopup: React.FC = ({ ingredients }) => > минус -

{`${product.amount_of_pack} уп.`}

+

{product.amount_of_pack}

-

{`${product.quantity} шт`}

+

{product.quantity}

diff --git a/src/pages/shopping-cart/index.tsx b/src/pages/shopping-cart/index.tsx index e73cfa1c..764117c5 100644 --- a/src/pages/shopping-cart/index.tsx +++ b/src/pages/shopping-cart/index.tsx @@ -118,7 +118,10 @@ const ShoppingCart: React.FC = () => {

- + )} From de8d3e1b3d271c1abd689fb92b7decf92dbc042e Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Sat, 23 Dec 2023 17:39:10 +0300 Subject: [PATCH 013/141] fix: interface for MakingOrderBtn props --- src/components/Button/index.tsx | 2 +- src/components/making-order-btn/index.tsx | 10 ++++------ src/pages/shopping-cart/index.tsx | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 1f3791ec..f9aee6c7 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import styles from './button.module.scss'; -type ButtonProps = { +export type ButtonProps = { buttonText: string; buttonStyle: string; classNameActive?: string; diff --git a/src/components/making-order-btn/index.tsx b/src/components/making-order-btn/index.tsx index 311d8411..8c6c7922 100644 --- a/src/components/making-order-btn/index.tsx +++ b/src/components/making-order-btn/index.tsx @@ -1,20 +1,18 @@ import React from 'react'; import styles from './making-order-btn.module.scss'; import Button from '@components/Button'; +import type { ButtonProps } from '@components/Button'; -interface MakingOrderBtnProps { - isDisabled: boolean; - onClick?: () => void; -} +interface MakingOrderBtnProps extends Pick {} -const MakingOrderBtn: React.FC = ({ onClick, isDisabled }) => { +const MakingOrderBtn: React.FC = ({ onClick, disabled }) => { return (
From c623293ccea5e1a18b5238b330e268c73593dcf9 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Sat, 23 Dec 2023 17:51:18 +0300 Subject: [PATCH 014/141] fix: add all className variants to type ButtonProps --- src/components/Button/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index f9aee6c7..da9e54e8 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -3,8 +3,12 @@ import styles from './button.module.scss'; export type ButtonProps = { buttonText: string; - buttonStyle: string; - classNameActive?: string; + buttonStyle: + | 'green-border-button' + | 'green-border-button__active' + | 'greenish-button' + | 'green-button'; + classNameActive?: 'greenish-button__active' | ''; onClick?: () => void; disabled?: boolean; classNames?: string; From bfa90c169d645f9cfacdf73756b40d90a7542f0e Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Fri, 22 Dec 2023 16:08:34 +0300 Subject: [PATCH 015/141] feat: implement button adding favourites to cart --- src/pages/profile/profile-favorites/index.tsx | 61 +++++++++++++++---- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/src/pages/profile/profile-favorites/index.tsx b/src/pages/profile/profile-favorites/index.tsx index 6a12cd1c..ff6129d8 100644 --- a/src/pages/profile/profile-favorites/index.tsx +++ b/src/pages/profile/profile-favorites/index.tsx @@ -4,6 +4,7 @@ import api from '@services/api'; import ProductCard from '@components/product-card'; import ReturnBackButton from '@components/profile-components/return-back-button'; import { useProfile } from '@hooks/use-profile'; +import { useCart } from '@hooks/use-cart-context'; import type { Product } from '@services/generated-api/data-contracts'; import { Link } from 'react-router-dom'; @@ -14,11 +15,17 @@ export default function ProfileFavorites() { error: '', }); const [products, setProducts] = useState>([]); - const [checkboxesValues, setCheckboxesValue] = useState([]); + const [checkboxesValues, setCheckboxesValue] = useState>({}); const { isMobileScreen } = useProfile(); + const { updateCart } = useCart(); useEffect(() => { - products && setCheckboxesValue(products.map(() => false)); + products && + products.forEach((product) => { + setCheckboxesValue((prev) => { + return { ...prev, [product.id]: false }; + }); + }); }, [products]); useEffect(() => { @@ -46,18 +53,40 @@ export default function ProfileFavorites() { }); }, []); - const onCheckButton = (index: number) => { + const onCheckButton = (id: number) => { return () => { - setCheckboxesValue( - checkboxesValues.map((value, i) => (i === index ? !value : value)) - ); + setCheckboxesValue((prev) => { + return { ...prev, [id]: !prev[id] }; + }); }; }; - const isChooseAll = checkboxesValues.every((el) => el) || !products.length; + const isChooseAll = + Object.values(checkboxesValues).every((el) => el) || !products.length; const toggleAll = () => { - setCheckboxesValue(checkboxesValues.map(() => (isChooseAll ? false : true))); + if (isChooseAll) { + Object.keys(checkboxesValues).forEach((key) => { + setCheckboxesValue((prev) => { + return { ...prev, [key]: false }; + }); + }); + } else { + Object.keys(checkboxesValues).forEach((key) => { + setCheckboxesValue((prev) => { + return { ...prev, [key]: true }; + }); + }); + } + }; + + const handleAddToCart = () => { + const dataToSend = Object.keys(checkboxesValues) + .filter((i) => checkboxesValues[i as unknown as keyof typeof checkboxesValues]) + .map((item) => ({ id: Number(item), quantity: 1 })); + updateCart(dataToSend); + + window.scroll(0, 0); }; return ( @@ -71,7 +100,7 @@ export default function ProfileFavorites() {
    {!productsLoadingStatus.inProcess && products.length ? ( - products.map((product, index) => ( + products.map((product) => (
  • )}
- + ); } From d2fc67f4e84c8c8e51a5ae1d48d4080c436d4bd4 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Fri, 22 Dec 2023 19:26:19 +0300 Subject: [PATCH 016/141] feat: add price near cart icon in header --- src/components/navigation-icons/index.tsx | 3 +++ .../navigation-icons.module.scss | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/components/navigation-icons/index.tsx b/src/components/navigation-icons/index.tsx index 6077540b..b604cd03 100644 --- a/src/components/navigation-icons/index.tsx +++ b/src/components/navigation-icons/index.tsx @@ -3,10 +3,12 @@ import { Link } from 'react-router-dom'; import { useAuth } from '@hooks/use-auth.ts'; import styles from './navigation-icons.module.scss'; import { usePopup } from '@hooks/use-popup.ts'; +import { useCart } from '@hooks/use-cart-context'; const NavigationIcons: React.FC = () => { const { isLoggedIn } = useAuth(); const { handleOpenPopup } = usePopup(); + const { cartData } = useCart(); return (
@@ -20,6 +22,7 @@ const NavigationIcons: React.FC = () => { Date: Fri, 22 Dec 2023 21:08:38 +0300 Subject: [PATCH 017/141] feat: add price near cart icon in header for not-auth user --- src/components/navigation-icons/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/navigation-icons/index.tsx b/src/components/navigation-icons/index.tsx index b604cd03..5a504c00 100644 --- a/src/components/navigation-icons/index.tsx +++ b/src/components/navigation-icons/index.tsx @@ -39,6 +39,7 @@ const NavigationIcons: React.FC = () => {
    {array.map((item: Record, index: number) => ( @@ -41,13 +36,6 @@ function CardCatalogLink({ title, array, type, category }: CardCatalogLinkProps) key={index} style={{ gridArea: item.gridArea }} > - {type === 'bento-grid' && ( - - )} {type === 'single-row' && ( Date: Mon, 25 Dec 2023 13:49:49 +0300 Subject: [PATCH 021/141] feat: add title-link for catalog on main page --- src/assets/images/chevron-right.svg | 2 +- .../catalog-promo/catalog-promo.module.scss | 53 +++++++++++++++++++ src/components/catalog-promo/index.tsx | 7 +++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/assets/images/chevron-right.svg b/src/assets/images/chevron-right.svg index 81ea602a..bea4b50d 100644 --- a/src/assets/images/chevron-right.svg +++ b/src/assets/images/chevron-right.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/catalog-promo/catalog-promo.module.scss b/src/components/catalog-promo/catalog-promo.module.scss index 45dffb48..d27b17ab 100644 --- a/src/components/catalog-promo/catalog-promo.module.scss +++ b/src/components/catalog-promo/catalog-promo.module.scss @@ -164,3 +164,56 @@ height: 12px; } } + +.link__container { + padding-bottom: 28px; + + @media screen and (width <= 768px) { + padding-bottom: 8px; + } +} + +.link__title { + color: $active-text-color; + font-family: $ubuntu-font; + font-size: 30px; + font-weight: 700; + line-height: 140%; + margin: 0; + padding-right: 5px; + + @media screen and (width <= 768px) { + font-size: 24px; + padding-right: 3px; + } +} + +.link__arrow { + width: 20px; + height: 32px; + margin-top: auto; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + stroke: $active-text-color; + + @media screen and (width <= 768px) { + width: 18px; + height: 28px; + } +} + +.link { + text-decoration: none; + display: flex; + max-width: max-content; + + &:hover .link__title, + &:hover .link__arrow { + stroke: $accent-color-bright-green; + color: $accent-color-bright-green; + transition: + color 0.3s ease-in-out, + stroke 0.3s ease-in-out; + } +} diff --git a/src/components/catalog-promo/index.tsx b/src/components/catalog-promo/index.tsx index 411167b5..ac58ccaa 100644 --- a/src/components/catalog-promo/index.tsx +++ b/src/components/catalog-promo/index.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; +import ArrowIcon from '@images/chevron-right.svg?react'; import clsx from 'clsx'; import { useProfile } from '@hooks/use-profile'; import api from '@services/api'; @@ -37,6 +38,12 @@ const CatalogPromo = () => { }, []); return ( <> +
    + +

    Каталог

    + + +
      {categories.slice(0, numberToShow).map((category) => (
    • From 0d4208553441e7998941e22a1264aba7e5ea9cb5 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Mon, 25 Dec 2023 15:31:53 +0300 Subject: [PATCH 022/141] feat: add component TitleArrowLink --- src/assets/images/chevron-right-no-stroke.svg | 3 ++ src/assets/images/chevron-right.svg | 2 +- .../catalog-promo/catalog-promo.module.scss | 47 +------------------ src/components/catalog-promo/index.tsx | 9 ++-- src/components/title-arrow-link/index.tsx | 20 ++++++++ .../title-arrow-link.module.scss | 46 ++++++++++++++++++ 6 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 src/assets/images/chevron-right-no-stroke.svg create mode 100644 src/components/title-arrow-link/index.tsx create mode 100644 src/components/title-arrow-link/title-arrow-link.module.scss diff --git a/src/assets/images/chevron-right-no-stroke.svg b/src/assets/images/chevron-right-no-stroke.svg new file mode 100644 index 00000000..bea4b50d --- /dev/null +++ b/src/assets/images/chevron-right-no-stroke.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/chevron-right.svg b/src/assets/images/chevron-right.svg index bea4b50d..81ea602a 100644 --- a/src/assets/images/chevron-right.svg +++ b/src/assets/images/chevron-right.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/catalog-promo/catalog-promo.module.scss b/src/components/catalog-promo/catalog-promo.module.scss index d27b17ab..a4b0c873 100644 --- a/src/components/catalog-promo/catalog-promo.module.scss +++ b/src/components/catalog-promo/catalog-promo.module.scss @@ -165,55 +165,10 @@ } } -.link__container { +.link { padding-bottom: 28px; @media screen and (width <= 768px) { padding-bottom: 8px; } } - -.link__title { - color: $active-text-color; - font-family: $ubuntu-font; - font-size: 30px; - font-weight: 700; - line-height: 140%; - margin: 0; - padding-right: 5px; - - @media screen and (width <= 768px) { - font-size: 24px; - padding-right: 3px; - } -} - -.link__arrow { - width: 20px; - height: 32px; - margin-top: auto; - background-position: center; - background-size: cover; - background-repeat: no-repeat; - stroke: $active-text-color; - - @media screen and (width <= 768px) { - width: 18px; - height: 28px; - } -} - -.link { - text-decoration: none; - display: flex; - max-width: max-content; - - &:hover .link__title, - &:hover .link__arrow { - stroke: $accent-color-bright-green; - color: $accent-color-bright-green; - transition: - color 0.3s ease-in-out, - stroke 0.3s ease-in-out; - } -} diff --git a/src/components/catalog-promo/index.tsx b/src/components/catalog-promo/index.tsx index ac58ccaa..551b77cb 100644 --- a/src/components/catalog-promo/index.tsx +++ b/src/components/catalog-promo/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; -import ArrowIcon from '@images/chevron-right.svg?react'; +import TitleArrowLink from '@components/title-arrow-link'; import clsx from 'clsx'; import { useProfile } from '@hooks/use-profile'; import api from '@services/api'; @@ -38,11 +38,8 @@ const CatalogPromo = () => { }, []); return ( <> -
      - -

      Каталог

      - - +
      +
        {categories.slice(0, numberToShow).map((category) => ( diff --git a/src/components/title-arrow-link/index.tsx b/src/components/title-arrow-link/index.tsx new file mode 100644 index 00000000..88c30e94 --- /dev/null +++ b/src/components/title-arrow-link/index.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import ArrowIcon from '@images/chevron-right-no-stroke.svg?react'; +import styles from './title-arrow-link.module.scss'; + +type TitleArrowLinkProps = { + title: string; + link: string; +}; + +const TitleArrowLink: React.FC = ({ title, link }) => { + return ( + +

        {title}

        + + + ); +}; + +export default TitleArrowLink; diff --git a/src/components/title-arrow-link/title-arrow-link.module.scss b/src/components/title-arrow-link/title-arrow-link.module.scss new file mode 100644 index 00000000..de4af51c --- /dev/null +++ b/src/components/title-arrow-link/title-arrow-link.module.scss @@ -0,0 +1,46 @@ +@use '@scss/variables' as *; + +.link__title { + color: $active-text-color; + font-family: $ubuntu-font; + font-size: 30px; + font-weight: 700; + line-height: 140%; + margin: 0; + padding-right: 5px; + + @media screen and (width <= 768px) { + font-size: 24px; + padding-right: 3px; + } +} + +.link__arrow { + width: 20px; + height: 32px; + margin-top: auto; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + stroke: $active-text-color; + + @media screen and (width <= 768px) { + width: 18px; + height: 28px; + } +} + +.link { + text-decoration: none; + display: flex; + max-width: max-content; + + &:hover .link__title, + &:hover .link__arrow { + stroke: $accent-color-bright-green; + color: $accent-color-bright-green; + transition: + color 0.2s ease-in-out, + stroke 0.2s ease-in-out; + } +} From 1fee0b1796f169d1c2fa42b5075ceaacc56f275a Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Mon, 25 Dec 2023 15:41:41 +0300 Subject: [PATCH 023/141] feat: change Recipe title to new TitleArrowLink component --- src/components/our-block/index.tsx | 5 ++++- src/components/our-block/our-block.module.scss | 12 +++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/our-block/index.tsx b/src/components/our-block/index.tsx index 86ddff8c..a80a5bd1 100644 --- a/src/components/our-block/index.tsx +++ b/src/components/our-block/index.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; import TopicCard from '@components/topic-card'; +import TitleArrowLink from '@components/title-arrow-link'; import styles from './our-block.module.scss'; import api from '@services/api'; import type { Recipe } from '@services/generated-api/data-contracts'; @@ -13,7 +14,9 @@ const OurBlock: React.FC = () => { return (
        -

        Рецепты

        +
        + +
        {recipes.map((recipe) => { return ; diff --git a/src/components/our-block/our-block.module.scss b/src/components/our-block/our-block.module.scss index 6b47ea42..6612a20f 100644 --- a/src/components/our-block/our-block.module.scss +++ b/src/components/our-block/our-block.module.scss @@ -5,16 +5,11 @@ width: 100%; } -.our-blog__title { - color: var(--active, #1a1a1a); - font-family: $ubuntu-font; - font-size: 30px; - font-style: normal; - font-weight: 700; - line-height: 140%; +.link { + padding-bottom: 28px; @media screen and (width <= 768px) { - font-size: 24px; + padding-bottom: 8px; } } @@ -22,7 +17,6 @@ display: flex; justify-content: center; gap: 20px; - margin-top: 21px; width: 100%; overflow-x: auto; From 138cb73170400945bf18dbb1098dca737815046e Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Mon, 25 Dec 2023 17:13:45 +0300 Subject: [PATCH 024/141] feat: change Catalog title to new TitleArrowLink component --- .../card-catalog-link.module.scss | 23 ------------------- src/components/card-catalog-link/index.tsx | 7 ++---- src/components/title-arrow-link/index.tsx | 20 +++++++++++++--- .../title-arrow-link.module.scss | 13 +++++++++++ 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/components/card-catalog-link/card-catalog-link.module.scss b/src/components/card-catalog-link/card-catalog-link.module.scss index b825a426..76e6fed3 100644 --- a/src/components/card-catalog-link/card-catalog-link.module.scss +++ b/src/components/card-catalog-link/card-catalog-link.module.scss @@ -19,33 +19,10 @@ } } -.card-catalog-link__title-container_type_bento-grid { - margin-bottom: 28px; -} - .card-catalog-link__title-container_type_single-row { margin-bottom: 16px; } -.card-catalog-link__title { - margin: 0; - color: #1a1a1a; - - @media screen and (width <= 768px) { - font-size: 24px; - } -} - -.card-catalog-link__arrow { - width: 20px; - height: 32px; - margin-top: auto; - background-position: center; - background-size: cover; - background-repeat: no-repeat; - background-image: url('@images/chevron-right.svg'); -} - .card-catalog-link__list-single { margin: 0; padding: 0; diff --git a/src/components/card-catalog-link/index.tsx b/src/components/card-catalog-link/index.tsx index 75fb019f..df5e4dbb 100644 --- a/src/components/card-catalog-link/index.tsx +++ b/src/components/card-catalog-link/index.tsx @@ -1,8 +1,8 @@ /* eslint-disable */ import clsx from 'clsx'; import ProductCard from '../product-card'; +import TitleArrowLink from '@components/title-arrow-link'; import styles from './card-catalog-link.module.scss'; -import { Link } from 'react-router-dom'; import { Product } from '@services/generated-api/data-contracts'; type CardCatalogLinkProps = { @@ -20,10 +20,7 @@ function CardCatalogLink({ title, array, type, category }: CardCatalogLinkProps) styles[`card-catalog-link__title-container_type_${type}`] }`} > - -

        {title}

        - - +
          = ({ title, link }) => { +const TitleArrowLink: React.FC = ({ title, link, type }) => { return ( -

          {title}

          - +

          + {title} +

          + ); }; diff --git a/src/components/title-arrow-link/title-arrow-link.module.scss b/src/components/title-arrow-link/title-arrow-link.module.scss index de4af51c..616ae385 100644 --- a/src/components/title-arrow-link/title-arrow-link.module.scss +++ b/src/components/title-arrow-link/title-arrow-link.module.scss @@ -15,6 +15,12 @@ } } +.link__title_type_smallFont { + @media screen and (width <= 768px) { + font-size: 18px; + } +} + .link__arrow { width: 20px; height: 32px; @@ -30,6 +36,13 @@ } } +.link__arrow_type_small { + @media screen and (width <= 768px) { + width: 14px; + height: 20px; + } +} + .link { text-decoration: none; display: flex; From 0e6f26d2af54557fc6fc6d2357f176dfbb12613b Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Tue, 26 Dec 2023 18:02:55 +0300 Subject: [PATCH 025/141] fix: change recipe fields according to new responce from server --- src/components/popups/popup-recipe/index.tsx | 12 +++--- .../ingredients-list-popup/index.tsx | 14 +++---- .../ingredients-list/index.tsx | 14 +++---- .../products-list-popup/index.tsx | 26 ++++++------ src/pages/recipe/index.tsx | 40 ++++--------------- 5 files changed, 39 insertions(+), 67 deletions(-) diff --git a/src/components/popups/popup-recipe/index.tsx b/src/components/popups/popup-recipe/index.tsx index 7226ac8c..11e8d8ea 100644 --- a/src/components/popups/popup-recipe/index.tsx +++ b/src/components/popups/popup-recipe/index.tsx @@ -7,14 +7,14 @@ import ProductsListPopup from '@components/recipes-components/products-list-popu type RecipeIngredientsProps = { ingredients: { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }[]; }; diff --git a/src/components/recipes-components/ingredients-list-popup/index.tsx b/src/components/recipes-components/ingredients-list-popup/index.tsx index 4443fb8b..cbdbbfc6 100644 --- a/src/components/recipes-components/ingredients-list-popup/index.tsx +++ b/src/components/recipes-components/ingredients-list-popup/index.tsx @@ -3,14 +3,14 @@ import styles from './ingredients-list-popup.module.scss'; type RecipeIngredientsProps = { ingredients: { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }[]; }; @@ -27,7 +27,7 @@ const IngredientsListPopup: React.FC = ({ ingredients }) {`${ingredient?.quantity} ${ingredient?.measure_unit}`} + >{`${ingredient?.quantity_in_recipe} ${ingredient?.measure_unit}`} ); })} diff --git a/src/components/recipes-components/ingredients-list/index.tsx b/src/components/recipes-components/ingredients-list/index.tsx index 10cc02c7..ca8f416e 100644 --- a/src/components/recipes-components/ingredients-list/index.tsx +++ b/src/components/recipes-components/ingredients-list/index.tsx @@ -5,14 +5,14 @@ import clsx from 'clsx'; type RecipeIngredientsProps = { ingredients: { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }[]; }; @@ -51,7 +51,7 @@ const IngredientsList: React.FC = ({ ingredients }) => { >{`${ingredient.name}, ${ingredient.amount}${ingredient.measure_unit}`}

          {`${ingredient?.quantity} ${ingredient.measure_unit}`}

          + >{`${ingredient?.quantity_in_recipe} ${ingredient.measure_unit}`}

        ); })} diff --git a/src/components/recipes-components/products-list-popup/index.tsx b/src/components/recipes-components/products-list-popup/index.tsx index 37421e05..094c871e 100644 --- a/src/components/recipes-components/products-list-popup/index.tsx +++ b/src/components/recipes-components/products-list-popup/index.tsx @@ -7,14 +7,14 @@ import plusIcon from '@images/plus_button.svg'; import minusIcon from '@images/minus_button.svg'; type ReceipeIngredient = { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }; type RecipeIngredientsProps = { @@ -33,7 +33,7 @@ const ProductsListPopup: React.FC = ({ ingredients }) => setProducts( products?.map((product, i) => { if (i === index) { - product.amount_of_pack = Math.min(Math.max(newAmount, 1), 20); + product.need_to_buy = Math.min(Math.max(newAmount, 1), 20); } return product; }) @@ -70,27 +70,25 @@ const ProductsListPopup: React.FC = ({ ingredients }) => -

        {product.amount_of_pack}

        +

        {product.need_to_buy}

      - {product.price && product.amount_of_pack && ( + {product.final_price && product.need_to_buy && (

      {`${ - product.price * product.amount_of_pack + product.final_price * product.need_to_buy } руб.`}

      )} -

      {`${product.amount_of_pack} уп.`}

      +

      {product.amount_of_pack}

      -

      {`${product.quantity} шт`}

      +

      {product.quantity}

      diff --git a/src/pages/shopping-cart/index.tsx b/src/pages/shopping-cart/index.tsx index e73cfa1c..764117c5 100644 --- a/src/pages/shopping-cart/index.tsx +++ b/src/pages/shopping-cart/index.tsx @@ -118,7 +118,10 @@ const ShoppingCart: React.FC = () => {

      - + )} From 36286bfa5cfb32e6c6769f897f4949912912a779 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Sat, 23 Dec 2023 17:39:10 +0300 Subject: [PATCH 031/141] fix: interface for MakingOrderBtn props --- src/components/Button/index.tsx | 2 +- src/components/making-order-btn/index.tsx | 10 ++++------ src/pages/shopping-cart/index.tsx | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 1f3791ec..f9aee6c7 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import styles from './button.module.scss'; -type ButtonProps = { +export type ButtonProps = { buttonText: string; buttonStyle: string; classNameActive?: string; diff --git a/src/components/making-order-btn/index.tsx b/src/components/making-order-btn/index.tsx index 311d8411..8c6c7922 100644 --- a/src/components/making-order-btn/index.tsx +++ b/src/components/making-order-btn/index.tsx @@ -1,20 +1,18 @@ import React from 'react'; import styles from './making-order-btn.module.scss'; import Button from '@components/Button'; +import type { ButtonProps } from '@components/Button'; -interface MakingOrderBtnProps { - isDisabled: boolean; - onClick?: () => void; -} +interface MakingOrderBtnProps extends Pick {} -const MakingOrderBtn: React.FC = ({ onClick, isDisabled }) => { +const MakingOrderBtn: React.FC = ({ onClick, disabled }) => { return (
      From 3a97cf4c601864f635248e40ce63b084ddcbc4f5 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Sat, 23 Dec 2023 17:51:18 +0300 Subject: [PATCH 032/141] fix: add all className variants to type ButtonProps --- src/components/Button/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index f9aee6c7..da9e54e8 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -3,8 +3,12 @@ import styles from './button.module.scss'; export type ButtonProps = { buttonText: string; - buttonStyle: string; - classNameActive?: string; + buttonStyle: + | 'green-border-button' + | 'green-border-button__active' + | 'greenish-button' + | 'green-button'; + classNameActive?: 'greenish-button__active' | ''; onClick?: () => void; disabled?: boolean; classNames?: string; From 8605221d567a354d48383df45f3652ae28ab0097 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Tue, 26 Dec 2023 18:02:55 +0300 Subject: [PATCH 033/141] fix: change recipe fields according to new responce from server --- src/components/popups/popup-recipe/index.tsx | 12 +++--- .../ingredients-list-popup/index.tsx | 14 +++---- .../ingredients-list/index.tsx | 14 +++---- .../products-list-popup/index.tsx | 26 ++++++------ src/pages/recipe/index.tsx | 40 ++++--------------- 5 files changed, 39 insertions(+), 67 deletions(-) diff --git a/src/components/popups/popup-recipe/index.tsx b/src/components/popups/popup-recipe/index.tsx index 7226ac8c..11e8d8ea 100644 --- a/src/components/popups/popup-recipe/index.tsx +++ b/src/components/popups/popup-recipe/index.tsx @@ -7,14 +7,14 @@ import ProductsListPopup from '@components/recipes-components/products-list-popu type RecipeIngredientsProps = { ingredients: { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }[]; }; diff --git a/src/components/recipes-components/ingredients-list-popup/index.tsx b/src/components/recipes-components/ingredients-list-popup/index.tsx index 4443fb8b..cbdbbfc6 100644 --- a/src/components/recipes-components/ingredients-list-popup/index.tsx +++ b/src/components/recipes-components/ingredients-list-popup/index.tsx @@ -3,14 +3,14 @@ import styles from './ingredients-list-popup.module.scss'; type RecipeIngredientsProps = { ingredients: { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }[]; }; @@ -27,7 +27,7 @@ const IngredientsListPopup: React.FC = ({ ingredients }) {`${ingredient?.quantity} ${ingredient?.measure_unit}`} + >{`${ingredient?.quantity_in_recipe} ${ingredient?.measure_unit}`}
    • ); })} diff --git a/src/components/recipes-components/ingredients-list/index.tsx b/src/components/recipes-components/ingredients-list/index.tsx index 10cc02c7..ca8f416e 100644 --- a/src/components/recipes-components/ingredients-list/index.tsx +++ b/src/components/recipes-components/ingredients-list/index.tsx @@ -5,14 +5,14 @@ import clsx from 'clsx'; type RecipeIngredientsProps = { ingredients: { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }[]; }; @@ -51,7 +51,7 @@ const IngredientsList: React.FC = ({ ingredients }) => { >{`${ingredient.name}, ${ingredient.amount}${ingredient.measure_unit}`}

      {`${ingredient?.quantity} ${ingredient.measure_unit}`}

      + >{`${ingredient?.quantity_in_recipe} ${ingredient.measure_unit}`}

      ); })} diff --git a/src/components/recipes-components/products-list-popup/index.tsx b/src/components/recipes-components/products-list-popup/index.tsx index 4b5fcc06..71d7e02a 100644 --- a/src/components/recipes-components/products-list-popup/index.tsx +++ b/src/components/recipes-components/products-list-popup/index.tsx @@ -7,14 +7,14 @@ import plusIcon from '@images/plus_button.svg'; import minusIcon from '@images/minus_button.svg'; type ReceipeIngredient = { + amount: number; + final_price: number; id: number; - name: string; - measure_unit: string; - quantity: number; ingredient_photo: string; - amount_of_pack: number; - amount?: number; - price?: number; + measure_unit: string; + name: string; + need_to_buy: number; + quantity_in_recipe: number; }; type RecipeIngredientsProps = { @@ -36,7 +36,7 @@ const ProductsListPopup: React.FC = ({ ingredients }) => setProducts( products?.map((product, i) => { if (i === index) { - product.amount_of_pack = Math.min(Math.max(newAmount, 1), 20); + product.need_to_buy = Math.min(Math.max(newAmount, 1), 20); } return product; }) @@ -80,27 +80,25 @@ const ProductsListPopup: React.FC = ({ ingredients }) => -

      {product.amount_of_pack}

      +

      {product.need_to_buy}

      - {product.price && product.amount_of_pack && ( + {product.final_price && product.need_to_buy && (

      {`${ - product.price * product.amount_of_pack + product.final_price * product.need_to_buy } руб.`}

      )} + {cartData.products.length > 0 ? ( @@ -79,7 +89,7 @@ const ShoppingCart: React.FC = () => {

      Итого

      - {cartData.total_price ? `${cartData.total_price.toFixed(2)} руб.` : 'N/A'} + {cartData.total_price ? `${cartData.total_price.toFixed(2)} руб.` : '0'}

      diff --git a/src/pages/shopping-cart/shopping-cart.module.scss b/src/pages/shopping-cart/shopping-cart.module.scss index 931064c2..cf55b813 100644 --- a/src/pages/shopping-cart/shopping-cart.module.scss +++ b/src/pages/shopping-cart/shopping-cart.module.scss @@ -259,25 +259,24 @@ /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */ font-family: Ubuntu; font-size: 18px; - color: #285718; - font-style: normal; + color: $green-primary-700; font-weight: 400; line-height: 140%; cursor: pointer; - :hover { - opacity: 0.5; - transition: opacity 0.5s ease-in-out; + &:hover { + color: $accent-color-bright-green; + transition: color 0.2s ease-in-out; } - .products__btn_unactive { - color: #e5e5e5; + &:disabled { + color: $gray-button; cursor: default; } @media screen and (width <= 768px) { font-size: 13px; - color: #285718; + color: $green-primary-700; } } From cac1242018bcaeab5be98476572856aa463e6948 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Tue, 26 Dec 2023 19:51:39 +0300 Subject: [PATCH 039/141] fix: after review --- src/contexts/cart-context.tsx | 34 +++++++++++++--------------------- src/services/api.ts | 17 ----------------- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/src/contexts/cart-context.tsx b/src/contexts/cart-context.tsx index 451218fe..78b06bfa 100644 --- a/src/contexts/cart-context.tsx +++ b/src/contexts/cart-context.tsx @@ -31,22 +31,19 @@ type CartDataItem = { total_price: number; }; +type ResponseText = { + loadCartData: string; + updateCart: string; + deleteCart: string; + clearCart: string; +}; + type CartContextType = { cartData: CartDataItem; loading: boolean; cartUpdating: boolean; - error: { - loadCartData: string; - updateCart: string; - deleteCart: string; - clearCart: string; - }; - successText: { - loadCartData: string; - updateCart: string; - deleteCart: string; - clearCart: string; - }; + error: ResponseText; + successText: ResponseText; reset: () => void; loadCartData: () => void; updateCart: (data: Product[]) => void; @@ -116,7 +113,6 @@ export const CartProvider: React.FC = ({ children }) => { setCartData(data); }) .catch((error) => { - console.error('Error loading cart data:', error); setError((prev) => { return { ...prev, loadCartData: error.errors[0].detail }; }); @@ -143,7 +139,6 @@ export const CartProvider: React.FC = ({ children }) => { }); }) .catch((error) => { - console.error('Error updating cart:', error); setError((prev) => { return { ...prev, updateCart: error.errors[0].detail }; }); @@ -157,7 +152,6 @@ export const CartProvider: React.FC = ({ children }) => { api .usersShoppingCartDelete(idProduct) .catch((error) => { - console.error('Error deleting cart item:', error); if (error?.errors) { setError((prev) => { return { ...prev, deleteCart: error.errors[0]?.detail }; @@ -172,23 +166,21 @@ export const CartProvider: React.FC = ({ children }) => { const clearCart = () => { api .usersShoppingCartDeleteAll() - .then((data) => { + .then(({ message }) => { loadCartData(); setSuccessText((prev) => { - return { ...prev, clearCart: data.message }; + return { ...prev, clearCart: message }; }); }) - .catch((error) => { + .catch(({ errors }) => { setError((prev) => { - return { ...prev, clearCart: error.errors }; + return { ...prev, clearCart: errors }; }); - console.log('Error clearing cart:', error); }); }; const addItemToCart = (productId: number) => { if (!cartUpdating) { - console.log(cartUpdating); const existingProductIndex = cartData.products.findIndex( (product) => product.id === productId ); diff --git a/src/services/api.ts b/src/services/api.ts index 618dd448..84c995bb 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -312,23 +312,6 @@ class Api { }); } - // usersShoppingCartRead(userId: string, id: number) { - // return this._request(`users/${userId}/shopping_cart/${id}/`, { - // method: 'GET', - // }); - // } - - // usersShoppingCartPartialUpdate( - // userId: string, - // id: number, - // data: ShoppingCartPostUpdateDelete - // ) { - // return this._request(`users/${userId}/shopping_cart/${id}/`, { - // method: 'PATCH', - // body: JSON.stringify(data), - // }); - // } - /* ----------------------------- Products ----------------------------- */ productsList(slug: string) { const headers: HeadersInit = { From eaabcd6401a91dc8c968ec7f9625723da61ab6b7 Mon Sep 17 00:00:00 2001 From: kavabunga Date: Wed, 27 Dec 2023 01:43:59 +0200 Subject: [PATCH 040/141] feat: add checkout success screen layout --- package.json | 2 +- src/App.tsx | 2 + .../checkout-success.module.scss | 81 +++++++++++++++++++ src/pages/checkout/checkout-success/index.tsx | 44 ++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/pages/checkout/checkout-success/checkout-success.module.scss create mode 100644 src/pages/checkout/checkout-success/index.tsx diff --git a/package.json b/package.json index add4f68f..776605f6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "tsc && vite build", "lint:js": "eslint . --ext ts,tsx,js,jsx --report-unused-disable-directives --cache --max-warnings 0", "lint:js:fix": "npm run lint:js -- --fix", diff --git a/src/App.tsx b/src/App.tsx index 63ed8053..b9ec8688 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,6 +22,7 @@ import NotFound from '@pages/not_found/not-found.tsx'; import { CartProvider } from '@contexts/cart-context.tsx'; import RecipeList from '@pages/recipe-list/index.tsx'; import Agreement from '@pages/agreement/index.tsx'; +import CheckoutSuccess from '@pages/checkout/checkout-success/index.tsx'; // импорт временных массивов для отображения каталогов и продуктов // временное решение для верстки, потом удалить @@ -46,6 +47,7 @@ function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/pages/checkout/checkout-success/checkout-success.module.scss b/src/pages/checkout/checkout-success/checkout-success.module.scss new file mode 100644 index 00000000..0c9a75ab --- /dev/null +++ b/src/pages/checkout/checkout-success/checkout-success.module.scss @@ -0,0 +1,81 @@ +@use '@scss/mixins' as *; +@use '@scss/variables' as *; + +.checkoutSuccess { + display: flex; + flex-direction: column; + font-family: $ubuntu-font; +} + +.checkoutSuccess__order { + padding: 108px 128px 100px; + max-width: 1024px; + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: center; + + @media screen and (width <= 768px) { + padding: 40px 20px; + } +} + +.checkoutSuccess__title { + align-self: center; + padding: 0 0 38px; + + @media screen and (width <= 768px) { + align-self: stretch; + text-align: left; + padding: 0 0 20px; + font-size: 24px; + } +} + +.checkoutSuccess__paragraph { + padding: 0 0 74px; + margin: 0; + align-self: center; + text-align: center; + font-size: 22px; + font-weight: 400; + line-height: 140%; + + @media screen and (width <= 768px) { + padding: 0 0 32px; + align-self: stretch; + text-align: left; + font-size: 14px; + } +} + +.checkoutSuccess__advice { + padding: 0 0 44px; + margin: 0; + font-size: 30px; + font-weight: 700; + line-height: 140%; + + @media screen and (width <= 768px) { + padding: 0 0 16px; + font-size: 18px; + } +} + +.checkoutSuccess__link { + color: var(--hint, #2180b6); + text-decoration: none; +} + +.checkoutSuccess__ourBlock { + padding: 100px 128px 0; + max-width: 1024px; + + @media screen and (width <= 768px) { + padding: 40px 20px 0; + } + + @media screen and (width <= 550px) { + padding-right: 0; + } +} diff --git a/src/pages/checkout/checkout-success/index.tsx b/src/pages/checkout/checkout-success/index.tsx new file mode 100644 index 00000000..60d125f4 --- /dev/null +++ b/src/pages/checkout/checkout-success/index.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { useLocation } from 'react-router'; +import styles from './checkout-success.module.scss'; +import OurBlock from '@components/our-block'; +import { Link } from 'react-router-dom'; + +const CheckoutSuccess: React.FC = () => { + // TODO: Add check for active order and redirect + const [order, setOrder] = React.useState(''); + const location = useLocation(); + + React.useEffect( + () => location.state?.order && setOrder(location.state.order), + [location] + ); + + return ( +
      +
      +

      + Заказ №{order} успешно оформлен! +

      +

      + Мы уже приступили к его сборке.
      + За статусом заказа можно следить в{' '} + + {' '} + личном кабинете + + . +

      +
      {/* // TODO: Component "Order Tracker" */}
      +
      +
      +

      + Пока вы ждёте заказ, можете ознакомиться с рецептами из нашего блога +

      + +
      +
      + ); +}; + +export default CheckoutSuccess; From 99836702f601e5dbf3ef5bb8b7daff8bc469090e Mon Sep 17 00:00:00 2001 From: kavabunga Date: Wed, 27 Dec 2023 01:48:00 +0200 Subject: [PATCH 041/141] ci: fix husky permissions --- .husky/pre-commit | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 From f7468d8d34e579d686b69ec380c1f2a7e6ec5715 Mon Sep 17 00:00:00 2001 From: kavabunga Date: Wed, 27 Dec 2023 02:48:22 +0200 Subject: [PATCH 042/141] feat: add status tracker component --- src/assets/images/car-alt-min.svg | 1 + src/assets/images/cart-add-min.svg | 1 + src/assets/images/circle-ok-min.svg | 1 + src/assets/images/home-alt-min.svg | 1 + src/assets/images/spacer-min.svg | 1 + src/assets/images/vspacer-min.svg | 1 + src/components/order-status-tracker/index.tsx | 50 +++++++++++++ .../order-status-tracker.module.scss | 72 +++++++++++++++++++ .../checkout-success.module.scss | 6 +- src/pages/checkout/checkout-success/index.tsx | 4 +- 10 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 src/assets/images/car-alt-min.svg create mode 100644 src/assets/images/cart-add-min.svg create mode 100644 src/assets/images/circle-ok-min.svg create mode 100644 src/assets/images/home-alt-min.svg create mode 100644 src/assets/images/spacer-min.svg create mode 100644 src/assets/images/vspacer-min.svg create mode 100644 src/components/order-status-tracker/index.tsx create mode 100644 src/components/order-status-tracker/order-status-tracker.module.scss diff --git a/src/assets/images/car-alt-min.svg b/src/assets/images/car-alt-min.svg new file mode 100644 index 00000000..207da658 --- /dev/null +++ b/src/assets/images/car-alt-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/cart-add-min.svg b/src/assets/images/cart-add-min.svg new file mode 100644 index 00000000..0964f5bd --- /dev/null +++ b/src/assets/images/cart-add-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/circle-ok-min.svg b/src/assets/images/circle-ok-min.svg new file mode 100644 index 00000000..8e83b22d --- /dev/null +++ b/src/assets/images/circle-ok-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/home-alt-min.svg b/src/assets/images/home-alt-min.svg new file mode 100644 index 00000000..fb437323 --- /dev/null +++ b/src/assets/images/home-alt-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/spacer-min.svg b/src/assets/images/spacer-min.svg new file mode 100644 index 00000000..aedb17de --- /dev/null +++ b/src/assets/images/spacer-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/vspacer-min.svg b/src/assets/images/vspacer-min.svg new file mode 100644 index 00000000..a0001db8 --- /dev/null +++ b/src/assets/images/vspacer-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/order-status-tracker/index.tsx b/src/components/order-status-tracker/index.tsx new file mode 100644 index 00000000..c474673c --- /dev/null +++ b/src/components/order-status-tracker/index.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import styles from './order-status-tracker.module.scss'; +import donePath from '@images/circle-ok-min.svg'; +import packagingPath from '@images/cart-add-min.svg'; +import deliveringPath from '@images/car-alt-min.svg'; +import deliveredPath from '@images/home-alt-min.svg'; + +const OrderStatusTracker: React.FC = () => { + return ( +
        +
      • + Заказ принят +

        Заказ принят

        +
      • +
      • +
      • + Сборка заказа +

        Сборка заказа

        +
      • +
      • +
      • + Передан в доставку +

        Передан в доставку

        +
      • +
      • +
      • + Успешно доставлено +

        Успешно доставлено

        +
      • +
      + ); +}; + +export default OrderStatusTracker; diff --git a/src/components/order-status-tracker/order-status-tracker.module.scss b/src/components/order-status-tracker/order-status-tracker.module.scss new file mode 100644 index 00000000..c200b0e2 --- /dev/null +++ b/src/components/order-status-tracker/order-status-tracker.module.scss @@ -0,0 +1,72 @@ +@use '@scss/mixins' as *; +@use '@scss/variables' as *; + +.orderStatusTracker { + padding: 0; + margin: 0; + display: flex; + flex-direction: row; + font-family: $ubuntu-font; + gap: 16px; + font-size: 20px; + font-weight: 400; + line-height: 140%; + + @media screen and (width <= 1024px) { + font-size: 14px; + } + + @media screen and (width <= 768px) { + font-size: 14px; + flex-direction: column; + gap: 8px; + } +} + +.orderStatusTracker__item { + max-width: 120px; + display: flex; + flex-direction: column; + gap: 12px; + align-items: center; + text-align: center; + + @media screen and (width <= 1024px) { + max-width: 80px; + } + + @media screen and (width <= 768px) { + flex-direction: row; + gap: 16px; + max-width: 100%; + } +} + +.orderStatusTracker__icon { + width: 80px; + aspect-ratio: 1; + + @media screen and (width <= 1024px) { + width: 60px; + } + + @media screen and (width <= 768px) { + width: 40px; + } +} + +.orderStatusTracker__status { + padding: 0; + margin: 0; +} + +.orderStatusTracker__spacer { + background: no-repeat center url('/src/assets/images/spacer-min.svg'); + width: 64px; + + @media screen and (width <= 768px) { + background: no-repeat center url('/src/assets/images/vspacer-min.svg'); + width: 40px; + height: 20px; + } +} diff --git a/src/pages/checkout/checkout-success/checkout-success.module.scss b/src/pages/checkout/checkout-success/checkout-success.module.scss index 0c9a75ab..08bf80da 100644 --- a/src/pages/checkout/checkout-success/checkout-success.module.scss +++ b/src/pages/checkout/checkout-success/checkout-success.module.scss @@ -14,18 +14,18 @@ display: flex; flex-direction: column; justify-content: center; + align-items: center; @media screen and (width <= 768px) { + align-items: stretch; padding: 40px 20px; } } .checkoutSuccess__title { - align-self: center; padding: 0 0 38px; @media screen and (width <= 768px) { - align-self: stretch; text-align: left; padding: 0 0 20px; font-size: 24px; @@ -35,7 +35,6 @@ .checkoutSuccess__paragraph { padding: 0 0 74px; margin: 0; - align-self: center; text-align: center; font-size: 22px; font-weight: 400; @@ -43,7 +42,6 @@ @media screen and (width <= 768px) { padding: 0 0 32px; - align-self: stretch; text-align: left; font-size: 14px; } diff --git a/src/pages/checkout/checkout-success/index.tsx b/src/pages/checkout/checkout-success/index.tsx index 60d125f4..e9a54208 100644 --- a/src/pages/checkout/checkout-success/index.tsx +++ b/src/pages/checkout/checkout-success/index.tsx @@ -3,6 +3,7 @@ import { useLocation } from 'react-router'; import styles from './checkout-success.module.scss'; import OurBlock from '@components/our-block'; import { Link } from 'react-router-dom'; +import OrderStatusTracker from '@components/order-status-tracker'; const CheckoutSuccess: React.FC = () => { // TODO: Add check for active order and redirect @@ -24,12 +25,11 @@ const CheckoutSuccess: React.FC = () => { Мы уже приступили к его сборке.
      За статусом заказа можно следить в{' '} - {' '} личном кабинете .

      -
      {/* // TODO: Component "Order Tracker" */}
      +

      From b0e2213ccae85d5ac6d5a9d00c8d4cc61129cd19 Mon Sep 17 00:00:00 2001 From: kavabunga Date: Wed, 27 Dec 2023 03:06:14 +0200 Subject: [PATCH 043/141] feat: add redirect after checkout, protect success screen closes #181 --- src/pages/checkout/checkout-success/index.tsx | 11 +++++++---- src/pages/checkout/index.tsx | 5 ++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/pages/checkout/checkout-success/index.tsx b/src/pages/checkout/checkout-success/index.tsx index e9a54208..1c3e941f 100644 --- a/src/pages/checkout/checkout-success/index.tsx +++ b/src/pages/checkout/checkout-success/index.tsx @@ -1,18 +1,21 @@ import React from 'react'; -import { useLocation } from 'react-router'; +import { useLocation, useNavigate } from 'react-router'; import styles from './checkout-success.module.scss'; import OurBlock from '@components/our-block'; import { Link } from 'react-router-dom'; import OrderStatusTracker from '@components/order-status-tracker'; const CheckoutSuccess: React.FC = () => { - // TODO: Add check for active order and redirect const [order, setOrder] = React.useState(''); const location = useLocation(); + const navigate = useNavigate(); React.useEffect( - () => location.state?.order && setOrder(location.state.order), - [location] + () => + location.state?.order + ? setOrder(location.state.order) + : navigate('/', { replace: true }), + [location, navigate] ); return ( diff --git a/src/pages/checkout/index.tsx b/src/pages/checkout/index.tsx index e6afff76..7c6ba90d 100644 --- a/src/pages/checkout/index.tsx +++ b/src/pages/checkout/index.tsx @@ -84,10 +84,9 @@ const Checkout: React.FC = () => { api .usersOrderCreate(formData) - .then(() => { - navigate('/cart'); + .then((res) => { + navigate('/cart/success', { state: { order: res.order_number } }); loadCartData(); - alert('заказ оформлен'); }) .catch((error) => { if (error.response && error.response.data && error.response.data.errors) { From 8c1c10e148acb3663e8324eef2434ad005bb6575 Mon Sep 17 00:00:00 2001 From: kavabunga Date: Wed, 27 Dec 2023 15:15:29 +0200 Subject: [PATCH 044/141] refactor: polish after review --- src/components/order-status-tracker/index.tsx | 70 ++++++++----------- src/pages/checkout/checkout-success/index.tsx | 8 ++- src/pages/checkout/index.tsx | 1 + 3 files changed, 34 insertions(+), 45 deletions(-) diff --git a/src/components/order-status-tracker/index.tsx b/src/components/order-status-tracker/index.tsx index c474673c..fedc4bba 100644 --- a/src/components/order-status-tracker/index.tsx +++ b/src/components/order-status-tracker/index.tsx @@ -1,50 +1,36 @@ import React from 'react'; -import styles from './order-status-tracker.module.scss'; + import donePath from '@images/circle-ok-min.svg'; import packagingPath from '@images/cart-add-min.svg'; import deliveringPath from '@images/car-alt-min.svg'; import deliveredPath from '@images/home-alt-min.svg'; -const OrderStatusTracker: React.FC = () => { - return ( -

        -
      • - Заказ принят -

        Заказ принят

        -
      • -
      • -
      • - Сборка заказа -

        Сборка заказа

        -
      • -
      • -
      • - Передан в доставку -

        Передан в доставку

        -
      • -
      • -
      • - Успешно доставлено -

        Успешно доставлено

        -
      • -
      - ); -}; +import styles from './order-status-tracker.module.scss'; + +const orderSteps = [ + { icon: donePath, text: 'Заказ принят' }, + { icon: packagingPath, text: 'Сборка заказа' }, + { icon: deliveringPath, text: 'Передан в доставку' }, + { icon: deliveredPath, text: 'Успешно доставлено' }, +]; +const OrderStatusTracker: React.FC = () => ( +
        + {orderSteps.map((step, index) => ( + +
      • + {step.text} +

        {step.text}

        +
      • + {index < orderSteps.length - 1 && ( +
      • + )} + + ))} +
      +); export default OrderStatusTracker; diff --git a/src/pages/checkout/checkout-success/index.tsx b/src/pages/checkout/checkout-success/index.tsx index 1c3e941f..42b9502b 100644 --- a/src/pages/checkout/checkout-success/index.tsx +++ b/src/pages/checkout/checkout-success/index.tsx @@ -1,12 +1,14 @@ import React from 'react'; import { useLocation, useNavigate } from 'react-router'; -import styles from './checkout-success.module.scss'; -import OurBlock from '@components/our-block'; import { Link } from 'react-router-dom'; + +import OurBlock from '@components/our-block'; import OrderStatusTracker from '@components/order-status-tracker'; +import styles from './checkout-success.module.scss'; + const CheckoutSuccess: React.FC = () => { - const [order, setOrder] = React.useState(''); + const [order, setOrder] = React.useState(''); const location = useLocation(); const navigate = useNavigate(); diff --git a/src/pages/checkout/index.tsx b/src/pages/checkout/index.tsx index 7c6ba90d..f536d6cb 100644 --- a/src/pages/checkout/index.tsx +++ b/src/pages/checkout/index.tsx @@ -89,6 +89,7 @@ const Checkout: React.FC = () => { loadCartData(); }) .catch((error) => { + console.log(error); if (error.response && error.response.data && error.response.data.errors) { const errorMessage = error.response.data.errors; alert('Ошибка при создании заказа: ' + errorMessage); From d7d35724ebef097e5a459c8d7ccad07282597b88 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Wed, 27 Dec 2023 19:12:06 +0300 Subject: [PATCH 045/141] refactor: improve function readability --- src/pages/profile/profile-favorites/index.tsx | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/pages/profile/profile-favorites/index.tsx b/src/pages/profile/profile-favorites/index.tsx index ff6129d8..8287deb1 100644 --- a/src/pages/profile/profile-favorites/index.tsx +++ b/src/pages/profile/profile-favorites/index.tsx @@ -65,19 +65,9 @@ export default function ProfileFavorites() { Object.values(checkboxesValues).every((el) => el) || !products.length; const toggleAll = () => { - if (isChooseAll) { - Object.keys(checkboxesValues).forEach((key) => { - setCheckboxesValue((prev) => { - return { ...prev, [key]: false }; - }); - }); - } else { - Object.keys(checkboxesValues).forEach((key) => { - setCheckboxesValue((prev) => { - return { ...prev, [key]: true }; - }); - }); - } + Object.keys(checkboxesValues).forEach((key) => + setCheckboxesValue((prev) => ({ ...prev, [key]: isChooseAll ? false : true })) + ); }; const handleAddToCart = () => { From 53a8543d43a9ed0fffbbae528880b77571bf8f73 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 14:41:35 +0300 Subject: [PATCH 046/141] fix: change field name according to changed name on backend --- src/pages/catalog/index.tsx | 4 ++-- src/services/generated-api/data-contracts.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/catalog/index.tsx b/src/pages/catalog/index.tsx index bdfcd7a0..b09c52eb 100644 --- a/src/pages/catalog/index.tsx +++ b/src/pages/catalog/index.tsx @@ -11,7 +11,7 @@ type Catalog = { id: number; name: string; slug: string; - top_three_products: Product[]; + top_products: Product[]; }; const Catalog: React.FC = () => { @@ -53,7 +53,7 @@ const Catalog: React.FC = () => { title={catalog.name} category={catalog.slug} type="single-row" - array={catalog.top_three_products} + array={catalog.top_products.slice(0, 3)} /> ))}
      diff --git a/src/services/generated-api/data-contracts.ts b/src/services/generated-api/data-contracts.ts index dc6e447d..f2aaae26 100644 --- a/src/services/generated-api/data-contracts.ts +++ b/src/services/generated-api/data-contracts.ts @@ -39,8 +39,8 @@ export interface Category { */ slug?: string; subcategories?: SubcategoryLight[]; - /** Top three products */ - top_three_products?: string; + /** Top products */ + top_products: string; } export interface CategoryCreate { From cd906caae901bc90c2314cc413543c7f990816e2 Mon Sep 17 00:00:00 2001 From: kavabunga Date: Thu, 28 Dec 2023 16:12:25 +0200 Subject: [PATCH 047/141] fix: fix key prop in order-status-tracker list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Поправил key в списке статусов в компоненте Order Status Tracker. Сейчас браузер не ругается на отсутствие key --- src/components/order-status-tracker/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/order-status-tracker/index.tsx b/src/components/order-status-tracker/index.tsx index fedc4bba..b84ddb03 100644 --- a/src/components/order-status-tracker/index.tsx +++ b/src/components/order-status-tracker/index.tsx @@ -17,8 +17,8 @@ const orderSteps = [ const OrderStatusTracker: React.FC = () => (
        {orderSteps.map((step, index) => ( - -
      • + +
      • {step.text} Date: Fri, 29 Dec 2023 11:14:25 +0300 Subject: [PATCH 048/141] fix: change field name according to changed name on backend --- src/components/card-catalog-link/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/card-catalog-link/index.tsx b/src/components/card-catalog-link/index.tsx index df5e4dbb..664a0482 100644 --- a/src/components/card-catalog-link/index.tsx +++ b/src/components/card-catalog-link/index.tsx @@ -36,7 +36,7 @@ function CardCatalogLink({ title, array, type, category }: CardCatalogLinkProps) {type === 'single-row' && ( Date: Fri, 29 Dec 2023 11:48:23 +0300 Subject: [PATCH 049/141] fix: css style causing error in firefox --- src/components/catalog-promo/catalog-promo.module.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/catalog-promo/catalog-promo.module.scss b/src/components/catalog-promo/catalog-promo.module.scss index a4b0c873..47464cb6 100644 --- a/src/components/catalog-promo/catalog-promo.module.scss +++ b/src/components/catalog-promo/catalog-promo.module.scss @@ -52,11 +52,8 @@ margin: 0; } -.catalogPromo__link { - position: relative; -} - .catalogPromo__innerContainer { + position: relative; width: 100%; height: 100%; margin: 0; From adc23912df5b88b267316d3fb39346058296db3e Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Fri, 29 Dec 2023 12:04:12 +0300 Subject: [PATCH 050/141] fix: change css property to one that is work in firefox --- src/components/navigation-icons/navigation-icons.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/navigation-icons/navigation-icons.module.scss b/src/components/navigation-icons/navigation-icons.module.scss index 567dfddf..ba96cfab 100644 --- a/src/components/navigation-icons/navigation-icons.module.scss +++ b/src/components/navigation-icons/navigation-icons.module.scss @@ -41,7 +41,7 @@ .navigation__icon_busket::after { content: attr(data-content); position: absolute; - text-wrap: nowrap; + white-space: nowrap; left: 50%; top: 27px; transform: translate(-50%); From 82347f54a5ce7982bc4fe4120f5d8b07412b7a7c Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 14:28:43 +0300 Subject: [PATCH 051/141] feat: add PopupCheckoutResponse component --- .../popups/popup-checkout-response/index.tsx | 24 +++++++++++++++++ .../popup-checkout-response.module.scss | 26 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/components/popups/popup-checkout-response/index.tsx create mode 100644 src/components/popups/popup-checkout-response/popup-checkout-response.module.scss diff --git a/src/components/popups/popup-checkout-response/index.tsx b/src/components/popups/popup-checkout-response/index.tsx new file mode 100644 index 00000000..333ee930 --- /dev/null +++ b/src/components/popups/popup-checkout-response/index.tsx @@ -0,0 +1,24 @@ +import { usePopup } from '@hooks/use-popup'; +import styles from './popup-checkout-response.module.scss'; +import Popup from '@components/popup'; + +type PopupCheckoutResponseProps = { + text: string; + handleClose: () => void; +}; + +const PopupCheckoutResponse: React.FC = ({ + text, + handleClose, +}) => { + const { popupState } = usePopup(); + return ( + +
        +

        {text}

        +
        +
        + ); +}; + +export default PopupCheckoutResponse; diff --git a/src/components/popups/popup-checkout-response/popup-checkout-response.module.scss b/src/components/popups/popup-checkout-response/popup-checkout-response.module.scss new file mode 100644 index 00000000..7e73327f --- /dev/null +++ b/src/components/popups/popup-checkout-response/popup-checkout-response.module.scss @@ -0,0 +1,26 @@ +@use '@scss/variables' as *; + +.textContainer { + display: flex; + justify-content: center; + align-items: center; + max-width: 500px; + min-height: 200px; + padding: 50px; + font-size: 30px; +} + +.text { + margin: 0; + text-align: center; + color: $active-text-color; + font-family: $ubuntu-font; + font-size: 20px; + font-weight: 400; + line-height: 140%; + + @media screen and (width <= 768px) { + font-size: 14px; + font-weight: 500; + } +} From 53f9d68cf1b98aaa917cdfd80548da223d4b58bb Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 19:10:59 +0300 Subject: [PATCH 052/141] feat: change link to orders in profile --- src/pages/checkout/checkout-success/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/checkout/checkout-success/index.tsx b/src/pages/checkout/checkout-success/index.tsx index 42b9502b..9124b751 100644 --- a/src/pages/checkout/checkout-success/index.tsx +++ b/src/pages/checkout/checkout-success/index.tsx @@ -29,7 +29,7 @@ const CheckoutSuccess: React.FC = () => {

        Мы уже приступили к его сборке.
        За статусом заказа можно следить в{' '} - + личном кабинете . From 48c000702a3d2e940a228cd28d6f73a181a2df9a Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 19:13:23 +0300 Subject: [PATCH 053/141] feat: add PopupCheckoutResponse to context --- src/contexts/popup-context.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/contexts/popup-context.tsx b/src/contexts/popup-context.tsx index a57bf205..c3eed1c6 100644 --- a/src/contexts/popup-context.tsx +++ b/src/contexts/popup-context.tsx @@ -8,6 +8,7 @@ type PopupState = { openPopupAddressesWarning: boolean; openPopupAddressesDeleteConfirm: boolean; openPopupRecipe: boolean; + openPopupCheckoutResponse: boolean; }; type PopupContextType = { @@ -27,6 +28,7 @@ export const PopupProvider: React.FC<{ children: ReactNode }> = ({ children }) = openPopupAddressesWarning: false, openPopupAddressesDeleteConfirm: false, openPopupRecipe: false, + openPopupCheckoutResponse: false, }); const handleOpenPopup = (popupName: string) => { From 577bb19831124f4ee54dc28df2ce3093c24444c6 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 19:15:43 +0300 Subject: [PATCH 054/141] fix: mark userData as optional field --- src/services/generated-api/data-contracts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/generated-api/data-contracts.ts b/src/services/generated-api/data-contracts.ts index f2aaae26..6f7dd8fb 100644 --- a/src/services/generated-api/data-contracts.ts +++ b/src/services/generated-api/data-contracts.ts @@ -1056,7 +1056,7 @@ export interface OrderPostDelete { } export interface OrderPostAdd extends OrderPostDelete { - user_data: { + user_data?: { first_name: string; last_name: string; phone_number: string; From deeadf4e10cf1bbf144c0208519c4fa4c5d5b41b Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 19:28:06 +0300 Subject: [PATCH 055/141] fix: delivery method passed from ShoppingCart --- src/pages/shopping-cart/index.tsx | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/pages/shopping-cart/index.tsx b/src/pages/shopping-cart/index.tsx index c8d5af0f..c644555d 100644 --- a/src/pages/shopping-cart/index.tsx +++ b/src/pages/shopping-cart/index.tsx @@ -12,7 +12,7 @@ import { Product } from '@services/generated-api/data-contracts'; const ShoppingCart: React.FC = () => { const { cartData, clearCart, loading } = useCart(); - const [activeButton, setActiveButton] = React.useState('shipment'); + const [deliveryMethod, setDeliveryMethod] = React.useState('By courier'); const navigate = useNavigate(); const [promotionProducts, setPromotionProducts] = useState([]); @@ -41,15 +41,13 @@ const ShoppingCart: React.FC = () => { }, []); const handleOrderTypeClick = (type: string) => { - setActiveButton(type); + setDeliveryMethod(type); }; const handleSubmitOrderClick = () => { - const typeToSend = activeButton; - navigate('/cart/order', { state: { - orderType: typeToSend, + orderType: deliveryMethod, }, }); }; @@ -95,10 +93,13 @@ const ShoppingCart: React.FC = () => {

        { - handleOrderTypeClick('shipment'); + handleOrderTypeClick('By courier'); }} >
        {
        { - handleOrderTypeClick('pickup'); + handleOrderTypeClick('Point of delivery'); }} >

        Самовывоз From d6967064ccaa01717be04de882407c41deff1864 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Thu, 28 Dec 2023 21:35:07 +0300 Subject: [PATCH 056/141] feat: add popup with errors on checkout --- src/pages/checkout/index.tsx | 173 +++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 67 deletions(-) diff --git a/src/pages/checkout/index.tsx b/src/pages/checkout/index.tsx index f536d6cb..61370070 100644 --- a/src/pages/checkout/index.tsx +++ b/src/pages/checkout/index.tsx @@ -1,9 +1,11 @@ -import React, { useEffect } from 'react'; +import React, { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import styles from './checkout.module.scss'; import api from '@services/api.ts'; import Input from '@ui/input'; import { useFormAndValidation } from '@hooks/use-form-and-validation.ts'; +import { usePopup } from '@hooks/use-popup'; +import PopupCheckoutResponse from '@components/popups/popup-checkout-response'; import { OrderPostAdd } from '@services/generated-api/data-contracts.ts'; import { useAuth } from '@hooks/use-auth.ts'; import { useCart } from '@hooks/use-cart-context.ts'; @@ -16,47 +18,78 @@ type Address = { const Checkout: React.FC = () => { const { isLoggedIn, user } = useAuth(); const { loadCartData, cartData } = useCart(); + const { handleOpenPopup, handleClosePopup } = usePopup(); const location = useLocation(); - const recievedType = location.state?.orderType; + const receivedType = location.state?.orderType; + const deliveryType = receivedType ? receivedType : 'Point of delivery'; const { values, handleChange, errors, isValid } = useFormAndValidation(); const navigate = useNavigate(); - - const [deliveryType, setDeliveryType] = React.useState('shipment'); const [selectedPayment, setSelectedPayment] = React.useState(''); const [selectedTime, setSelectedTime] = React.useState(''); const [selectedAddress, setSelectedAddress] = React.useState(''); - const [actualDeliveryType, setActualDeliveryType] = React.useState(''); const userAddresses = user?.addresses as unknown[] as Address[]; const [comment, setComment] = React.useState(''); + const [popupText, setPopupText] = useState(''); const addressesById = { - 1: 'Ленина, 23, кв. 19', - 2: 'Улица 2, дом 5', - 3: 'Другой адрес и т.д.', + 1: 'Санкт-Петербург Невский проспект 17', + 2: 'Санкт-Петербург Горохова улица 10', + 3: 'Санкт-Петербург Невский проспект 89', + 4: 'Санкт-Петербург Большой Самсониевский 6', + 5: 'Санкт-Петербург Лесной проспект 56', + 6: 'Санкт-Петербург Владимирский проспект 1', + 7: 'Санкт-Петербург Лиговский проспект 170', + }; + + const openInfoPopup = (text: string) => { + setPopupText(text); + handleOpenPopup('openPopupCheckoutResponse'); }; const handleSubmitOrder = () => { - if (isLoggedIn) { - if ( - !selectedPayment || - !actualDeliveryType || - (actualDeliveryType !== 'shipment' && !selectedAddress?.toString().trim()) - ) { - alert('Пожалуйста, заполните все обязательные поля'); + switch (true) { + case isLoggedIn && !user.first_name: + openInfoPopup('Пожалуйста, заполните имя в личном кабинете'); + return; + case isLoggedIn && !user.last_name: + openInfoPopup('Пожалуйста, заполните фамилию в личном кабинете'); + return; + case isLoggedIn && !user.phone_number: + openInfoPopup('Пожалуйста, заполните номер телефона в личном кабинете'); return; - } - } else { - if ( - !values.order_firstName?.toString().trim() || - !values.order_lastName?.toString().trim() || - !values.order_phoneNumber?.toString().trim() || - !values.order_email?.toString().trim() || - !selectedPayment || - !actualDeliveryType || - (actualDeliveryType !== 'shipment' && !selectedAddress?.toString().trim()) - ) { - alert('Пожалуйста, заполните все обязательные поля'); + case isLoggedIn && !selectedAddress?.toString().trim(): + openInfoPopup( + 'Пожалуйста, выберите адрес (добавить адрес можно в личном кабинете)' + ); + return; + case isLoggedIn && !selectedPayment: + openInfoPopup('Пожалуйста, выберите способ оплаты'); + return; + } + + switch (true) { + case !isLoggedIn && !values.order_firstName?.toString().trim(): + openInfoPopup('Пожалуйста, заполните имя'); + return; + case !isLoggedIn && !values.order_lastName?.toString().trim(): + openInfoPopup('Пожалуйста, заполните фамилию'); + return; + case !isLoggedIn && !values.order_phoneNumber?.toString().trim(): + openInfoPopup('Пожалуйста, заполните номер телефона'); + return; + case !isLoggedIn && !values.order_email?.toString().trim(): + openInfoPopup('Пожалуйста, заполните e-mail'); + return; + case !isLoggedIn && + deliveryType === 'Point of delivery' && + !selectedAddress?.toString().trim(): + openInfoPopup('Пожалуйста, выберите адрес'); + return; + case !isLoggedIn && !selectedAddress?.toString().trim(): + openInfoPopup('Пожалуйста, введите адрес'); + return; + case !isLoggedIn && !selectedPayment: + openInfoPopup('Пожалуйста, выберите способ оплаты'); return; - } } let formData: OrderPostAdd = { @@ -69,15 +102,18 @@ const Checkout: React.FC = () => { payment_method: selectedPayment as | 'Payment at the point of delivery' | 'In getting by cash', - delivery_method: actualDeliveryType as 'Point of delivery' | 'By courier', + delivery_method: deliveryType as 'Point of delivery' | 'By courier', delivery_point: - actualDeliveryType === 'shipment' ? Number(selectedAddress) || 2 : 2, + deliveryType === 'Point of delivery' ? Number(selectedAddress) : null, package: 0, comment: comment, add_address: selectedAddress || '', }; - if (isLoggedIn && actualDeliveryType === 'By courier') { + deliveryType === 'By courier' && delete formData.delivery_point; + isLoggedIn && delete formData.user_data; + + if (isLoggedIn && deliveryType === 'By courier') { delete formData.add_address; formData = { ...formData, address: parseInt(selectedAddress, 10) }; } @@ -89,12 +125,10 @@ const Checkout: React.FC = () => { loadCartData(); }) .catch((error) => { - console.log(error); - if (error.response && error.response.data && error.response.data.errors) { - const errorMessage = error.response.data.errors; - alert('Ошибка при создании заказа: ' + errorMessage); + if (error.errors[0].detail) { + openInfoPopup('Ошибка при создании заказа: ' + error.errors[0].detail); } else { - alert('Произошла ошибка при создании заказа.'); + openInfoPopup('Произошла ошибка при создании заказа.'); } }); }; @@ -116,21 +150,10 @@ const Checkout: React.FC = () => { setSelectedAddress(event.target.value); }; - React.useState(() => { - if (!recievedType) { - setDeliveryType('shipment'); - } else { - setDeliveryType(recievedType); - } - }); - - useEffect(() => { - if (deliveryType === 'shipment') { - setActualDeliveryType('By courier'); - } else { - setActualDeliveryType('Point of delivery'); - } - }, [deliveryType]); + const handleClose = () => { + handleClosePopup('openPopupCheckoutResponse'); + setPopupText(''); + }; return (

        @@ -220,7 +243,7 @@ const Checkout: React.FC = () => { )}

        -
        - - -
        + {deliveryType === 'By courier' && ( +
        + + +
        + )} + {deliveryType === 'Point of delivery' && ( +
        + + +
        + )}

        - {deliveryType === 'shipment' ? 'Доставка' : 'Самовывоз'} + {deliveryType === 'By courier' ? 'Доставка' : 'Самовывоз'}

        0 руб.

        @@ -443,6 +481,7 @@ const Checkout: React.FC = () => { + ); }; From a43c761e97b7e106b59465f23145eb6761f1c52a Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Wed, 3 Jan 2024 20:07:26 +0300 Subject: [PATCH 057/141] refactor: move switch to separate function and add default --- src/pages/checkout/index.tsx | 53 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/pages/checkout/index.tsx b/src/pages/checkout/index.tsx index 61370070..19647ba2 100644 --- a/src/pages/checkout/index.tsx +++ b/src/pages/checkout/index.tsx @@ -21,7 +21,7 @@ const Checkout: React.FC = () => { const { handleOpenPopup, handleClosePopup } = usePopup(); const location = useLocation(); const receivedType = location.state?.orderType; - const deliveryType = receivedType ? receivedType : 'Point of delivery'; + const deliveryType = receivedType || 'Point of delivery'; const { values, handleChange, errors, isValid } = useFormAndValidation(); const navigate = useNavigate(); const [selectedPayment, setSelectedPayment] = React.useState(''); @@ -45,52 +45,47 @@ const Checkout: React.FC = () => { handleOpenPopup('openPopupCheckoutResponse'); }; - const handleSubmitOrder = () => { + const validateOrderData = () => { switch (true) { case isLoggedIn && !user.first_name: - openInfoPopup('Пожалуйста, заполните имя в личном кабинете'); - return; + return openInfoPopup('Пожалуйста, заполните имя в личном кабинете'); case isLoggedIn && !user.last_name: - openInfoPopup('Пожалуйста, заполните фамилию в личном кабинете'); - return; + return openInfoPopup('Пожалуйста, заполните фамилию в личном кабинете'); case isLoggedIn && !user.phone_number: - openInfoPopup('Пожалуйста, заполните номер телефона в личном кабинете'); - return; + return openInfoPopup('Пожалуйста, заполните номер телефона в личном кабинете'); + case isLoggedIn && + deliveryType === 'Point of delivery' && + !selectedAddress?.toString().trim(): + return openInfoPopup('Пожалуйста, выберите адрес'); case isLoggedIn && !selectedAddress?.toString().trim(): - openInfoPopup( + return openInfoPopup( 'Пожалуйста, выберите адрес (добавить адрес можно в личном кабинете)' ); - return; case isLoggedIn && !selectedPayment: - openInfoPopup('Пожалуйста, выберите способ оплаты'); - return; - } - - switch (true) { + return openInfoPopup('Пожалуйста, выберите способ оплаты'); case !isLoggedIn && !values.order_firstName?.toString().trim(): - openInfoPopup('Пожалуйста, заполните имя'); - return; + return openInfoPopup('Пожалуйста, заполните имя'); case !isLoggedIn && !values.order_lastName?.toString().trim(): - openInfoPopup('Пожалуйста, заполните фамилию'); - return; + return openInfoPopup('Пожалуйста, заполните фамилию'); case !isLoggedIn && !values.order_phoneNumber?.toString().trim(): - openInfoPopup('Пожалуйста, заполните номер телефона'); - return; + return openInfoPopup('Пожалуйста, заполните номер телефона'); case !isLoggedIn && !values.order_email?.toString().trim(): - openInfoPopup('Пожалуйста, заполните e-mail'); - return; + return openInfoPopup('Пожалуйста, заполните e-mail'); case !isLoggedIn && deliveryType === 'Point of delivery' && !selectedAddress?.toString().trim(): - openInfoPopup('Пожалуйста, выберите адрес'); - return; + return openInfoPopup('Пожалуйста, выберите адрес'); case !isLoggedIn && !selectedAddress?.toString().trim(): - openInfoPopup('Пожалуйста, введите адрес'); - return; + return openInfoPopup('Пожалуйста, введите адрес'); case !isLoggedIn && !selectedPayment: - openInfoPopup('Пожалуйста, выберите способ оплаты'); - return; + return openInfoPopup('Пожалуйста, выберите способ оплаты'); + default: + return true; } + }; + + const handleSubmitOrder = () => { + if (!validateOrderData()) return; let formData: OrderPostAdd = { user_data: { From 68965cb94d9b5384fac11dbf5eab8db5a1b2bc07 Mon Sep 17 00:00:00 2001 From: jsapro <77.3.77@mail.ru> Date: Wed, 3 Jan 2024 20:14:01 +0300 Subject: [PATCH 058/141] refactor: move pickupPointAddresses to constants file --- src/data/constants.ts | 10 ++++++++++ src/pages/checkout/index.tsx | 12 ++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/data/constants.ts b/src/data/constants.ts index e00b08e5..08d62dd3 100644 --- a/src/data/constants.ts +++ b/src/data/constants.ts @@ -8,3 +8,13 @@ export const URLS = { AGREEMENT: '/agreement', DELIVERY_COND: '/delivery-conditions', }; + +export const pickupPointAddresses = { + 1: 'Санкт-Петербург Невский проспект 17', + 2: 'Санкт-Петербург Горохова улица 10', + 3: 'Санкт-Петербург Невский проспект 89', + 4: 'Санкт-Петербург Большой Самсониевский 6', + 5: 'Санкт-Петербург Лесной проспект 56', + 6: 'Санкт-Петербург Владимирский проспект 1', + 7: 'Санкт-Петербург Лиговский проспект 170', +}; diff --git a/src/pages/checkout/index.tsx b/src/pages/checkout/index.tsx index 19647ba2..29deee2a 100644 --- a/src/pages/checkout/index.tsx +++ b/src/pages/checkout/index.tsx @@ -9,6 +9,7 @@ import PopupCheckoutResponse from '@components/popups/popup-checkout-response'; import { OrderPostAdd } from '@services/generated-api/data-contracts.ts'; import { useAuth } from '@hooks/use-auth.ts'; import { useCart } from '@hooks/use-cart-context.ts'; +import { pickupPointAddresses } from '@data/constants'; type Address = { id: number; @@ -30,15 +31,6 @@ const Checkout: React.FC = () => { const userAddresses = user?.addresses as unknown[] as Address[]; const [comment, setComment] = React.useState(''); const [popupText, setPopupText] = useState(''); - const addressesById = { - 1: 'Санкт-Петербург Невский проспект 17', - 2: 'Санкт-Петербург Горохова улица 10', - 3: 'Санкт-Петербург Невский проспект 89', - 4: 'Санкт-Петербург Большой Самсониевский 6', - 5: 'Санкт-Петербург Лесной проспект 56', - 6: 'Санкт-Петербург Владимирский проспект 1', - 7: 'Санкт-Петербург Лиговский проспект 170', - }; const openInfoPopup = (text: string) => { setPopupText(text); @@ -250,7 +242,7 @@ const Checkout: React.FC = () => { - {Object.entries(addressesById).map(([id, address]) => ( + {Object.entries(pickupPointAddresses).map(([id, address]) => (