ostapenkonataliia 1 year ago
parent
commit
a3ee085d4e
54 changed files with 28691 additions and 67 deletions
  1. 3 67
      .idea/workspace.xml
  2. 23 0
      sneakers/.gitignore
  3. 5 0
      sneakers/.idea/.gitignore
  4. 8 0
      sneakers/.idea/modules.xml
  5. 12 0
      sneakers/.idea/react-sneakers-master.iml
  6. 6 0
      sneakers/.idea/vcs.xml
  7. 13 0
      sneakers/README.md
  8. 16205 0
      sneakers/package-lock.json
  9. 47 0
      sneakers/package.json
  10. BIN
      sneakers/public/favicon.ico
  11. 4 0
      sneakers/public/img/arrow.svg
  12. 25 0
      sneakers/public/img/btn-checked.svg
  13. 4 0
      sneakers/public/img/btn-plus.svg
  14. 4 0
      sneakers/public/img/btn-remove.svg
  15. 5 0
      sneakers/public/img/cart.svg
  16. BIN
      sneakers/public/img/complete-order.jpg
  17. BIN
      sneakers/public/img/empty-cart.jpg
  18. 3 0
      sneakers/public/img/heart.svg
  19. 10 0
      sneakers/public/img/liked.svg
  20. BIN
      sneakers/public/img/logo.png
  21. 3 0
      sneakers/public/img/plus.svg
  22. 3 0
      sneakers/public/img/search.svg
  23. BIN
      sneakers/public/img/sneakers/1.jpg
  24. BIN
      sneakers/public/img/sneakers/10.jpg
  25. BIN
      sneakers/public/img/sneakers/2.jpg
  26. BIN
      sneakers/public/img/sneakers/3.jpg
  27. BIN
      sneakers/public/img/sneakers/4.jpg
  28. BIN
      sneakers/public/img/sneakers/5.jpg
  29. BIN
      sneakers/public/img/sneakers/6.jpg
  30. BIN
      sneakers/public/img/sneakers/7.jpg
  31. BIN
      sneakers/public/img/sneakers/8.jpg
  32. BIN
      sneakers/public/img/sneakers/9.jpg
  33. 4 0
      sneakers/public/img/unliked.svg
  34. 3 0
      sneakers/public/img/user.svg
  35. 45 0
      sneakers/public/index.html
  36. BIN
      sneakers/public/logo192.png
  37. BIN
      sneakers/public/logo512.png
  38. 25 0
      sneakers/public/manifest.json
  39. 3 0
      sneakers/public/robots.txt
  40. 152 0
      sneakers/src/App.js
  41. 38 0
      sneakers/src/components/Card/Card.module.scss
  42. 76 0
      sneakers/src/components/Card/index.js
  43. 46 0
      sneakers/src/components/Drawer/Drawer.module.scss
  44. 101 0
      sneakers/src/components/Drawer/index.js
  45. 40 0
      sneakers/src/components/Header.js
  46. 20 0
      sneakers/src/components/Info.jsx
  47. 5 0
      sneakers/src/context.js
  48. 9 0
      sneakers/src/hooks/useCart.js
  49. 17 0
      sneakers/src/index.js
  50. 203 0
      sneakers/src/index.scss
  51. 23 0
      sneakers/src/pages/Favorites.jsx
  52. 51 0
      sneakers/src/pages/Home.jsx
  53. 40 0
      sneakers/src/pages/Orders.jsx
  54. 11407 0
      sneakers/yarn.lock

+ 3 - 67
.idea/workspace.xml

@@ -1,72 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="ChangeListManager">
-    <list default="true" id="c45bf7d2-992f-400a-8194-6f236ee5f805" name="Changes" comment="">
-      <change afterPath="$PROJECT_DIR$/final-project/src/App.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/App.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Dialogs/Dialog.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Dialogs/Dialogs.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Header/Header.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Header/Header.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/NavBar/NavBar.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/NavBar/NavBar.modules.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Profile/MyPost/Profile.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Profile/Profile.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Profile/Profile.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/final-project/src/components/Profile/ProfileInfo/ProfileInfo.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Dialogs/DialogItem/DialogItem.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Dialogs/DialogItem/DialogItem.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Dialogs/Dialogs.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Dialogs/Dialogs.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Dialogs/DialogsContainer.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Dialogs/Message/Message.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Friends/Friends.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Friends/Friends.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Header/Header.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Header/Header.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Music/Music.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/NavBar/NavBar.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/NavBar/Navbar.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/News/News.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/MyPosts/MyPosts.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/MyPosts/MyPosts.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/MyPosts/MyPostsContainer.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/MyPosts/Post/Post.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/MyPosts/Post/Post.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/Profile.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/Profile.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/ProfileInfo/ProfileInfo.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Profile/ProfileInfo/ProfileInfo.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Setting/Setting.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Users/Users.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Users/Users.module.css" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/components/Users/UsersContainer.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/redux/DialogsReducer.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/redux/ProfileReducer.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/redux/UsersReducer.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/redux/reduxStore.js" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/my-app/src/redux/store.js" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/.gitignore" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/README.md" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/package-lock.json" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/package.json" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/public/favicon.ico" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/public/index.html" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/public/logo192.png" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/public/logo512.png" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/public/manifest.json" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/public/robots.txt" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/App.css" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/App.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/App.test.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/index.css" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/index.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/logo.svg" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/reportWebVitals.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/HW_react_1/my-app/src/setupTests.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/rgb.js" beforeDir="false" />
-    </list>
+    <list default="true" id="c45bf7d2-992f-400a-8194-6f236ee5f805" name="Changes" comment="" />
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -99,7 +34,7 @@
     "RunOnceActivity.OpenProjectViewOnStart": "true",
     "RunOnceActivity.ShowReadmeOnStart": "true",
     "WebServerToolWindowFactoryState": "false",
