Olga_Brekhuntsova před 2 roky
rodič
revize
4377331fd3
48 změnil soubory, kde provedl 13376 přidání a 0 odebrání
  1. 23 0
      project_marketplace/.gitignore
  2. 70 0
      project_marketplace/README.md
  3. 12032 0
      project_marketplace/package-lock.json
  4. 48 0
      project_marketplace/package.json
  5. binární
      project_marketplace/public/favicon.ico
  6. binární
      project_marketplace/public/images/krakenBlue.png
  7. binární
      project_marketplace/public/images/krakenLogo.png
  8. 33 0
      project_marketplace/public/index.html
  9. binární
      project_marketplace/public/logo192.png
  10. binární
      project_marketplace/public/logo512.png
  11. 25 0
      project_marketplace/public/manifest.json
  12. 3 0
      project_marketplace/public/robots.txt
  13. 149 0
      project_marketplace/src/App.css
  14. 57 0
      project_marketplace/src/App.js
  15. 8 0
      project_marketplace/src/App.test.js
  16. 57 0
      project_marketplace/src/basic/dropZone.js
  17. 81 0
      project_marketplace/src/basic/dropZoneAds.js
  18. 13 0
      project_marketplace/src/basic/uploadFilesFunc.js
  19. 7 0
      project_marketplace/src/components/AdCard/AdCard.jsx
  20. 143 0
      project_marketplace/src/components/AllAds/AllAds.jsx
  21. 16 0
      project_marketplace/src/components/Carusell/Carusell.jsx
  22. 16 0
      project_marketplace/src/components/Footer/Footer.jsx
  23. 4 0
      project_marketplace/src/components/FrontArchitecture/Aside/Aside.jsx
  24. 8 0
      project_marketplace/src/components/FrontArchitecture/BodyWrapper/BodyWrapper.jsx
  25. 5 0
      project_marketplace/src/components/FrontArchitecture/Content/Content.jsx
  26. 12 0
      project_marketplace/src/components/FrontArchitecture/FirstPage/FirstPage.jsx
  27. 15 0
      project_marketplace/src/components/FrontArchitecture/Main/Main.jsx
  28. 37 0
      project_marketplace/src/components/Header/Header.jsx
  29. 7 0
      project_marketplace/src/components/LogOutBtn/LogOutBtn.jsx
  30. 60 0
      project_marketplace/src/components/LogValidation/LogValidation.js
  31. 48 0
      project_marketplace/src/components/Registration/LoginForm/LoginForm.jsx
  32. 13 0
      project_marketplace/src/components/Registration/Registration.jsx
  33. 52 0
      project_marketplace/src/components/Registration/SignUpForm/SignUpForm.jsx
  34. 17 0
      project_marketplace/src/components/Routing/Routing.jsx
  35. binární
      project_marketplace/src/images/krakenBlue.png
  36. binární
      project_marketplace/src/images/krakenLogo2.png
  37. binární
      project_marketplace/src/images/krakenLogoBig.png
  38. binární
      project_marketplace/src/images/krakenLogoSmallEmp.png
  39. binární
      project_marketplace/src/images/login.png
  40. binární
      project_marketplace/src/images/logout.png
  41. binární
      project_marketplace/src/images/logout1.png
  42. 13 0
      project_marketplace/src/index.css
  43. 16 0
      project_marketplace/src/index.js
  44. 1 0
      project_marketplace/src/logo.svg
  45. 13 0
      project_marketplace/src/reportWebVitals.js
  46. 170 0
      project_marketplace/src/requests/actions.js
  47. 99 0
      project_marketplace/src/requests/reducers.js
  48. 5 0
      project_marketplace/src/setupTests.js

+ 23 - 0
project_marketplace/.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*

+ 70 - 0
project_marketplace/README.md

@@ -0,0 +1,70 @@
+# Getting Started with Create React App
+
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.\
+Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
+
+The page will reload when you make changes.\
+You may also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.\
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.\
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.\
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can't go back!**
+
+If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
+
+You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
+
+### Analyzing the Bundle Size
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
+
+### Making a Progressive Web App
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
+
+### Advanced Configuration
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
+
+### Deployment
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
+
+### `npm run build` fails to minify
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 12032 - 0
project_marketplace/package-lock.json


+ 48 - 0
project_marketplace/package.json

@@ -0,0 +1,48 @@
+{
+  "name": "project_marketplace",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@emotion/react": "^11.7.1",
+    "@emotion/styled": "^11.6.0",
+    "@mui/material": "^5.4.2",
+    "@testing-library/jest-dom": "^5.16.2",
+    "@testing-library/react": "^12.1.2",
+    "@testing-library/user-event": "^13.5.0",
+    "array-move": "^4.0.0",
+    "react": "^17.0.2",
+    "react-dom": "^17.0.2",
+    "react-dropzone": "^12.0.2",
+    "react-redux": "^7.2.6",
+    "react-router-dom": "^5.3.0",
+    "react-scripts": "5.0.0",
+    "react-sortable-hoc": "^2.0.0",
+    "redux": "^4.1.2",
+    "redux-thunk": "^2.4.1",
+    "web-vitals": "^2.1.4"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "eject": "react-scripts eject"
+  },
+  "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ární
project_marketplace/public/favicon.ico


binární
project_marketplace/public/images/krakenBlue.png


binární
project_marketplace/public/images/krakenLogo.png


+ 33 - 0
project_marketplace/public/index.html