-    "last_opened_file_path": "C:/HipstaGram-master (2)",
+    "last_opened_file_path": "C:/A-Level/JS",
     "list.type.of.created.stylesheet": "CSS",
     "nodejs_package_manager_path": "npm",
     "settings.editor.selected.configurable": "preferences.pluginManager",
@@ -199,6 +134,7 @@
       <workItem from="1676987868108" duration="7277000" />
       <workItem from="1677015437421" duration="14053000" />
       <workItem from="1677099793698" duration="24000" />
+      <workItem from="1692210758161" duration="303000" />
     </task>
     <servers />
   </component>

+ 23 - 0
sneakers/.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 5 - 0
sneakers/.idea/.gitignore

@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/

+ 8 - 0
sneakers/.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/react-sneakers-master.iml" filepath="$PROJECT_DIR$/.idea/react-sneakers-master.iml" />
+    </modules>
+  </component>
+</project>

+ 12 - 0
sneakers/.idea/react-sneakers-master.iml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
+      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/tmp" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
sneakers/.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>

+ 13 - 0
sneakers/README.md

@@ -0,0 +1,13 @@
+Интернет-магазин кроссовок - **React Sneakers**
+
+- [Плейлист с полным курсом на YouTube](https://www.youtube.com/watch?v=ptiom4YWqoE&list=PL0FGkDGJQjJEos_0yVkbKjsQ9zGVy3dG7)
+- [Как задеплоить React Sneakers на бесплатный хостинг](https://www.youtube.com/watch?v=-pJN9faoa8E&t=1951s)
+
+**Stack:**
+
+- ReactJS + хуки
+- React Router
+- Axios
+- SCSS
+- React Context
+- React Skeleton

File diff suppressed because it is too large
+ 16205 - 0
sneakers/package-lock.json


+ 47 - 0
sneakers/package.json

@@ -0,0 +1,47 @@
+{
+  "name": "react-sneakers",
+  "version": "0.1.0",
+  "private": true,
+  "homepage": "https://archakov06.github.io/react-sneakers",
+  "dependencies": {
+    "@testing-library/jest-dom": "^5.11.4",
+    "@testing-library/react": "^11.1.0",
+    "@testing-library/user-event": "^12.1.10",
+    "axios": "^0.21.1",
+    "gh-pages": "^3.2.3",
+    "macro-css": "^1.0.4",
+    "node-sass": "^4.14.1",
+    "react": "^17.0.2",
+    "react-content-loader": "^6.0.3",
+    "react-dom": "^17.0.2",
+    "react-router-dom": "^5.2.0",
+    "react-scripts": "4.0.3",
+    "web-vitals": "^1.0.1"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "eject": "react-scripts eject",
+    "predeploy": "npm run build",
+    "deploy": "gh-pages -d build"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app",
+      "react-app/jest"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  }
+}

BIN
sneakers/public/favicon.ico


+ 4 - 0
sneakers/public/img/arrow.svg

@@ -0,0 +1,4 @@
+<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 7H14.7143" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M8.71436 1L14.7144 7L8.71436 13" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

File diff suppressed because it is too large
+ 25 - 0
sneakers/public/img/btn-checked.svg


+ 4 - 0
sneakers/public/img/btn-plus.svg

@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" fill="white" stroke="#F2F2F2"/>
+<path d="M20.6653 15.1312H17.2021V11.6682C17.2021 10.3328 15.1311 10.3328 15.1311 11.6682V15.1312H11.668C10.3329 15.1312 10.3329 17.2022 11.668 17.2022H15.1311V20.6652C15.1311 22.0005 17.2021 22.0005 17.2021 20.6652V17.2022H20.6653C22.0005 17.2022 22.0005 15.1312 20.6653 15.1312Z" fill="#D3D3D3"/>
+</svg>

+ 4 - 0
sneakers/public/img/btn-remove.svg

@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" fill="white" stroke="#DBDBDB"/>
+<path d="M20.0799 18.6155L17.6311 16.1667L20.0798 13.718C21.0241 12.7738 19.5596 11.3093 18.6154 12.2536L16.1667 14.7023L13.7179 12.2535C12.7738 11.3095 11.3095 12.7738 12.2535 13.7179L14.7023 16.1667L12.2536 18.6154C11.3093 19.5596 12.7738 21.0241 13.718 20.0798L16.1667 17.6311L18.6155 20.0799C19.5597 21.0241 21.0241 19.5597 20.0799 18.6155Z" fill="#B5B5B5"/>
+</svg>

+ 5 - 0
sneakers/public/img/cart.svg

@@ -0,0 +1,5 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7.54548 18.1818C7.99735 18.1818 8.36366 17.8155 8.36366 17.3636C8.36366 16.9117 7.99735 16.5454 7.54548 16.5454C7.09361 16.5454 6.72729 16.9117 6.72729 17.3636C6.72729 17.8155 7.09361 18.1818 7.54548 18.1818Z" stroke="#9B9B9B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M16.5455 18.1818C16.9973 18.1818 17.3637 17.8155 17.3637 17.3636C17.3637 16.9117 16.9973 16.5454 16.5455 16.5454C16.0936 16.5454 15.7273 16.9117 15.7273 17.3636C15.7273 17.8155 16.0936 18.1818 16.5455 18.1818Z" stroke="#9B9B9B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M1 1H4.27273L6.46545 11.9555C6.54027 12.3321 6.7452 12.6705 7.04436 12.9113C7.34351 13.1522 7.71784 13.2801 8.10182 13.2727H16.0545C16.4385 13.2801 16.8129 13.1522 17.112 12.9113C17.4112 12.6705 17.6161 12.3321 17.6909 11.9555L19 5.09091H5.09091" stroke="#9B9B9B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

BIN
sneakers/public/img/complete-order.jpg


BIN
sneakers/public/img/empty-cart.jpg


File diff suppressed because it is too large
+ 3 - 0
sneakers/public/img/heart.svg


File diff suppressed because it is too large
+ 10 - 0
sneakers/public/img/liked.svg


BIN
sneakers/public/img/logo.png


+ 3 - 0
sneakers/public/img/plus.svg

@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10.6653 5.13128H7.20219V1.66827C7.20219 0.332907 5.13118 0.332907 5.13118 1.66827V5.13128H1.66805C0.332981 5.13128 0.332981 7.20221 1.66805 7.20221H5.13118V10.6652C5.13118 12.0006 7.20219 12.0006 7.20219 10.6652V7.20221H10.6653C12.0006 7.20221 12.0006 5.13128 10.6653 5.13128Z" fill="#D3D3D3"/>
+</svg>

+ 3 - 0
sneakers/public/img/search.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15.25 15.25L11.8855 11.8795L15.25 15.25ZM13.75 7.375C13.75 9.06576 13.0784 10.6873 11.8828 11.8828C10.6873 13.0784 9.06576 13.75 7.375 13.75C5.68424 13.75 4.06274 13.0784 2.86719 11.8828C1.67165 10.6873 1 9.06576 1 7.375C1 5.68424 1.67165 4.06274 2.86719 2.86719C4.06274 1.67165 5.68424 1 7.375 1C9.06576 1 10.6873 1.67165 11.8828 2.86719C13.0784 4.06274 13.75 5.68424 13.75 7.375V7.375Z" stroke="#E4E4E4" stroke-width="2" stroke-linecap="round"/>
+</svg>

BIN
sneakers/public/img/sneakers/1.jpg


BIN
sneakers/public/img/sneakers/10.jpg


BIN
sneakers/public/img/sneakers/2.jpg


BIN
sneakers/public/img/sneakers/3.jpg


BIN
sneakers/public/img/sneakers/4.jpg


BIN
sneakers/public/img/sneakers/5.jpg


BIN
sneakers/public/img/sneakers/6.jpg


BIN
sneakers/public/img/sneakers/7.jpg


BIN
sneakers/public/img/sneakers/8.jpg


BIN
sneakers/public/img/sneakers/9.jpg


File diff suppressed because it is too large
+ 4 - 0
sneakers/public/img/unliked.svg


File diff suppressed because it is too large
+ 3 - 0
sneakers/public/img/user.svg


+ 45 - 0
sneakers/public/index.html

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta name="description" content="Web site created using create-react-app" />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <!--
+      manifest.json provides metadata used when your web app is installed on a
+      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <link rel="preconnect" href="https://fonts.gstatic.com" />
+    <link
+      href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
+      rel="stylesheet"
+    />
+    <title>React Sneakers</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>

BIN
sneakers/public/logo192.png


BIN
sneakers/public/logo512.png


+ 25 - 0
sneakers/public/manifest.json

@@ -0,0 +1,25 @@
+{
+  "short_name": "React App",
+  "name": "Create React App Sample",
+  "icons": [
+    {
+      "src": "favicon.ico",
+      "sizes": "64x64 32x32 24x24 16x16",
+      "type": "image/x-icon"
+    },
+    {
+      "src": "logo192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    },
+    {
+      "src": "logo512.png",
+      "type": "image/png",
+      "sizes": "512x512"
+    }
+  ],
+  "start_url": ".",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}

+ 3 - 0
sneakers/public/robots.txt

@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:

+ 152 - 0
sneakers/src/App.js

@@ -0,0 +1,152 @@
+import React from 'react';
+import { Route } from 'react-router-dom';
+import axios from 'axios';
+import Header from './components/Header';
+import Drawer from './components/Drawer';
+import AppContext from './context';
+
+import Home from './pages/Home';
+import Favorites from './pages/Favorites';
+import Orders from './pages/Orders';
+
+function App() {
+  const [items, setItems] = React.useState([]);
+  const [cartItems, setCartItems] = React.useState([]);
+  const [favorites, setFavorites] = React.useState([]);
+  const [searchValue, setSearchValue] = React.useState('');
+  const [cartOpened, setCartOpened] = React.useState(false);
+  const [isLoading, setIsLoading] = React.useState(true);
+
+  React.useEffect(() => {
+    async function fetchData() {
+      try {
+        const [cartResponse, favoritesResponse, itemsResponse] = await Promise.all([
+          axios.get('https://60d62397943aa60017768e77.mockapi.io/cart'),
+          axios.get('https://60d62397943aa60017768e77.mockapi.io/favorites'),
+          axios.get('https://60d62397943aa60017768e77.mockapi.io/items'),
+        ]);
+
+        setIsLoading(false);
+        setCartItems(cartResponse.data);
+        setFavorites(favoritesResponse.data);
+        setItems(itemsResponse.data);
+      } catch (error) {
+        alert('Ошибка при запросе данных ;(');
+        console.error(error);
+      }
+    }
+
+    fetchData();
+  }, []);
+
+  const onAddToCart = async (obj) => {
+    try {
+      const findItem = cartItems.find((item) => Number(item.parentId) === Number(obj.id));
+      if (findItem) {
+        setCartItems((prev) => prev.filter((item) => Number(item.parentId) !== Number(obj.id)));
+        await axios.delete(`https://60d62397943aa60017768e77.mockapi.io/cart/${findItem.id}`);
+      } else {
+        setCartItems((prev) => [...prev, obj]);
+        const { data } = await axios.post('https://60d62397943aa60017768e77.mockapi.io/cart', obj);
+        setCartItems((prev) =>
+          prev.map((item) => {
+            if (item.parentId === data.parentId) {
+              return {
+                ...item,
+                id: data.id,
+              };
+            }
+            return item;
+          }),
+        );
+      }
+    } catch (error) {
+      alert('Ошибка при добавлении в корзину');
+      console.error(error);
+    }
+  };
+
+  const onRemoveItem = (id) => {
+    try {
+      axios.delete(`https://60d62397943aa60017768e77.mockapi.io/cart/${id}`);
+      setCartItems((prev) => prev.filter((item) => Number(item.id) !== Number(id)));
+    } catch (error) {
+      alert('Ошибка при удалении из корзины');
+      console.error(error);
+    }
+  };
+
+  const onAddToFavorite = async (obj) => {
+    try {
+      if (favorites.find((favObj) => Number(favObj.id) === Number(obj.id))) {
+        axios.delete(`https://60d62397943aa60017768e77.mockapi.io/favorites/${obj.id}`);
+        setFavorites((prev) => prev.filter((item) => Number(item.id) !== Number(obj.id)));
+      } else {
+        const { data } = await axios.post(
+          'https://60d62397943aa60017768e77.mockapi.io/favorites',
+          obj,
+        );
+        setFavorites((prev) => [...prev, data]);
+      }
+    } catch (error) {
+      alert('Не удалось добавить в фавориты');
+      console.error(error);
+    }
+  };
+
+  const onChangeSearchInput = (event) => {
+    setSearchValue(event.target.value);
+  };
+
+  const isItemAdded = (id) => {
+    return cartItems.some((obj) => Number(obj.parentId) === Number(id));
+  };
+
+  return (
+    <AppContext.Provider
+      value={{
+        items,
+        cartItems,
+        favorites,
+        isItemAdded,
+        onAddToFavorite,
+        onAddToCart,
+        setCartOpened,
+        setCartItems,
+      }}>
+      <div className="wrapper clear">
+        <Drawer
+          items={cartItems}
+          onClose={() => setCartOpened(false)}
+          onRemove={onRemoveItem}
+          opened={cartOpened}
+        />
+
+        <Header onClickCart={() => setCartOpened(true)} />
+
+        <Route path="" exact>
+          <Home
+            items={items}
+            cartItems={cartItems}
+            searchValue={searchValue}
+            setSearchValue={setSearchValue}
+            onChangeSearchInput={onChangeSearchInput}
+            onAddToFavorite={onAddToFavorite}
+            onAddToCart={onAddToCart}
+            isLoading={isLoading}
+          />
+        </Route>
+
+        <Route path="favorites" exact>
+          <Favorites />
+        </Route>
+
+        <Route path="orders" exact>
+          <Orders />
+        </Route>
+      </div>
+    </AppContext.Provider>
+  );
+}
+
+export default App;

+ 38 - 0
sneakers/src/components/Card/Card.module.scss

@@ -0,0 +1,38 @@
+.card {
+  border: 1px solid #f3f3f3;
+  padding: 30px;
+  width: 220px;
+  border-radius: 40px;
+  margin-right: 30px;
+  margin-bottom: 30px;
+  transition: box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out;
+
+  .plus {
+    cursor: pointer;
+  }
+
+  &:hover {
+    box-shadow: 0px 20px 35px rgba(0, 0, 0, 0.06);
+    transform: translateY(-5px);
+  }
+
+  .favorite {
+    position: absolute;
+    cursor: pointer;
+  }
+
+  span {
+    font-size: 13px;
+    opacity: 0.5;
+    text-transform: uppercase;
+  }
+
+  b {
+    font-size: 14px;
+  }
+
+  h5 {
+    font-weight: 400;
+    font-size: 14px;
+  }
+}

+ 76 - 0
sneakers/src/components/Card/index.js

@@ -0,0 +1,76 @@
+import React from 'react';
+import ContentLoader from 'react-content-loader';
+
+import AppContext from '../../context';
+
+import styles from './Card.module.scss';
+
+function Card({
+  id,
+  title,
+  imageUrl,
+  price,
+  onFavorite,
+  onPlus,
+  favorited = false,
+  loading = false,
+}) {
+  const { isItemAdded } = React.useContext(AppContext);
+  const [isFavorite, setIsFavorite] = React.useState(favorited);
+  const obj = { id, parentId: id, title, imageUrl, price };
+
+  const onClickPlus = () => {
+    onPlus(obj);
+  };
+
+  const onClickFavorite = () => {
+    onFavorite(obj);
+    setIsFavorite(!isFavorite);
+  };
+
+  return (
+    <div className={styles.card}>
+      {loading ? (
+        <ContentLoader
+          speed={2}
+          width={155}
+          height={250}
+          viewBox="0 0 155 265"
+          backgroundColor="#f3f3f3"
+          foregroundColor="#ecebeb">
+          <rect x="1" y="0" rx="10" ry="10" width="155" height="155" />
+          <rect x="0" y="167" rx="5" ry="5" width="155" height="15" />
+          <rect x="0" y="187" rx="5" ry="5" width="100" height="15" />
+          <rect x="1" y="234" rx="5" ry="5" width="80" height="25" />
+          <rect x="124" y="230" rx="10" ry="10" width="32" height="32" />
+        </ContentLoader>
+      ) : (
+        <>
+          {onFavorite && (
+            <div className={styles.favorite} onClick={onClickFavorite}>
+              <img src={isFavorite ? 'img/liked.svg' : 'img/unliked.svg'} alt="Unliked" />
+            </div>
+          )}
+          <img width="100%" height={135} src={imageUrl} alt="Sneakers" />
+          <h5>{title}</h5>
+          <div className="d-flex justify-between align-center">
+            <div className="d-flex flex-column">
+              <span>Цена:</span>
+              <b>{price} руб.</b>
+            </div>
+            {onPlus && (
+              <img
+                className={styles.plus}
+                onClick={onClickPlus}
+                src={isItemAdded(id) ? 'img/btn-checked.svg' : 'img/btn-plus.svg'}
+                alt="Plus"
+              />
+            )}
+          </div>
+        </>
+      )}
+    </div>
+  );
+}
+
+export default Card;

+ 46 - 0
sneakers/src/components/Drawer/Drawer.module.scss

@@ -0,0 +1,46 @@
+.overlay {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  z-index: 1;
+  visibility: hidden;
+  opacity: 0;
+  transition: opacity 0.1s ease-out, visibility 0.1s ease-out;
+  overflow: hidden;
+}
+
+.overlayVisible {
+  visibility: visible;
+  opacity: 1;
+
+  .drawer {
+    transform: translateX(0);
+  }
+}
+
+.drawer {
+  display: flex;
+  flex-direction: column;
+  position: absolute;
+  width: 420px;
+  height: 100%;
+  right: 0;
+  background: #ffffff;
+  box-shadow: -10px 4px 24px rgba(0, 0, 0, 0.1);
+  padding: 30px;
+  transform: translateX(100%);
+  transition: transform 0.3s ease-out;
+
+  .items {
+    flex: 1;
+    overflow: auto;
+    margin-bottom: 40px;
+  }
+
+  h2 {
+    margin: 0;
+  }
+}

+ 101 - 0
sneakers/src/components/Drawer/index.js

@@ -0,0 +1,101 @@
+import React from 'react';
+import axios from 'axios';
+
+import Info from '../Info';
+import { useCart } from '../../hooks/useCart';
+
+import styles from './Drawer.module.scss';
+
+const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
+function Drawer({ onClose, onRemove, items = [], opened }) {
+  const { cartItems, setCartItems, totalPrice } = useCart();
+  const [orderId, setOrderId] = React.useState(null);
+  const [isOrderComplete, setIsOrderComplete] = React.useState(false);
+  const [isLoading, setIsLoading] = React.useState(false);
+
+  const onClickOrder = async () => {
+    try {
+      setIsLoading(true);
+      const { data } = await axios.post('/orders', {
+        items: cartItems,
+      });
+      setOrderId(data.id);
+      setIsOrderComplete(true);
+      setCartItems([]);
+
+      for (let i = 0; i < cartItems.length; i++) {
+        const item = cartItems[i];
+        await axios.delete('/cart/' + item.id);
+        await delay(1000);
+      }
+    } catch (error) {
+      alert('Ошибка при создании заказа :(');
+    }
+    setIsLoading(false);
+  };
+
+  return (
+    <div className={`${styles.overlay} ${opened ? styles.overlayVisible : ''}`}>
+      <div className={styles.drawer}>
+        <h2 className="d-flex justify-between mb-30">
+          Корзина <img onClick={onClose} className="cu-p" src="img/btn-remove.svg" alt="Close" />
+        </h2>
+
+        {items.length > 0 ? (
+          <div className="d-flex flex-column flex">
+            <div className="items flex">
+              {items.map((obj) => (
+                <div key={obj.id} className="cartItem d-flex align-center mb-20">
+                  <div
+                    style={{ backgroundImage: `url(${obj.imageUrl})` }}
+                    className="cartItemImg"></div>
+
+                  <div className="mr-20 flex">
+                    <p className="mb-5">{obj.title}</p>
+                    <b>{obj.price} руб.</b>
+                  </div>
+                  <img
+                    onClick={() => onRemove(obj.id)}
+                    className="removeBtn"
+                    src="img/btn-remove.svg"
+                    alt="Remove"
+                  />
+                </div>
+              ))}
+            </div>
+            <div className="cartTotalBlock">
+              <ul>
+                <li>
+                  <span>Итого:</span>
+                  <div></div>
+                  <b>{totalPrice} руб. </b>
+                </li>
+                <li>
+                  <span>Налог 5%:</span>
+                  <div></div>
+                  <b>{(totalPrice / 100) * 5} руб. </b>
+                </li>
+              </ul>
+              <button disabled={isLoading} onClick={onClickOrder} className="greenButton">
+                Оформить заказ <img src="img/arrow.svg" alt="Arrow" />
+              </button>
+            </div>
+          </div>
+        ) : (
+          <Info
+            title={isOrderComplete ? 'Заказ оформлен!' : 'Корзина пустая'}
+            description={
+              isOrderComplete
+                ? `Ваш заказ #${orderId} скоро будет передан курьерской доставке`
+                : 'Добавьте хотя бы одну пару кроссовок, чтобы сделать заказ.'
+            }
+            image={isOrderComplete ? 'img/complete-order.jpg' : 'img/empty-cart.jpg'}
+          />
+        )}
+      </div>
+    </div>
+  );
+}
+
+export default Drawer;

+ 40 - 0
sneakers/src/components/Header.js

@@ -0,0 +1,40 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+import { useCart } from '../hooks/useCart';
+
+function Header(props) {
+  const { totalPrice } = useCart();
+
+  return (
+    <header className="d-flex justify-between align-center p-40">
+      <Link to="/">
+        <div className="d-flex align-center">
+          <img width={40} height={40} src="img/logo.png" alt="Logotype" />
+          <div>
+            <h3 className="text-uppercase">React Sneakers</h3>
+            <p className="opacity-5">Магазин лучших кроссовок</p>
+          </div>
+        </div>
+      </Link>
+      <ul className="d-flex">
+        <li onClick={props.onClickCart} className="mr-30 cu-p">
+          <img width={18} height={18} src="img/cart.svg" alt="Корзина" />
+          <span>{totalPrice} руб.</span>
+        </li>
+        <li className="mr-20 cu-p">
+          <Link to="/favorites">
+            <img width={18} height={18} src="img/heart.svg" alt="Закладки" />
+          </Link>
+        </li>
+        <li>
+          <Link to="/orders">
+            <img width={18} height={18} src="img/user.svg" alt="Пользователь" />
+          </Link>
+        </li>
+      </ul>
+    </header>
+  );
+}
+
+export default Header;

+ 20 - 0
sneakers/src/components/Info.jsx

@@ -0,0 +1,20 @@
+import React from 'react';
+import AppContext from '../context';
+
+const Info = ({ title, image, description }) => {
+  const { setCartOpened } = React.useContext(AppContext);
+
+  return (
+    <div className="cartEmpty d-flex align-center justify-center flex-column flex">
+      <img className="mb-20" width="120px" src={image} alt="Empty" />
+      <h2>{title}</h2>
+      <p className="opacity-6">{description}</p>
+      <button onClick={() => setCartOpened(false)} className="greenButton">
+        <img src="img/arrow.svg" alt="Arrow" />
+        Вернуться назад
+      </button>
+    </div>
+  );
+};
+
+export default Info;

+ 5 - 0
sneakers/src/context.js

@@ -0,0 +1,5 @@
+import React from 'react';
+
+const AppContext = React.createContext({});
+
+export default AppContext;

+ 9 - 0
sneakers/src/hooks/useCart.js

@@ -0,0 +1,9 @@
+import React from 'react';
+import AppContext from '../context';
+
+export const useCart = () => {
+  const { cartItems, setCartItems } = React.useContext(AppContext);
+  const totalPrice = cartItems.reduce((sum, obj) => obj.price + sum, 0);
+
+  return { cartItems, setCartItems, totalPrice };
+};

+ 17 - 0
sneakers/src/index.js

@@ -0,0 +1,17 @@
+import React from 'react';
+import { BrowserRouter as Router } from 'react-router-dom';
+import ReactDOM from 'react-dom';
+
+import './index.scss';
+import 'macro-css';
+
+import App from './App';
+
+ReactDOM.render(
+  <React.StrictMode>
+    <Router>
+      <App />
+    </Router>
+  </React.StrictMode>,
+  document.getElementById('root'),
+);

+ 203 - 0
sneakers/src/index.scss

@@ -0,0 +1,203 @@
+body {
+  margin: 0;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  background-color: #e7f6ff;
+}
+
+* {
+  font-family: 'Inter', system-ui;
+}
+
+.wrapper {
+  background: #ffffff;
+  box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.04);
+  border-radius: 20px;
+  max-width: 1080px;
+  margin: 50px auto;
+}
+
+header {
+  border-bottom: 1px solid #eaeaea;
+  img {
+    margin-right: 15px;
+  }
+
+  h3,
+  p {
+    margin: 0;
+  }
+}
+
+.content {
+  h1 {
+    margin: 0;
+  }
+}
+
+.cartEmpty {
+  text-align: center;
+
+  p {
+    width: 280px;
+    line-height: 24px;
+  }
+
+  .greenButton {
+    width: 245px;
+    margin-top: 20px;
+
+    &:hover {
+      img {
+        transform: rotate(180deg) translateX(3px);
+      }
+    }
+
+    img {
+      position: relative;
+      top: 1px;
+      transform: rotate(180deg);
+      margin-right: 15px;
+      transition: transform 0.15s ease-in-out;
+    }
+  }
+}
+
+.search-block {
+  border: 1px solid #f3f3f3;
+  border-radius: 10px;
+  padding: 0 15px;
+  position: relative;
+
+  .clear {
+    position: absolute;
+    right: 0;
+    width: 18px;
+    height: 18px;
+    top: 14px;
+    right: 15px;
+  }
+
+  input {
+    border: 0;
+    padding: 13px;
+    font-size: 16px;
+    width: 200px;
+  }
+}
+
+.cartTotalBlock {
+  ul {
+    display: block;
+    margin-bottom: 40px !important;
+
+    li {
+      display: flex;
+      align-items: flex-end;
+      margin-bottom: 20px;
+
+      div {
+        flex: 1;
+        height: 1px;
+        border-bottom: 1px dashed #dfdfdf;
+        position: relative;
+        top: -4px;
+        margin: 0 7px;
+      }
+    }
+  }
+
+  .greenButton {
+    position: relative;
+
+    &:disabled {
+      animation: button-loading 0.6s ease-in-out infinite;
+    }
+
+    &:hover {
+      img {
+        transform: translateX(5px);
+      }
+    }
+
+    img {
+      position: absolute;
+      right: 30px;
+      top: 20px;
+      transition: transform 0.15s ease-in-out;
+    }
+  }
+}
+
+.greenButton {
+  width: 100%;
+  height: 55px;
+  background: #9dd558;
+  border-radius: 18px;
+  border: 0;
+  color: #fff;
+  font-size: 16px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: background 0.1s ease-in-out;
+
+  &:disabled {
+    background-color: #bebebe !important;
+    cursor: default;
+  }
+
+  &:hover {
+    background: lighten(#9dd558, 5%);
+  }
+
+  &:active {
+    background: darken(#9dd558, 5%);
+  }
+}
+
+.cartItem {
+  border: 1px solid #f3f3f3;
+  border-radius: 20px;
+  overflow: hidden;
+  padding: 20px;
+
+  .cartItemImg {
+    height: 70px;
+    width: 70px;
+    background-size: contain;
+    background-position: 0 -3px;
+    background-repeat: no-repeat;
+    margin-right: 20px;
+  }
+
+  p {
+    font-size: 16px;
+    margin: 0;
+  }
+
+  b {
+    font-size: 14px;
+  }
+
+  .removeBtn {
+    opacity: 0.5;
+    cursor: pointer;
+    transition: opacity 0.15s ease-in-out;
+
+    &:hover {
+      opacity: 1;
+    }
+  }
+}
+
+@keyframes button-loading {
+  0% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.7;
+  }
+  100% {
+    opacity: 1;
+  }
+}

+ 23 - 0
sneakers/src/pages/Favorites.jsx

@@ -0,0 +1,23 @@
+import React from 'react';
+import Card from '../components/Card';
+import AppContext from '../context';
+
+function Favorites() {
+  const { favorites, onAddToFavorite } = React.useContext(AppContext);
+
+  return (
+    <div className="content p-40">
+      <div className="d-flex align-center justify-between mb-40">
+        <h1>Мои закладки</h1>
+      </div>
+
+      <div className="d-flex flex-wrap">
+        {favorites.map((item, index) => (
+          <Card key={index} favorited={true} onFavorite={onAddToFavorite} {...item} />
+        ))}
+      </div>
+    </div>
+  );
+}
+
+export default Favorites;

+ 51 - 0
sneakers/src/pages/Home.jsx

@@ -0,0 +1,51 @@
+import React from 'react';
+
+import Card from '../components/Card';
+
+function Home({
+  items,
+  searchValue,
+  setSearchValue,
+  onChangeSearchInput,
+  onAddToFavorite,
+  onAddToCart,
+  isLoading,
+}) {
+  const renderItems = () => {
+    const filtredItems = items.filter((item) =>
+      item.title.toLowerCase().includes(searchValue.toLowerCase()),
+    );
+    return (isLoading ? [...Array(8)] : filtredItems).map((item, index) => (
+      <Card
+        key={index}
+        onFavorite={(obj) => onAddToFavorite(obj)}
+        onPlus={(obj) => onAddToCart(obj)}
+        loading={isLoading}
+        {...item}
+      />
+    ));
+  };
+
+  return (
+    <div className="content p-40">
+      <div className="d-flex align-center justify-between mb-40">
+        <h1>{searchValue ? `Поиск по запросу: "${searchValue}"` : 'Все кроссовки'}</h1>
+        <div className="search-block d-flex">
+          <img src="img/search.svg" alt="Search" />
+          {searchValue && (
+            <img
+              onClick={() => setSearchValue('')}
+              className="clear cu-p"
+              src="img/btn-remove.svg"
+              alt="Clear"
+            />
+          )}
+          <input onChange={onChangeSearchInput} value={searchValue} placeholder="Поиск..." />
+        </div>
+      </div>
+      <div className="d-flex flex-wrap">{renderItems()}</div>
+    </div>
+  );
+}
+
+export default Home;

+ 40 - 0
sneakers/src/pages/Orders.jsx

@@ -0,0 +1,40 @@
+import React from 'react';
+import axios from 'axios';
+
+import Card from '../components/Card';
+import AppContext from '../context';
+
+function Orders() {
+  const { onAddToFavorite, onAddToCart } = React.useContext(AppContext);
+  const [orders, setOrders] = React.useState([]);
+  const [isLoading, setIsLoading] = React.useState(true);
+
+  React.useEffect(() => {
+    (async () => {
+      try {
+        const { data } = await axios.get('https://60d62397943aa60017768e77.mockapi.io/orders');
+        setOrders(data.reduce((prev, obj) => [...prev, ...obj.items], []));
+        setIsLoading(false);
+      } catch (error) {
+        alert('Ошибка при запросе заказов');
+        console.error(error);
+      }
+    })();
+  }, []);
+
+  return (
+    <div className="content p-40">
+      <div className="d-flex align-center justify-between mb-40">
+        <h1>Мои заказы</h1>
+      </div>
+
+      <div className="d-flex flex-wrap">
+        {(isLoading ? [...Array(8)] : orders).map((item, index) => (
+          <Card key={index} loading={isLoading} {...item} />
+        ))}
+      </div>
+    </div>
+  );
+}
+
+export default Orders;

File diff suppressed because it is too large
+ 11407 - 0
sneakers/yarn.lock