@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/images/krakenLogo.png" />
+    <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%/images/krakenLogo.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%/images/krakenLogo.png" /> -->
+    <!--
+      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`.
+    -->
+    <title>Kraken App</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+   </body>
+</html>

binární
project_marketplace/public/logo192.png


binární
project_marketplace/public/logo512.png


+ 25 - 0
project_marketplace/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
project_marketplace/public/robots.txt

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

+ 149 - 0
project_marketplace/src/App.css

@@ -0,0 +1,149 @@
+.bodyWrapper {
+  font-family: "Roboto", sans-serif;
+  color: #797878;
+  margin-right: auto;
+  margin-left: auto;
+  width: 100%;
+  max-width: 1600px;
+  background-color: #ffffff;
+}
+a:hover,
+a:focus {
+  color: tomato;
+}
+
+.headerWrapper {
+  min-width: 1200px;
+  max-width: 1600px;
+  width: 100%;
+  margin-left: auto;
+  margin-right: auto;
+  background-color: rgb(6, 59, 60);
+  color: white;
+}
+.navWrapper {
+  display: flex;
+  max-width: 1300px;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 10px;
+  justify-content: space-between;
+}
+
+.mainWrapper {
+  min-width: 1200px;
+  max-width: 1600px;
+  /* max-width: 1300px; */
+  margin-left: auto;
+  margin-right: auto;
+  /* background-color: rgb(247, 243, 243); */
+}
+.menuWrapper {
+  width: 400px;
+  background-color: rgba(250, 103, 6, 0.4);
+  color: rgb(68, 68, 68);
+  padding: 10px;
+  font-weight: 700;
+}
+.searchWrapper {
+  width: 200px;
+  border-radius: 5px;
+  background-color: #ffffff;
+}
+.footerWrapper {
+  min-width: 1200px;
+  max-width: 1600px;
+  margin-left: auto;
+  margin-right: auto;
+  background-color: rgb(6, 59, 60);
+  color: white;
+}
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  height: 60px;
+  pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  .App-logo {
+    animation: App-logo-spin infinite 20s linear;
+  }
+}
+
+/* .App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+} */
+
+/* @keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+} */
+.footer-logo {
+  height: 50px;
+}
+.footerNav {
+  display: flex;
+  max-width: 1300px;
+  margin-left: auto;
+  margin-right: auto;
+  padding: 10px;
+  justify-content: space-between;
+}
+.logoBlock {
+  border-radius: 50%;
+  background-color: #ffffff;
+  width: 50px;
+  height: 50px;
+}
+
+.firstpage {
+  text-align: center;
+  padding-left: 200px;
+  padding-right: 200px;
+  padding-bottom: 200px;
+  padding-top: 50px;
+  background-color: white;
+}
+.formWrapper {
+  display: flex;
+  justify-content: center;
+}
+.greetingCard {
+  width: 300px;
+  padding: 10px;
+  background-color: lightgray;
+  border-radius: 5px;
+}
+.greetingLogo {
+  width: 100%;
+}
+/* .loginIconWrapper {
+  width: 40px;
+  height: 40px;
+  border-radius: 5px;
+  padding: 8px;
+} */
+.loginIcon {
+  width: 40px;
+  height: 40px;
+  border-radius: 5px;
+  padding: 8px;
+}

+ 57 - 0
project_marketplace/src/App.js

@@ -0,0 +1,57 @@
+import logo from './images/krakenLogo2.png';
+import logoFooter from './images/krakenBlue.png';
+import './App.css';
+import { Provider, connect } from 'react-redux';
+import { useState, useCallback, useEffect } from 'react';
+import {Router, Route, Link, Redirect, Switch} from 'react-router-dom';
+import createHistory from "history/createBrowserHistory";
+import { createBrowserHistory } from "history";
+import BodyWrapper from './components/FrontArchitecture/BodyWrapper/BodyWrapper';
+import Header from './components/Header/Header';
+import Main from './components/FrontArchitecture/Main/Main';
+import Footer from './components/Footer/Footer';
+import Registration from './components/Registration/Registration';
+import { store } from './requests/reducers';
+import { uploadFile, uploadFiles } from './basic/uploadFilesFunc';
+import { CBasic } from './basic/dropZone';
+import Gallery from './components/Carusell/Carusell';
+import CPageRoute from './components/Routing/Routing';
+
+
+export let history = createBrowserHistory();
+
+
+console.log(store.getState())
+store.subscribe(() => console.log(store.getState()))
+
+function App() {
+  const [open, setOpen] = useState(false);
+  // useEffect(() => { store.getState().auth.token? setOpen(true) : setOpen(false) }, [localStorage.authToken])
+ 
+  return (
+      <Router history={history}>
+    <Provider store={store}>
+   {/* <CPageRoute> */}
+      {!open && <Registration/>}
+         {open && <BodyWrapper >
+        <Header>
+            <img src={logo} className="App-logo" alt="logo" />
+          </Header>
+        <Main>
+       {/* <CMainAds></CMainAds> */}
+          {/* <CBasic onLoad={uploadFile}></CBasic> */}
+          {/* <CAd onLoad={uploadFiles}></CAd> */}
+          <Gallery />
+        </Main>
+       <Footer>
+          <img src={logoFooter} className="footer-logo" alt="logo" />
+        </Footer>
+        </BodyWrapper>}
+         {/* </CPageRoute> */}
+    </Provider>
+  </Router>)
+}
+
+export default App;
+
+

+ 8 - 0
project_marketplace/src/App.test.js

@@ -0,0 +1,8 @@
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+  render(<App />);
+  const linkElement = screen.getByText(/learn react/i);
+  expect(linkElement).toBeInTheDocument();
+});

+ 57 - 0
project_marketplace/src/basic/dropZone.js

@@ -0,0 +1,57 @@
+import { useDropzone } from 'react-dropzone';
+import { actionSetAvatar } from '../requests/actions';
+import { connect } from 'react-redux';
+import { useState, useCallback, useEffect } from 'react';
+
+// const actionUploadFile = file => actionPromise('uploadFile', uploadFile(file));
+// const CBasic = connect(null, {onLoad:actionUploadFile})(Basic  );
+
+
+// const actionAvatar = (imageId) =>
+//   async (dispatch, getState) => {
+//     await dispatch (actionPromise('setAvatar', gql(`mutation setAvatar ( $imageId:ID, $user_id:String) {
+//     UserUpsert(user:{_id: $user_id, avatar: {_id: $imageId}}){
+//         _id, avatar{
+//             _id
+//         }
+//     }}`, {imageId, user_id: getState().auth.payload?.sub?.id})))
+//   }
+// const actionAboutMe = (_id) =>
+//     actionPromise('aboutMe', gql(`query {
+//         userFindOne(user: {_id})}`))
+
+// const actionSetAvatar = file =>
+//   async (dispatch) => {
+//     let result=await dispatch(actionUploadFile(file));
+//     if (result) {
+//      await dispatch(actionAvatar(result._id));
+//       // dispatch(actionAboutMe(store.state.auth.payload));
+//     }
+    
+//    }
+
+function Basic({onLoad}) {
+  const {acceptedFiles, getRootProps, getInputProps} = useDropzone();
+  
+  const files = acceptedFiles.map(file => (
+    <li key={file.path}>
+      {file.path} - {file.size} bytes
+    </li>
+  ));
+    useEffect(() => {acceptedFiles[0] && onLoad(acceptedFiles[0]) }, [acceptedFiles]);
+      return (
+    <section className="container">
+      <div {...getRootProps({className: 'dropzone'})}>
+        <input {...getInputProps()} />
+        <p>Drag 'n' drop some files here, or click to select files</p>
+      </div>
+      <aside>
+        <h4>Files</h4>
+        <ul>{files}</ul>
+      </aside>
+    </section>
+  );
+};
+// const CBasic = connect(null, { onLoad: actionSetAvatar })(Basic);
+    
+// export {CBasic};

+ 81 - 0
project_marketplace/src/basic/dropZoneAds.js

@@ -0,0 +1,81 @@
+import { useState } from "react";
+import { sortableContainer, sortableElement } from 'react-sortable-hoc';
+import { arrayMoveImmutable } from 'array-move';
+import { backendURL} from '../requests/reducers';
+import { connect } from 'react-redux';
+import { actionAdUpsert } from "../requests/actions";
+
+
+const defaultAd = {
+    "_id": "5e8dc32942ab995bb1706662",
+    "title": "second post",
+    "comments": null,
+    "images": [
+        {
+            "_id": "5e8dc1fc42ab995bb1706660",
+            "url": "images/00e734a765bb89be183727f2404be689"
+        },
+        {
+            "_id": "5e8dc1fd42ab995bb1706661",
+            "url": "images/60a5fa93cbae5d144cb215f0222e5bef"
+        }
+    ]
+};
+
+
+const SortableItem = sortableElement(({ url }) => (
+    <li>
+        <img
+            src={backendURL + '/' + url}
+            style={{ maxWidth: '200px', maxHeight: '200px' }}
+        />
+    </li>
+));
+
+const SortableContainer = sortableContainer(({ children }) => {
+    return <ul>{children}</ul>
+});
+
+
+
+const AdEditor = ({ ad = defaultAd, onSave, onFileDrop, fileStatus }) => {
+    const [state, setState] = useState(ad)
+    const onSortEnd = ({ oldIndex, newIndex }) => {
+        setState({
+            ...state,
+            images: arrayMoveImmutable(state.images, oldIndex, newIndex),
+        })
+       
+    }
+    // onFileDrop();
+    // fileStatus
+   
+    // useEffect(() => { if (fileStatus.status===resolved) { } }, [fileStatus]);
+    return (<>
+      <SortableContainer onSortEnd={onSortEnd}>
+            {state.images.map(({ _id, url }, index) => (
+                <SortableItem key={`item - ${_id}`} index={index} url={url}/>
+        ))}
+      </SortableContainer>
+      <button onClick={() => onSave(state)}> Save </button>
+    </>
+  )
+}
+
+// const EntityEditor = ({entity={array: []}, onSave, onFileDrop, fileStatus}) => {
+//     const [state,setState] = useState(entity)
+    //по файлу в дропзоне:
+        //дергать onFileDrop
+        //fileStatus - информация о заливке файла из redux
+        //через useEffect дождаться когда файл зальется
+        //и сделать setState({...state, array: [...state.array, {объект файла с бэка с _id и url}]})
+    //по react-sortable-hoc
+        //делаете как в пример arrayMove для state.array
+    //по кнопке сохранения делаем onSave(state)
+    //где-то рядом остальные поля из state типа title name text
+    //но это вы уже знаете
+// }
+
+const CAd = connect(null, { onSave: actionAdUpsert })(AdEditor);
+// const CAdEdit = connect(state => ({ fileStatus:state.promise.}), { onSave: actionAdUpsert })(AdEditor);
+export {CAd };

+ 13 - 0
project_marketplace/src/basic/uploadFilesFunc.js

@@ -0,0 +1,13 @@
+import { backendURL } from "../requests/reducers";
+
+const uploadFile = file => {
+  const fd = new FormData();
+  fd.append('photo', file);
+  return fetch((backendURL+'/upload'), {
+        method: 'POST',
+        headers: localStorage.authToken ? { "Authorization": "Bearer " + localStorage.authToken } : {},
+        body: fd
+        }).then(res => res.json())
+};
+ 
+export { uploadFile};

+ 7 - 0
project_marketplace/src/components/AdCard/AdCard.jsx

@@ -0,0 +1,7 @@
+// import { useEffect, useState } from 'react';
+// import { connect } from 'react-redux';
+
+
+// const AdCard = () => {return <div>hello</div> }
+// const CAdCard = connect()();
+// export default CAdCard;

+ 143 - 0
project_marketplace/src/components/AllAds/AllAds.jsx

@@ -0,0 +1,143 @@
+// import { useEffect, useState } from 'react';
+import { connect } from 'react-redux';
+// import CAdCards from '../AdCard/AdCard';
+import { actionAllAds } from '../../requests/actions';
+import { store } from "../../requests/reducers";
+const defaultAds=[ 
+      {
+        "_id": "5dc9c9a879064d79bb6ba069",
+        "title": "iphone",
+        "comments": [
+          {
+            "text": "Comment"
+          },
+          {
+            "text": "Какой-то текст"
+          }
+        ],
+        "images": null
+      },
+      {
+        "_id": "5dc9dbc879064d79bb6ba06a",
+        "title": "dasd",
+        "comments": [
+          {
+            "text": "Мой первый комментарий"
+          }
+        ],
+        "images": null
+      },
+
+      {
+        "_id": "5dc9e5cfef38433c6c85e7c2",
+        "title": "New phone",
+        "comments": null,
+        "images": null
+      },
+      {
+        "_id": "5dcaaf0cef38433c6c85e7c5",
+        "title": "ASD",
+        "comments": null,
+        "images": null
+      },
+      {
+        "_id": "5dcab049ef38433c6c85e7c7",
+        "title": "Awesome new phone",
+        "comments": null,
+        "images": null
+      },
+      {
+        "_id": "5dcab950ef38433c6c85e7cc",
+        "title": "dasdsa",
+        "comments": null,
+        "images": null
+      },
+      {
+        "_id": "5f9dbe4d73753d12d794221b",
+        "title": "New AD999",
+        "comments": null,
+        "images": [
+          {
+            "_id": "5fc8f03873753d12d794227e",
+            "url": "images/56220f3577dc9dd53cbdb7e7a82067b3"
+          }
+        ]
+      },
+      {
+        "_id": "615349eee5a2263e42514221",
+        "title": "test",
+        "comments": null,
+        "images": null
+      },
+
+    
+      {
+        "_id": "5dcabc50ef38433c6c85e7d8",
+        "title": "",
+        "comments": null,
+        "images": [
+          {
+            "_id": "5dcabc50ef38433c6c85e7d7",
+            "url": null
+          }
+        ]
+      },
+      {
+        "_id": "5dcabc6eef38433c6c85e7da",
+        "title": "",
+        "comments": null,
+        "images": [
+          {
+            "_id": "5dcabc6eef38433c6c85e7d9",
+            "url": null
+          }
+        ]
+      },
+      {
+        "_id": "5dcac1ccef38433c6c85e7de",
+        "title": "dsada",
+        "comments": null,
+        "images": [
+          {
+            "_id": "5dcac1ccef38433c6c85e7dd",
+            "url": null
+          }
+        ]
+      },
+      {
+        "_id": "5dcac215ef38433c6c85e7e1",
+        "title": "dsad",
+        "comments": null,
+        "images": [
+          {
+            "_id": "5dcac215ef38433c6c85e7e0",
+            "url": null
+          }
+        ]
+      },
+           {
+        "_id": "5dcae383ef38433c6c85e7e7",
+        "title": "dsadasd",
+        "comments": null,
+        "images": [
+          {
+            "_id": "5dcae381ef38433c6c85e7e6",
+            "url": "images/c45eb7c1921ac2e114d27e26619a4974"
+          }
+        ]
+      }]
+const AdCard = ({ ad: { _id, title } = {}}) => 
+  <li key={_id} className='AdCard'>
+    {title}
+    </li>
+const AllAds = ({ ads = defaultAds }) => {
+ 
+    return (<ul className='AllAdsList'>
+        {ads.map(item => <AdCard ad={item} />)}
+    </ul>)
+}
+
+const CAllAds = connect(state => ({ ads: state.promise.allAds?.payload || [] }),
+  { onLoad:store.dispatch(actionAllAds()) })(AllAds);
+
+export default CAllAds;

+ 16 - 0
project_marketplace/src/components/Carusell/Carusell.jsx

@@ -0,0 +1,16 @@
+import { useState } from "react";
+
+const Gallery = ({ images=["https://cdnn21.img.ria.ru/images/155666/00/1556660022_36:0:2767:2048_1920x0_80_0_0_0f95880a02fc9e226884ce632677e009.jpg", "http://c.files.bbci.co.uk/4AC5/production/_111914191_b5eff905-0549-4c4f-84e1-1bc01721ae50.jpg", "https://cdni.rbth.com/rbthmedia/images/2021.11/original/61a5167b85600a470774586f.jpg"] }) => { 
+    const [index, setIndex] = useState(0);
+    return (
+        <div className="Gallery">
+           < div>
+
+                <img src={images[index]} style={{ width: "200px" }} alt="" />
+
+ </div>
+        </div>
+    )
+}
+
+export default Gallery;

+ 16 - 0
project_marketplace/src/components/Footer/Footer.jsx

@@ -0,0 +1,16 @@
+
+const Footer = ({ children}) => {
+            return (
+                <footer className='footerWrapper'>
+                    <div className="footerNav"><div className="logoBlock">{ children}</div></div>
+                {/* <div className={styles.container}>
+                    <div className={styles.wrapper}>
+                            <Logo />
+                            <Burger menuState={menuState} handleMenu = {(isOpen)=> setMenuState(isOpen)}/>
+                                        </div>
+                        <Menu menuList={menuList} contactsList={contactsList} menuState={ menuState}/>
+                </div> */}
+             </footer>
+        );
+    };
+export default Footer;

+ 4 - 0
project_marketplace/src/components/FrontArchitecture/Aside/Aside.jsx

@@ -0,0 +1,4 @@
+const Aside = () => { 
+    return <aside className="menuWrapper">menu</aside>
+}
+export default Aside;

+ 8 - 0
project_marketplace/src/components/FrontArchitecture/BodyWrapper/BodyWrapper.jsx

@@ -0,0 +1,8 @@
+// import styles from "../../../App.css";
+
+const BodyWrapper = ({ children }) => {
+    
+    return (<div className='bodyWrapper'>
+                 {children}
+    </div>);};
+export default BodyWrapper;

+ 5 - 0
project_marketplace/src/components/FrontArchitecture/Content/Content.jsx

@@ -0,0 +1,5 @@
+
+const Content = ({children}) => { 
+    return children
+}
+export default Content;

+ 12 - 0
project_marketplace/src/components/FrontArchitecture/FirstPage/FirstPage.jsx

@@ -0,0 +1,12 @@
+import logoBig from '../../../images/krakenLogoBig.png';
+const FirstPage = ({Render, handleOpen}) => {
+    return <>
+        <div className="firstpage">           
+              <h1>Welcome to KrakenPlace</h1>
+                <div className="formWrapper">
+                <div className='greetingCard'>  <img src={logoBig} className=" greetingLogo" alt="logoBig" /></div>
+                <Render handleOpen={ handleOpen}/>
+                </div>
+            </div></>
+}
+export default FirstPage;

+ 15 - 0
project_marketplace/src/components/FrontArchitecture/Main/Main.jsx

@@ -0,0 +1,15 @@
+
+import Aside from "../Aside/Aside";
+import Content from "../Content/Content";
+import CAllAds from "../../AllAds/AllAds";
+
+
+const Main = ({ children}) => <main className="mainWrapper">
+    skfjcmkszdxmk cmfd
+    <Aside>  </Aside>
+    <Content> 
+       <CAllAds/>    
+    </Content>
+    { children}
+</main>
+export default Main;

+ 37 - 0
project_marketplace/src/components/Header/Header.jsx

@@ -0,0 +1,37 @@
+import logout from '../../images/logout1.png';
+import { useState } from 'react';
+import { Button, Dialog, DialogTitle, DialogContent, TextField, DialogActions } from '@mui/material';
+import { store } from '../../requests/reducers';
+import CLogout  from '../../components/LogOutBtn/LogOutBtn';
+const Header = ({ children}) => {
+    // let burgerState = false;
+    // let [menuState, setMenuState] = useState(burgerState);
+    // function handleMenu (burgerState) {setMenuState(burgerState) };
+
+    const [open, setOpen] = useState(true);
+    const handleClickOpen = () => setOpen(false);
+      const handleClose = () => setOpen(true);
+
+            return (
+                <header className='headerWrapper'>
+                    <div className='navWrapper'>
+                         { children}
+                        ksdmcksmfckmvscklmck
+                        <div className="searchWrapper">asdcascx</div>
+                      <Button onClick={handleClickOpen}><img src={logout} className='loginIcon' alt="logoutIcon" />{store.getState().auth.payload?.sub?.login }</Button>
+                         <CLogout/> 
+                       
+          
+                    </div>
+                   
+                {/* <div className={styles.container}>
+                    <div className={styles.wrapper}>
+                            <Logo />
+                            <Burger menuState={menuState} handleMenu = {(isOpen)=> setMenuState(isOpen)}/>
+                                        </div>
+                        <Menu menuList={menuList} contactsList={contactsList} menuState={ menuState}/>
+                </div> */}
+             </header>
+        );
+    };
+export default Header;

+ 7 - 0
project_marketplace/src/components/LogOutBtn/LogOutBtn.jsx

@@ -0,0 +1,7 @@
+import { connect } from "react-redux";
+import { actionAuthLogout } from "../../requests/actions";
+import { Button, DialogTitle, DialogContent, TextField, DialogActions } from '@mui/material';
+
+ const CLogout = connect(state => ({ children: `Выйти`}),
+     { onClick: actionAuthLogout })(Button);
+export default CLogout;

+ 60 - 0
project_marketplace/src/components/LogValidation/LogValidation.js

@@ -0,0 +1,60 @@
+const f9 = (rootId) => {
+const login = 'admin';
+    const password = 'qwerty';
+    
+    const task09block = document.createElement('div');
+    const task09title = document.createElement('h2');
+    const loginInputId =document.createElement('input');
+    const passwordInputId =document.createElement('input');
+    const signBtnId =document.createElement('button');
+task09title.innerText = 'Task-09 Login and password';
+    task09title.style = "margin-left:auto; margin-right:auto";
+    loginInputId.style = 'display:block';
+    loginInputId.placeholder = 'Enter login';
+    passwordInputId.style = 'margin-top:10px;';
+    passwordInputId.placeholder = 'Enter password';
+    signBtnId.type = 'button';
+    signBtnId.style = 'display:block; margin-top:20px';
+    signBtnId.innerText = 'Sign in';
+    
+rootId.appendChild(task09block); 
+task09block.appendChild(task09title); 
+task09block.appendChild(loginInputId); 
+task09block.appendChild(passwordInputId); 
+task09block.appendChild(signBtnId); 
+const pswdInMemory = [];
+const pswdOnScreen = [];
+    
+//Замена вводимых символов пароля на экране на звездочки *
+passwordInputId.oninput = () => {
+    let pswdInputLength = 0;
+    pswdInputLength = passwordInputId.value.split('').length;
+    pswdInMemory.push(passwordInputId.value.split('')[pswdInputLength - 1]);
+    pswdOnScreen[pswdInputLength-1] = "*";
+    passwordInputId.value=pswdOnScreen.join('');
+     }
+//Верификация логина и пароля
+signBtnId.onclick = () => { 
+    const loginInput = loginInputId.value;
+    const pswdInput = pswdInMemory.join('');
+    let notific='';
+    if (loginInput === login) {
+        if (pswdInput === password)
+        {
+            notific = "Congratulations! Correct login & password";
+     }
+        else {
+            notific = "Wrong password";        
+        }
+    } else {
+        notific = "Wrong login"
+    }
+    passwordInputId.value = '';
+    pswdInMemory.length = 0;
+    pswdOnScreen.length = 0;
+    alert(notific);
+};
+
+}
+f9(root);
+export default f9;

+ 48 - 0
project_marketplace/src/components/Registration/LoginForm/LoginForm.jsx

@@ -0,0 +1,48 @@
+import { useState } from 'react';
+import { Button, DialogTitle, DialogContent, TextField, DialogActions } from '@mui/material';
+import { actionLogin } from '../../../requests/actions';
+import {connect } from 'react-redux';
+
+const LoginForm = ({ handleOpen, onLogin }) => { 
+  
+  const [login, setLogin] = useState('');
+  const [password, setPassword] = useState('');
+
+  const handleUserInput = (inputValue, inputType) => {
+    (inputType === 'login') && setLogin(inputValue);
+    (inputType === 'password') && setPassword(inputValue);
+  };
+  return <> 
+                 <div>     <DialogTitle id="registration">LOG IN</DialogTitle>
+                            <DialogContent>
+                                <TextField autoFocus margin='dense'
+                                    id='login'
+                                    label='Login'
+          type='text'
+          value={login}
+          onChange={e => { handleUserInput(setLogin(e.target.value, 'login')) }}
+                                    fullWidth>
+                                </TextField>
+                                <TextField margin='dense'
+                                    id='pass'
+                                    label='Password'
+          type='password'
+          value={password}
+          onChange={e => { handleUserInput(setPassword(e.target.value, 'password')) }}
+                                    fullWidth>
+                                </TextField>
+                                
+                            </DialogContent>
+      <DialogActions sx={{ display: "block" }}>
+     
+        <Button type="submit" onClick={() => {onLogin(login, password)}}>  Log in </Button>
+       <div> Don't you have an account?
+        <Button onClick={handleOpen}>Sign up</Button></div>
+     
+  
+          </DialogActions>
+          </div>
+           </>
+}
+const CLoginForm = connect(null, { onLogin: actionLogin })(LoginForm)
+export default CLoginForm;

+ 13 - 0
project_marketplace/src/components/Registration/Registration.jsx

@@ -0,0 +1,13 @@
+import CLoginForm from "./LoginForm/LoginForm";
+import SignUpForm from "./SignUpForm/SignUpForm";
+import FirstPage from "../FrontArchitecture/FirstPage/FirstPage";
+import { useState } from "react";
+
+const Registration = () => {
+    const [signUp, setSignUp] = useState(false);
+    const signUpOpen = () => { setSignUp(true) };
+    const signUpClose = () => { setSignUp(false) };
+    return !signUp ? (<FirstPage Render={CLoginForm} handleOpen={signUpOpen}></FirstPage>):(<FirstPage Render={SignUpForm} handleOpen={signUpClose}></FirstPage>)
+
+ };
+export default Registration;

+ 52 - 0
project_marketplace/src/components/Registration/SignUpForm/SignUpForm.jsx

@@ -0,0 +1,52 @@
+import { useState } from 'react';
+import { Button, DialogTitle, DialogContent, TextField, DialogActions } from '@mui/material';
+
+const SignUpForm = ({handleOpen }) => { 
+  const [login, setLogin] = useState('');
+  const [password, setPassword] = useState('');
+    const [passwordConf, setPasswordConf] = useState('');
+  const handleUserInput = (inputValue, inputType) => {
+    (inputType === 'login') && setLogin(inputValue);
+    (inputType === 'password') && setPassword(inputValue);
+    (inputType === 'passwordConf') && setPasswordConf(inputValue);
+  };
+  
+  return <>
+            <div>     <DialogTitle id="registration">SIGN UP</DialogTitle>
+                            <DialogContent sx={{ width:"446px" }}>
+                                <TextField autoFocus margin='dense'
+                                    id='login'
+                                    label='Login'
+          type='text'
+          value={login}
+          onChange={e => { handleUserInput(setLogin(e.target.value, 'login')) }}
+                                    fullWidth>
+                                </TextField>
+                                <TextField margin='dense'
+                                    id='pass'
+                                    label='Password'
+          type='password'
+          value={password}
+          onChange={e => { handleUserInput(setPassword(e.target.value, 'password')) }}
+                                    fullWidth>
+        </TextField>
+        
+          <TextField margin='dense'
+                                    id='passConf'
+                                    label='Confirm Password'
+          type='password'
+          value={passwordConf}
+          onChange={e => { handleUserInput(setPassword(e.target.value, 'passwordConf')) }}
+                                    fullWidth>
+                                </TextField>                                
+                            </DialogContent>
+      <DialogActions sx={{ display: "block"}}>    
+          <Button onClick={handleOpen}>Sign Up</Button>
+          {/* <Button onClick={(e) => { onLogin(login, password) }}>Sign up</Button> */}
+          <div>Already have an account? <Button onClick={handleOpen}>Log in</Button></div>
+     
+          </DialogActions>
+          </div>
+               </>
+}
+export default SignUpForm;

+ 17 - 0
project_marketplace/src/components/Routing/Routing.jsx

@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import {Router, Route, Link, Redirect, Switch} from 'react-router-dom';
+import Registration from '../Registration/Registration';
+
+const PageRoute = (token) => {
+return  <div>
+      <Switch>
+       {/* <Route path="/registration" component={CRegistrationForm} /> */}
+       <Route path="/login" component={Registration} />
+        {/* <Route path="/login" component={CLoginForm} /> */}
+             </Switch>
+    </div>
+ 
+}
+
+const CPageRoute = connect(state => ({ token: state.authReducer.token }))(PageRoute);
+export default CPageRoute;

binární
project_marketplace/src/images/krakenBlue.png


binární
project_marketplace/src/images/krakenLogo2.png


binární
project_marketplace/src/images/krakenLogoBig.png


binární
project_marketplace/src/images/krakenLogoSmallEmp.png


binární
project_marketplace/src/images/login.png


binární
project_marketplace/src/images/logout.png


binární
project_marketplace/src/images/logout1.png


+ 13 - 0
project_marketplace/src/index.css

@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}

+ 16 - 0
project_marketplace/src/index.js

@@ -0,0 +1,16 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './App';
+
+ReactDOM.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>,
+  document.getElementById('root')
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+// reportWebVitals();

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
project_marketplace/src/logo.svg


+ 13 - 0
project_marketplace/src/reportWebVitals.js

@@ -0,0 +1,13 @@
+const reportWebVitals = onPerfEntry => {
+  if (onPerfEntry && onPerfEntry instanceof Function) {
+    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+      getCLS(onPerfEntry);
+      getFID(onPerfEntry);
+      getFCP(onPerfEntry);
+      getLCP(onPerfEntry);
+      getTTFB(onPerfEntry);
+    });
+  }
+};
+
+export default reportWebVitals;

+ 170 - 0
project_marketplace/src/requests/actions.js

@@ -0,0 +1,170 @@
+import { gql, store } from './reducers';
+import { uploadFile } from '../basic/uploadFilesFunc';
+
+
+const actionPending = (name) => ({ type: "PROMISE", name, status: "PENDING" });
+const actionFulfilled = (name, payload) => ({
+    type: "PROMISE",
+    name,
+    status: "FULFILLED",
+    payload,
+});
+
+const actionRejected = (name, error) => ({
+    type: "PROMISE",
+    name,
+    status: "REJECTED",
+    error,
+});
+
+const actionPromise = (name, promise) =>
+    async (dispatch) => {
+    dispatch(actionPending(name));
+    try {
+        let payload = await promise;
+        dispatch(actionFulfilled(name, payload));
+        return payload;
+    } catch (error) {
+        dispatch(actionRejected(name, error));
+    }
+};
+
+//Регистрация
+
+async function gqlRegistration(login, password) {
+    try {
+        let result = await gql(`mutation register ($login:String!, $password:String!){
+  createUser(login:$login password:$password){
+    _id login
+  }
+}`,
+            { "login": login, "password": password });
+        return (result.errors?result.errors[0].message:result.data.UserUpsert._id);
+    }
+    catch (e) {return e}
+};
+
+const actionRegister = (login, password) => 
+    async dispatch => {
+        let user = await dispatch(actionPromise('login', gqlRegistration(login, password)));
+        if (user) { await dispatch(actionLogin(login, password)) }
+    };
+
+//Авторизация
+// async function gqlLogin(login, password) {
+//     try {
+//         let result = await gql(` query log($login:String!, $password:String!){
+//     login(login:$login, password:$password)
+//   }`,
+//             { "login": login, "password": password }); 
+                  
+//         return (result.data.login?result.data.login:'Пользователь не найден')
+//     }
+//        catch(e) {return e}   
+// };
+
+const actionLogin = (login, password) =>
+    async dispatch => {
+        let token = await dispatch(actionPromise("login", gql(` query login($login:String!, $password:String!){
+    login(login:$login, password:$password)
+  }`,
+    { "login": login, "password": password })));
+        if (token) { await dispatch(actionAuthLogin(token)) }
+    };
+
+const actionAuthLogin = (token) => ({ type: "AUTH_LOGIN", token });
+
+
+const actionFullLogin = (login, password) => ({ type: 'FULL_LOGIN', login, password });
+
+const actionAuthLogout = () => ({ type: "AUTH_LOGOUT" });
+
+//Занрузка всех объявлений
+const actionAllAds = () => 
+    actionPromise('allAds', gql(`query ads($query: String){
+        AdFind(query: $query) {
+            _id, 
+            owner {
+                login
+            },
+            images {
+                url
+            },
+            title,
+            description,
+            price
+        }
+    }`, { query: JSON.stringify([ {},{ sort: [{ _id: -1 }] }]) }
+    ))
+
+//Загрузка объявления по id
+const actionAdById = (_id) => 
+    actionPromise('adById', gql(`query adById($query: String){
+        AdFindOne(query:$query){
+            _id,
+            owner {
+              login
+            },
+            images {
+              _id,
+              url
+            },
+            comments {
+              _id,
+              text,
+              owner {
+                  login
+              }
+            },
+            createdAt,
+            title, 
+            description,
+            tags,
+            address,
+            price,
+			}
+        }`,
+        {query: JSON.stringify([{ _id }])
+        }
+    ))
+
+
+//Загрузка картинки аватара
+const actionAvatar = (imageId) =>
+    async (dispatch, getState) => {
+        await dispatch(actionPromise('setAvatar', gql(`mutation setAvatar ( $imageId:ID, $user_id:String) {
+    UserUpsert(user:{_id: $user_id, avatar: {_id: $imageId}}){
+        _id, avatar{
+            _id
+        }
+    }}`, { imageId, user_id: getState().auth.payload?.sub?.id })))
+    };
+
+const actionSetAvatar = file =>
+    async (dispatch) => {
+        let result = await dispatch(actionUploadFile(file));
+        if (result) {
+            await dispatch(actionAvatar(result._id));
+            // dispatch(actionAboutMe(store.state.auth.payload));
+        }
+    };
+   
+   //Загрузка изображений к объявлению Ad
+const actionUploadFile = file => actionPromise('uploadFile', uploadFile(file));
+const actionUploadFiles = files => actionPromise('uploadFiles', Promise.all(files=>files.map(file=>uploadFile(file))));
+
+const actionAdUpsert = (ad) =>
+    actionPromise(
+        'adUpsert',
+        gql(
+            `
+mutation AdUpsert($ad:AdInput){
+  AdUpsert(ad:$ad){
+    _id title
+  }
+}`,
+            { ad: { ...ad, images: ad.images.map(({ _id }) => ({ _id })) } },
+        ),
+    );
+
+export {actionPending, actionFulfilled, actionRejected, actionPromise, actionRegister, actionLogin, actionAuthLogin, actionFullLogin, actionAuthLogout, actionAdById, actionAvatar, actionSetAvatar, actionAllAds,  actionUploadFile, actionUploadFiles, actionAdUpsert  };

+ 99 - 0
project_marketplace/src/requests/reducers.js

@@ -0,0 +1,99 @@
+import thunk from 'redux-thunk';
+import {createStore, combineReducers, applyMiddleware} from 'redux';
+import { actionPending, actionFulfilled, actionRejected, actionPromise, actionRegister, actionLogin, actionAuthLogin, actionAuthLogout, actionUploadFile, actionUploadFiles, actionAdUpsert } from './actions';
+
+const backendURL = 'http://marketplace.asmer.fs.a-level.com.ua';
+
+//Шаблон запроса GQL
+const getGQL = url =>
+    (query, variables) => fetch(url, {
+        method: 'POST',
+        headers: {
+            "Content-Type": "application/json",
+            ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} : {})
+        },
+        body: JSON.stringify({query, variables})
+    }).then(res => res.json())
+        .then(data => {
+            if (data.data){
+                return Object.values(data.data)[0] 
+            } else {
+                throw new Error(JSON.stringify(data.errors))
+            }
+        })
+
+//Запрос на бэк marketplace 
+const gql = getGQL(backendURL + '/graphql');
+
+
+//Регистрация
+
+//Авторизация
+const jwtDecode = token => { 
+    try{
+        return JSON.parse(atob(token.split('.')[1]));
+    }
+    catch(e){
+        return null;
+    }
+}
+
+function promiseReducer(state = {}, { type, name, status, payload, error }) {
+    if (type === "PROMISE") {
+        return {
+            ...state,
+            [name]: { status, payload, error },
+        };
+    }
+    return state;
+}
+
+function authReducer(state, {type, token}){
+        if (!state) {
+            if (localStorage.authToken) {
+                type = 'AUTH_LOGIN'
+                token = localStorage.authToken
+            } else {
+                return {}
+            }
+        }
+        if (type === 'AUTH_LOGIN') {
+            let auth = jwtDecode(token)
+            if (auth) {
+                localStorage.authToken = token
+                return { token, payload: auth }
+            }
+        }
+        if (type === 'AUTH_LOGOUT') {
+            localStorage.authToken = ''
+            console.log(localStorage.authToken)
+            return {}
+        }
+        return state
+}
+    
+// function authReducer(state, {type, token}){
+//     if (state === undefined && localStorage.authToken){
+//         token = localStorage.authToken;
+//         // token = '';
+//         type = 'AUTH_LOGIN'
+//     }   
+//     if (type === 'AUTH_LOGIN'){
+//             if(jwtDecode(token)){
+//                 localStorage.authToken = token;
+//                 return { token, payload: jwtDecode(token) };
+//             }
+//         }
+//     if (type === 'AUTH_LOGOUT'){
+//         localStorage.authToken = '';
+//         return {}
+//     }
+//     return state || {}
+// }
+
+const store = createStore(combineReducers({
+    promise: promiseReducer,
+    auth: authReducer,
+   }), applyMiddleware(thunk));
+
+export {store, backendURL, gql};

+ 5 - 0
project_marketplace/src/setupTests.js

@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';