Bladeren bron

project-codepen

= 2 jaren geleden
bovenliggende
commit
789287a05c
76 gewijzigde bestanden met toevoegingen van 18677 en 1 verwijderingen
  1. BIN
      .DS_Store
  2. 1 0
      .gitignore
  3. 70 0
      README.md
  4. 0 1
      myproject-codepen
  5. 16838 0
      package-lock.json
  6. 54 0
      package.json
  7. BIN
      public/.DS_Store
  8. BIN
      public/img/.DS_Store
  9. BIN
      public/img/favicon.png
  10. 44 0
      public/index.html
  11. 25 0
      public/manifest.json
  12. 3 0
      public/robots.txt
  13. BIN
      src/.DS_Store
  14. 38 0
      src/App.css
  15. 17 0
      src/App.js
  16. 8 0
      src/App.test.js
  17. 1 0
      src/actions/actionAuthLogin.js
  18. 1 0
      src/actions/actionAuthLogout.js
  19. 8 0
      src/actions/actionFindUser.js
  20. 9 0
      src/actions/actionFullAva.js
  21. 14 0
      src/actions/actionFullLogin.js
  22. 11 0
      src/actions/actionFullRegister.js
  23. 6 0
      src/actions/actionImgFind.js
  24. 4 0
      src/actions/actionLogin.js
  25. 1 0
      src/actions/actionPending.js
  26. 16 0
      src/actions/actionPromise.js
  27. 4 0
      src/actions/actionReg.js
  28. 1 0
      src/actions/actionRejected.js
  29. 1 0
      src/actions/actionResolved.js
  30. 6 0
      src/actions/actionSearch.js
  31. 8 0
      src/actions/actionSetAva.js
  32. 12 0
      src/actions/actionSnippetAdd.js
  33. 10 0
      src/actions/actionSnippetById.js
  34. 6 0
      src/actions/actionSnippetByOwner.js
  35. 6 0
      src/actions/actionSnippetFindByOwner.js
  36. 18 0
      src/actions/actionUploadFile.js
  37. 12 0
      src/actions/gql.js
  38. 138 0
      src/actions/requests.js
  39. BIN
      src/components/.DS_Store
  40. 10 0
      src/components/Ava.js
  41. 15 0
      src/components/Editor.css
  42. 93 0
      src/components/Editor.js
  43. 94 0
      src/components/EditorsPage.css
  44. 151 0
      src/components/EditorsPage.js
  45. 93 0
      src/components/Header.css
  46. 16 0
      src/components/Header.js
  47. 16 0
      src/components/Logo.js
  48. 47 0
      src/components/Routers.js
  49. 15 0
      src/components/User.js
  50. 22 0
      src/components/UserHeader.js
  51. 23 0
      src/helpers/SelectFontSize.js
  52. 27 0
      src/helpers/SelectMode.js
  53. 24 0
      src/helpers/SelectTheme.js
  54. 69 0
      src/index.css
  55. 17 0
      src/index.js
  56. BIN
      src/logo.png
  57. 1 0
      src/logo.svg
  58. BIN
      src/pages/.DS_Store
  59. 29 0
      src/pages/Cabinet.js
  60. 18 0
      src/pages/FormReg.js
  61. 45 0
      src/pages/FormUpload.css
  62. 20 0
      src/pages/FormUpload.js
  63. 16 0
      src/pages/LoginForm.js
  64. 105 0
      src/pages/LoginReg.css
  65. 143 0
      src/pages/Main.css
  66. 19 0
      src/pages/Main.js
  67. 0 0
      src/pages/Project.js
  68. 0 0
      src/pages/Projects.js
  69. 57 0
      src/pages/Search.js
  70. 16 0
      src/pages/WorkPage.js
  71. 31 0
      src/reducers/auth.js
  72. 23 0
      src/reducers/index.js
  73. 13 0
      src/reducers/promise.js
  74. 13 0
      src/reportWebVitals.js
  75. 5 0
      src/setupTests.js
  76. BIN
      src/user.png

BIN
.DS_Store


+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+node_modules

+ 70 - 0
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 the browser.
+
+The page will reload if you make edits.\
+You will 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)

+ 0 - 1
myproject-codepen

@@ -1 +0,0 @@
-Subproject commit ef0fe5af27350e189e4ddc85f5eee36b90155be0

File diff suppressed because it is too large
+ 16838 - 0
package-lock.json


+ 54 - 0
package.json

@@ -0,0 +1,54 @@
+{
+  "name": "myproject",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@testing-library/jest-dom": "^5.14.1",
+    "@testing-library/react": "^11.2.7",
+    "@testing-library/user-event": "^12.8.3",
+    "ace-builds": "^1.4.12",
+    "base-64": "^1.0.0",
+    "base64-arraybuffer": "^1.0.1",
+    "codemirror": "^5.62.3",
+    "react": "^17.0.2",
+    "react-ace": "^9.4.4",
+    "react-avatar-edit": "^1.1.0",
+    "react-codemirror2": "^7.2.1",
+    "react-dom": "^17.0.2",
+    "react-dropzone": "^11.4.2",
+    "react-redux": "^7.2.5",
+    "react-router-dom": "^5.3.0",
+    "react-scripts": "4.0.3",
+    "redux": "^4.1.1",
+    "redux-thunk": "^2.3.0",
+    "web-vitals": "^1.1.2"
+  },
+  "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"
+    ]
+  },
+  "proxy": "http://snippet.asmer.fs.a-level.com.ua/",
+  "devDependencies": {
+    "webpack": "^4.44.2"
+  }
+}

BIN
public/.DS_Store


BIN
public/img/.DS_Store


BIN
public/img/favicon.png


+ 44 - 0
public/index.html

@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <!-- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> -->
+    <link rel="shorttut icon" href="img/favicon.png" type="image/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%/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`.
+    -->
+    <title>Codepen</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>

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

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

BIN
src/.DS_Store


+ 38 - 0
src/App.css

@@ -0,0 +1,38 @@
+/* .App {
+  text-align: center;
+} */
+
+.App-logo {
+  height: 40vmin;
+  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);
+  }
+}

+ 17 - 0
src/App.js

@@ -0,0 +1,17 @@
+import React from 'react';
+import { Provider }   from 'react-redux';
+import ConnectedRouts from './components/Routers';
+import store from './reducers';
+import './App.css';
+
+function App() {
+    return (
+        <>
+            <Provider store = {store}>
+            <ConnectedRouts/>
+            </Provider>
+        </>
+    )
+}
+    
+export default App;

+ 8 - 0
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();
+});

+ 1 - 0
src/actions/actionAuthLogin.js

@@ -0,0 +1 @@
+export const actionAuthLogin = token => ({type: 'LOGIN', token});

+ 1 - 0
src/actions/actionAuthLogout.js

@@ -0,0 +1 @@
+export const actionAuthLogout = () => ({type: 'LOGOUT'});

+ 8 - 0
src/actions/actionFindUser.js

@@ -0,0 +1,8 @@
+import { actionPromise } from "./actionPromise";
+import { userFind  } from "./requests";
+
+export const actionFindUser = () => async (dispatch, getState) => {
+    return await dispatch(
+      actionPromise("findUser", userFind(getState()?.a?.payload?.sub?.id))
+    );
+};

+ 9 - 0
src/actions/actionFullAva.js

@@ -0,0 +1,9 @@
+import { actionUploadFile } from "./actionUploadFile";
+import { actionSetAva } from "./actionSetAva";
+import { actionFindUser } from "./actionFindUser";
+
+export const actionFullAva = (file) => async (dispatch) => {
+    let result = await dispatch(actionUploadFile(file))
+    await dispatch(actionSetAva(result._id))
+    await dispatch(actionFindUser())
+};

+ 14 - 0
src/actions/actionFullLogin.js

@@ -0,0 +1,14 @@
+import { actionLogin } from "./actionLogin";
+import { actionAuthLogin } from "./actionAuthLogin";
+import { actionFindUser } from "./actionFindUser";
+
+export const actionFullLogin = (login, password) => async(dispatch) => {
+    let result = await dispatch(actionLogin(login, password))
+    if (result !== null){
+        dispatch(actionAuthLogin(result))
+    } else {
+        alert ('That user doesn’t exist!')
+        localStorage.clear()
+    };
+    await dispatch(actionFindUser());
+};

+ 11 - 0
src/actions/actionFullRegister.js

@@ -0,0 +1,11 @@
+import { actionReg } from "./actionReg";
+import { actionFullLogin } from "./actionFullLogin";
+
+export const actionFullRegister = (login, password) => async(dispatch) => {
+    let result = await dispatch(actionReg(login, password))
+    if(result?.data?.createUser !== null) {
+        dispatch(actionFullLogin(login, password))
+    } else {
+        alert('Such a user already exists!')
+    };
+};

+ 6 - 0
src/actions/actionImgFind.js

@@ -0,0 +1,6 @@
+import {actionPromise} from "./actionPromise";
+import {imgFind} from "./requests"
+
+export const actionImgFind = () => async (dispatch) => {
+    return await dispatch(actionPromise("img", imgFind()));
+};

+ 4 - 0
src/actions/actionLogin.js

@@ -0,0 +1,4 @@
+import { actionPromise } from "./actionPromise";
+import { log } from "./requests";
+
+export const actionLogin = (login, password) => actionPromise("login", log(login, password));

+ 1 - 0
src/actions/actionPending.js

@@ -0,0 +1 @@
+export const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name});

+ 16 - 0
src/actions/actionPromise.js

@@ -0,0 +1,16 @@
+import { actionPending } from "./actionPending";
+import { actionResolved } from "./actionResolved";
+import { actionRejected } from "./actionRejected";
+
+export const actionPromise = (name, promise) => 
+    async dispatch => {
+        dispatch(actionPending(name))
+        try{
+            let payload = await promise
+            dispatch(actionResolved(name, payload)) 
+            return payload
+        }
+        catch(error){
+             dispatch(actionRejected(name, error))
+        };
+    };

+ 4 - 0
src/actions/actionReg.js

@@ -0,0 +1,4 @@
+import { actionPromise } from "./actionPromise";
+import { reg } from "./requests";
+
+export const actionReg = (login, password) => actionPromise("reg", reg(login, password));

+ 1 - 0
src/actions/actionRejected.js

@@ -0,0 +1 @@
+export const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error});

+ 1 - 0
src/actions/actionResolved.js

@@ -0,0 +1 @@
+export const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload});

+ 6 - 0
src/actions/actionSearch.js

@@ -0,0 +1,6 @@
+import { search } from "./requests";
+import { actionPromise } from "./actionPromise";
+
+export const actionSearch = (string) => async (dispatch) => {
+    return await dispatch(actionPromise('searchSnippet', search(string)))
+};

+ 8 - 0
src/actions/actionSetAva.js

@@ -0,0 +1,8 @@
+import { actionPromise } from "./actionPromise";
+import { setAva } from "./requests";
+
+export const actionSetAva = (id) => async (dispatch, getState) => {
+    return await dispatch(
+      actionPromise("setAva", setAva(getState()?.a?.payload?.sub?.id, id))
+    );
+};

+ 12 - 0
src/actions/actionSnippetAdd.js

@@ -0,0 +1,12 @@
+import { actionPromise } from "./actionPromise";
+import { snippetAdd } from "./requests";
+
+export const actionSnippetAdd =
+(title, description, files, idSnippet) => async (dispatch) => {
+  return await dispatch(
+    actionPromise(
+      "addSnippet",
+      snippetAdd(title, description, files, idSnippet)
+    )
+  );
+};

+ 10 - 0
src/actions/actionSnippetById.js

@@ -0,0 +1,10 @@
+import { actionPromise } from "./actionPromise";
+import { snippetById } from "./requests";
+
+export const actionSnippetById =
+    (id) => async (dispatch) => {
+        return await dispatch(
+            actionPromise("findSnippetById",
+            snippetById(id))
+        )
+    };

+ 6 - 0
src/actions/actionSnippetByOwner.js

@@ -0,0 +1,6 @@
+import { actionPromise } from "./actionPromise";
+import { snippetByOwner } from "./requests";
+
+export const actionSnippetFindByOwner = (id) => async (dispatch) => {
+    return await dispatch(actionPromise("findSnippet", snippetByOwner(id)));
+};

+ 6 - 0
src/actions/actionSnippetFindByOwner.js

@@ -0,0 +1,6 @@
+import { actionPromise } from "./actionPromise";
+import { snippetByOwner } from "./requests";
+
+export const actionSnippetFindByOwner = (id) => async (dispatch) => {
+    return await dispatch(actionPromise("findSnippet", snippetByOwner(id)))
+};

+ 18 - 0
src/actions/actionUploadFile.js

@@ -0,0 +1,18 @@
+import { actionPromise } from "./actionPromise";
+
+export const actionUploadFile = (files) => async (dispatch) => {
+    let fd = new FormData();
+    fd.append("photo", files);
+    return await dispatch(
+      actionPromise(
+        "upload",
+        fetch("/upload", {
+          method: "POST",
+          headers: localStorage.authToken
+            ? { Authorization: "Bearer " + localStorage.authToken }
+            : {},
+          body: fd,
+        }).then((res) => res.json())
+      )
+    );
+};

+ 12 - 0
src/actions/gql.js

@@ -0,0 +1,12 @@
+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())
+
+export const gql = getGQL("/graphql");

+ 138 - 0
src/actions/requests.js

@@ -0,0 +1,138 @@
+import { gql } from "./gql";
+
+export const log = async(login, password) => {
+    let query = `query login($login:String!, $password:String!) {
+        login(login: $login, password: $password)
+    }`
+
+    let variables = {"login":login, "password":password}
+
+    let token = await gql(query, variables)
+    localStorage.authToken = token.data.login
+    console.log(token)
+    return token.data.login
+};
+
+export const reg = async(login, password) => {
+    let query = `mutation reg($login:String!, $password:String!) {
+    createUser(
+        login: $login,
+        password: $password
+
+    ){
+       _id
+    }}`
+    
+    let variables = {"login":login, "password":password}
+
+    let res = await gql(query, variables)
+    return res
+};
+
+export const imgFind = async () => {
+    return await gql(`query imgFind{
+      ImageFind(query:"[{}]"){
+        url owner{
+          nick
+        }
+      }
+    }`);
+};
+
+export const userFind = (_id) => {
+    return gql(
+      `query userOne($query:String) {
+            UserFindOne(query:$query){
+                _id avatar{
+                url
+                }
+            }
+        }`,
+      { query: JSON.stringify([{ _id }]) }
+    );
+  };
+
+export const setAva = async (idUser, id) => {
+    let query = `mutation setAvatar($idUser:String , $idAvatar:ID){ 
+      UserUpsert(user:{_id: $idUser, avatar: {_id: $idAvatar}}){
+          _id, avatar{
+              url
+          }
+      }
+  }`
+
+    let variables = { idUser: idUser, idAvatar: id }
+  
+    let res = await gql(query, variables)
+    return res
+};
+
+export const snippetAdd = async (title, description, files , idSnippet) => {
+    let query = `mutation newSnippet($snippet:SnippetInput) {
+      SnippetUpsert(snippet:$snippet){
+        _id
+      }
+    }`;
+  
+    let qVariables = { snippet: { title, description, files, _id: idSnippet }  };
+  
+    let res = await gql(query, qVariables);
+    return res;
+};
+
+export const snippetById = async(id) => {
+    let query = `query snippetFind($query:String){
+      SnippetFind(query:$query){
+                        owner{
+                          _id 
+                        }
+            title description _id files {
+             type text name
+           }
+        }
+  }`
+
+    let variables = {query: JSON.stringify([{_id: id }])}
+  
+    let res = gql(query, variables)
+    return res
+};
+
+export const snippetByOwner = async (id) => {
+    let query = `query snippetFind($query:String){
+        SnippetFind(query:$query){
+                          owner{
+                            _id 
+                          }
+              title description _id files {
+               type text name
+            }
+        }
+  }`
+
+    let variables = {query: JSON.stringify([{owner: id } , {sort:[{_id: -1}]}])}
+  
+    let res = gql(query, variables)
+    return res
+};
+
+export const search = async (string) => {
+    return gql(
+      `query snippetFind($query:String){
+        SnippetFind(query:$query){
+          owner {
+            _id login
+          }
+              title description _id files {
+               type text name
+             }
+          }
+    }`,
+      { query: JSON.stringify([
+        {
+            $or: [{title: `/${string.trim().split(" ").join('|')}/`},{description: `/${string.trim().split(" ").join('|')}/`}] 
+        },
+        {
+          sort: [{title: 1}]}]) }
+    )
+};

BIN
src/components/.DS_Store


+ 10 - 0
src/components/Ava.js

@@ -0,0 +1,10 @@
+import React from "react";
+import { connect } from "react-redux";
+import "./Header.css";
+import icon from "../user.png";
+
+function AvaLogo({ link }) {
+    return <img src= {link ? "http://localhost:3000/" + link : icon} className = 'avatar' alt='ava'></img>
+}
+
+export const ConnectedAvaLogo = connect(state => ({link: state?.p?.findUser?.payload?.data?.UserFindOne?.avatar?.url}))(AvaLogo);

+ 15 - 0
src/components/Editor.css

@@ -0,0 +1,15 @@
+.all_editors {
+    display: inline-block;
+    width: 464px;
+    padding-left: 10px;
+    color: rgb(196, 192, 192);
+}
+
+select {
+    margin: 5px;
+    font-size: 17px;
+}
+
+.editor {
+    margin-top: 5px;
+}

+ 93 - 0
src/components/Editor.js

@@ -0,0 +1,93 @@
+import AceEditor from "react-ace";
+import "ace-builds/webpack-resolver";
+import "ace-builds/src-noconflict/mode-html";
+import "ace-builds/src-noconflict/mode-css";
+import "ace-builds/src-noconflict/mode-javascript";
+import "ace-builds/src-noconflict/mode-java";
+import "ace-builds/src-noconflict/mode-python";
+import "ace-builds/src-noconflict/mode-xml";
+import "ace-builds/src-noconflict/mode-sass";
+import "ace-builds/src-noconflict/mode-ruby";
+import "ace-builds/src-noconflict/mode-markdown";
+import "ace-builds/src-noconflict/mode-mysql";
+import "ace-builds/src-noconflict/mode-json";
+import "ace-builds/src-noconflict/mode-handlebars";
+import "ace-builds/src-noconflict/mode-golang";
+import "ace-builds/src-noconflict/mode-csharp";
+import "ace-builds/src-noconflict/mode-elixir";
+import "ace-builds/src-noconflict/mode-typescript";
+import "ace-builds/src-noconflict/ext-language_tools";
+import "ace-builds/src-noconflict/theme-monokai";
+import "ace-builds/src-noconflict/theme-github";
+import "ace-builds/src-noconflict/theme-tomorrow";
+import "ace-builds/src-noconflict/theme-kuroir";
+import "ace-builds/src-noconflict/theme-twilight";
+import "ace-builds/src-noconflict/theme-xcode";
+import "ace-builds/src-noconflict/theme-textmate";
+import "ace-builds/src-noconflict/theme-terminal";
+import "ace-builds/src-noconflict/theme-solarized_dark";
+import "ace-builds/src-noconflict/theme-solarized_light";
+import { SelectMode } from "../helpers/SelectMode";
+import { SelectTheme } from "../helpers/SelectTheme";
+import { SelectFontSize } from "../helpers/SelectFontSize";
+import "./Editor.css";
+
+export const Editor = ({
+  data = { type: "", name: "", text: "", index: "" },
+  onChange,
+  theme,
+  font,
+}) => {
+  const changeTheme = (theme) => onChange({ theme });
+
+  const changeFont = (font) => onChange({ font });
+  return (
+    <div className="all_editors">
+      {"Mode: "}
+      <SelectMode
+        onChange={(type) =>
+          onChange({ type, text: data.text, name: data.name })
+        }
+        value={data.type}
+      />
+      <br />
+      {"Theme: "}
+      <SelectTheme onChange={changeTheme} value={data.theme} />
+      <br />
+      {"FontSize: "}
+      <SelectFontSize onChange={changeFont} value={data.font} />
+      <br />
+      {"Name of editor: "}
+      <input
+        placeholder="Enter a name for your editor"
+        type="text"
+        value={data.name}
+        onChange={(e) =>
+          onChange({ type: data.type, text: data.text, name: e.target.value })
+        }
+      />
+      <AceEditor
+        className="editor"
+        value={data.text}
+        onChange={(text) =>
+          onChange({ type: data.type, text, name: data.name })
+        }
+        placeholder="Your Code"
+        mode={data.type}
+        name={data.name}
+        theme={theme}
+        fontSize={Number(font)}
+        showPrintMargin={true}
+        showGutter={true}
+        highlightActiveLine={true}
+        setOptions={{
+          enableBasicAutocompletion: true,
+          enableLiveAutocompletion: true,
+          enableSnippets: true,
+          showLineNumbers: true,
+          tabSize: 2,
+        }}
+      />
+    </div>
+  );
+};

+ 94 - 0
src/components/EditorsPage.css

@@ -0,0 +1,94 @@
+body {
+    margin-top: 20px;
+}
+
+.button_plus {
+    display: flex;
+    justify-content: center;
+    cursor: pointer;
+    margin-left: 47.2%;
+    margin-bottom: 20px;
+    width: 6em;
+    height: 2.5em;
+    cursor: pointer;
+    margin-top: 7px;
+    line-height: 2.4em;
+    vertical-align: middle;
+    font-weight: 550;
+    text-align: center;
+    text-decoration: none;
+    user-select: none;
+    color: rgb(41, 14, 66);
+    outline: none;
+    border: 1px solid rgba(110,121,128,.8);
+    border-top-color: rgba(0,0,0,.3);
+    border-radius: 5px;
+    background: rgb(206, 220, 231) linear-gradient(rgb(206,220,231), rgb(106, 70, 148));
+    box-shadow:
+     0 -1px rgba(10,21,28,.9) inset,
+     0 1px rgba(255,255,255,.5) inset;
+}
+
+.button_plus:hover {
+    background: linear-gradient(#baa7cc, #9285b4);
+}
+
+.pane {
+    background-color:white;
+    width: 99%;
+    height: 300px;
+    margin-left: 10px;
+    margin-bottom: 30px;
+    border: dotted;
+}
+
+.input_title {
+    margin-left: 37.5%;
+    width: 400px;
+}
+
+.text_desc {
+    margin-left: 37.5%;
+    width: 400px;
+}
+
+.text {
+    margin-left: 37.5%;
+    font-size: 17px;
+    color: rgb(197, 193, 226);
+}
+
+.button_submit {
+    cursor: pointer;
+    margin-bottom: 90px;
+    margin-left: 46%;
+    width: 10em;
+    height: 2.5em;
+    cursor: pointer;
+    margin-top: 30px;
+    line-height: 2.4em;
+    vertical-align: middle;
+    font-weight: 550;
+    text-align: center;
+    text-decoration: none;
+    user-select: none;
+    color: rgb(41, 14, 66);
+    outline: none;
+    border: 1px solid rgba(110,121,128,.8);
+    border-top-color: rgba(0,0,0,.3);
+    border-radius: 5px;
+    background: rgb(206, 220, 231) linear-gradient(rgb(206,220,231), rgb(106, 70, 148));
+    box-shadow:
+     0 -1px rgba(10,21,28,.9) inset,
+     0 1px rgba(255,255,255,.5) inset;
+}
+
+.button_submit:hover {
+    background: linear-gradient(#baa7cc, #9285b4);
+} 
+
+.styles {
+   padding-left: 10px;
+   color: rgb(196, 192, 192);
+   padding-bottom: 30px;
+}

+ 151 - 0
src/components/EditorsPage.js

@@ -0,0 +1,151 @@
+import { useState, useEffect } from "react";
+import { Editor } from "../components/Editor";
+import { SelectTheme } from "../helpers/SelectTheme";
+import { SelectFontSize } from "../helpers/SelectFontSize";
+import "./EditorsPage.css";
+
+const datas = [
+  {
+    type: "html",
+    name: "",
+    text: `<!DOCTYPE html> 
+    <h1 id="codepen">Welcome to the Codepen</h1>`,
+  },
+  {
+    type: "css",
+    name: "",
+    text: `#codepen {
+    color: blue;
+    font-size: 25px;
+    font-family: Times New Roman;
+}`,
+  },
+  {
+    type: "javascript",
+    name: "",
+    text: `let btn = document.createElement('button');
+    btn.innerHTML = 'Change color';
+    btn.style.color = 'red';
+    btn.style.fontSize = '20px';
+    document.body.append(btn);
+    btn.style.cursor = 'pointer';
+    btn.onclick = () => {
+    document.getElementById('codepen').style.color = 'red'};`,
+  },
+];
+
+export const EditorsPage = (
+  data = { type: "", name: "", text: "", index: "", theme: "", font: "" },
+  onSave,
+  onChange
+) => {
+  const [editors, setEditors] = useState(datas);
+  const [title, setTitle] = useState("");
+  const [description, setDescription] = useState("");
+  const [srcDoc, setSrcDoc] = useState("");
+
+  useEffect(() => {
+    const timeout = setTimeout(() => {
+      let html, css, javascript;
+      editors.forEach((e) => {
+        if (e.type === "html") html = e.text;
+        if (e.type === "css") css = e.text;
+        if (e.type === "javascript") javascript = e.text;
+      });
+      setSrcDoc(`
+            <html>
+              <body>${html}</body>
+              <style>${css}</style>
+              <script>${javascript}</script>
+            </html>
+          `);
+    }, 250);
+
+    return () => clearTimeout(timeout);
+  }, [editors]);
+
+  const changeTheme = (theme) => onChange({ theme });
+
+  const changeFont = (font) => onChange({ font });
+
+  return (
+    <>
+      <div className="styles">
+        {"Theme: "}
+        <SelectTheme onChange={() => changeTheme} value={data.theme} />
+        <br />
+        {"FontSize: "}
+        <SelectFontSize onChange={() => changeFont} value={data.font} />
+        <br />
+      </div>
+      {editors.map((data, index) => {
+        return (
+          <Editor
+            data={data}
+            key={index}
+            theme='monokai'
+            font={16}
+            onChange={(newData) => {
+              let editor = [...editors];
+              editor.splice(index, 1, newData);
+              setEditors(editor);
+            }}
+          />
+        );
+      })}
+
+      <button
+        className="button_plus"
+        onClick={(newArray) => {
+          let editors2 = [...editors];
+          editors2.push(newArray);
+          setEditors(editors2);
+        }}
+      >
+        +
+      </button>
+
+      <button
+        className="button_plus"
+        onClick={(newArray) => {
+          let editors2 = [...editors];
+          editors2.pop(newArray);
+          setEditors(editors2);
+        }}
+      >
+        -
+      </button>
+
+      <div className="pane">
+        <iframe
+          srcDoc={srcDoc}
+          title="output"
+          sandbox="allow-scripts"
+          frameBorder="0"
+          width="100%"
+          height="100%"
+        />
+      </div>
+      <p className="text">Name of your project: </p>
+      <input
+        className="input_title"
+        placeholder="Name of your project"
+        value={title}
+        onChange={(e) => setTitle(e.target.value)}
+      />
+      <p className="text">Description: </p>
+      <textarea
+        className="text_desc"
+        placeholder="Description"
+        value={description}
+        onChange={(e) => setDescription(e.target.value)}
+      />
+      <button
+        className="button_submit"
+        onClick={() => onSave(title, description, editors)}
+      >
+        Submit this Editor
+      </button>
+    </>
+  );
+};

+ 93 - 0
src/components/Header.css

@@ -0,0 +1,93 @@
+body{
+    margin:0;
+}
+  
+nav {
+    display: flex;
+    margin-bottom: 30px;
+    padding: 5px;
+    background: linear-gradient(#a088bd, #c7a9aa);
+}
+
+.img_logo {
+    width: 100px;
+    margin-right: 1035px;
+    border: 1.5px solid rgb(72, 70, 75);
+    margin-top: 10px;
+    margin-left: 15px;
+}
+
+.img_logo:hover {
+    border: 1.5px solid rgb(231, 224, 224);
+    width: 99px;
+}
+
+.logaut a {
+    color: rgb(23, 6, 39);
+    text-decoration: none;
+    font-size: 20px;
+}
+
+.logaut button {
+    position: relative;
+    display: inline-block;
+    width: 6.5em;
+    height: 2.5em;
+    cursor: pointer;
+    margin-top: 7px;
+    margin-bottom: 5px;
+    vertical-align: middle;
+    font-weight: 550;
+    text-align: center;
+    text-decoration: none;
+    user-select: none;
+    color: rgb(41, 14, 66);
+    outline: none;
+    border: 1px solid rgba(110,121,128,.8);
+    border-top-color: rgba(0,0,0,.3);
+    border-radius: 5px;
+    background: rgb(206, 220, 231) linear-gradient(rgb(206,220,231), rgb(106, 70, 148));
+    box-shadow:
+     0 -1px rgba(10,21,28,.9) inset,
+     0 1px rgba(255,255,255,.5) inset;
+}
+
+.logaut button:hover {
+    background: linear-gradient(#baa7cc, #9285b4);
+} 
+
+.avatar {
+   margin-top: 25px;
+   margin-right: 10px;
+   width: 70px;
+   height: 70px;
+}
+
+.button_search {
+    cursor: pointer;
+    margin-top: 50px;
+    margin-right: 10px;
+    position: relative;
+    display: inline-block;
+    width: 6.5em;
+    height: 2.5em;
+    margin-bottom: 5px;
+    vertical-align: middle;
+    font-weight: 550;
+    text-align: center;
+    text-decoration: none;
+    user-select: none;
+    color: rgb(41, 14, 66);
+    outline: none;
+    border: 1px solid rgba(110,121,128,.8);
+    border-top-color: rgba(0,0,0,.3);
+    border-radius: 5px;
+    background: rgb(206, 220, 231) linear-gradient(rgb(206,220,231), rgb(106, 70, 148));
+    box-shadow:
+     0 -1px rgba(10,21,28,.9) inset,
+     0 1px rgba(255,255,255,.5) inset;
+}
+
+.button_search:hover {
+    background: linear-gradient(#baa7cc, #9285b4);
+}

+ 16 - 0
src/components/Header.js

@@ -0,0 +1,16 @@
+import ImgLogo from "./Logo";
+import ConnectedNick from "./UserHeader";
+import { ConnectFormUpload } from "../pages/FormUpload";
+import "./Header.css";
+
+const Header = () => {
+    return (
+    <nav className="navbar_header">
+        <ImgLogo />
+        <ConnectFormUpload />
+        <ConnectedNick/>
+    </nav>
+    )
+}
+
+export default Header;

+ 16 - 0
src/components/Logo.js

@@ -0,0 +1,16 @@
+import React from "react";
+import logo from "../logo.png";
+import "./Header.css";
+
+function ImgLogo() {
+    return <>
+    <a href="/work"> <img src={logo} alt="logo" className='img_logo'/></a>
+    <a href="/search">
+        <button className='button_search'>
+            Search
+        </button>
+    </a>
+    </>
+}
+  
+export default ImgLogo;

+ 47 - 0
src/components/Routers.js

@@ -0,0 +1,47 @@
+import React from "react";
+import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom";
+import * as createHistory from "history";
+import { connect } from "react-redux";
+import { actionFullLogin } from "../actions/actionFullLogin";
+import { actionFullRegister } from "../actions/actionFullRegister";
+import { LoginForm } from "../pages/LoginForm";
+import { FormReg } from "../pages/FormReg";
+import { Main } from "../pages/Main";
+import { Home } from "../pages/WorkPage";
+import { Cabinet } from "../pages/Cabinet";
+import ConnectFormSearch from "../pages/Search";
+import CProjects from "../pages/Projects";
+import ConnectedProject from "../pages/Project";
+
+const history = createHistory.createBrowserHistory();
+
+const ConnectLoginForm = connect(null, {onLogin:actionFullLogin}) (LoginForm);
+
+const ConnectFormReg = connect(null, {FormReg:actionFullRegister}) (FormReg);
+
+const Routs = ({token}) => {
+    return (
+        <div className="App">
+          <Router history = {history}>
+            {token && <Switch>
+                <Redirect from="/login" to="/work" />
+                <Redirect from="/reg" to="/work" />
+                <Route exact path='/work' component={Home} />
+                <Route path='/cabinet' component={Cabinet}/>
+                <Route path='/projects' component={CProjects}/>
+                <Route path='/project' component={ConnectedProject}/>
+                <Route path='/search' component={ConnectFormSearch}/>
+            </Switch>}
+            {!token && <Switch>
+                <Route path='/login' component={ConnectLoginForm}/>
+                <Route path='/reg' component={ConnectFormReg}/>
+                <Route path='/' component={Main}/>
+            </Switch>}
+          </Router>
+        </div>
+    )
+}
+
+const ConnectedRouts = connect(state =>({token: state.a.token}), null)(Routs);
+
+export default ConnectedRouts;

+ 15 - 0
src/components/User.js

@@ -0,0 +1,15 @@
+import { connect } from "react-redux";
+
+const Nick = ({nick}) => {
+    return (
+        <>
+        <div>
+            <p>{nick}</p>
+        </div>
+        
+        </>
+    )
+}
+    
+const ConnectNickName = connect(state => ({nick: state?.a?.payload?.sub?.login}))(Nick);
+export default ConnectNickName;

+ 22 - 0
src/components/UserHeader.js

@@ -0,0 +1,22 @@
+import { connect } from "react-redux";
+import { actionAuthLogout } from "../actions/actionAuthLogout";
+import "./Header.css";
+
+const NickName = ({ nick, onLogOut }) => {
+    return (
+      <>
+        <div className='logaut'>
+            <a href = '/cabinet'>{nick}</a>
+            <a href="/cabinet">
+                <button className='cabinet'>
+                    My Cabinet
+                </button>
+            </a>
+            <a href = '/'><button onClick = {() => (onLogOut())}>Log out</button></a>
+        </div>
+      </>
+    )
+}
+    
+const ConnectedNick = connect((state) => ({nick: state?.a?.payload?.sub?.login, logedIn: state.a.token}),{onLogOut:actionAuthLogout})(NickName);
+export default ConnectedNick;

+ 23 - 0
src/helpers/SelectFontSize.js

@@ -0,0 +1,23 @@
+const fonts = {
+  12: 12,
+  14: 14,
+  16: 16,
+  18: 18,
+  20: 20,
+  24: 24,
+  28: 28,
+  32: 32,
+  40: 40,
+};
+
+export const SelectFontSize = ({ listObj = fonts, onChange, value }) => {
+  return (
+    <select value={value} onChange={(e) => onChange(e.target.value)}>
+      {Object.entries(listObj).map(([value, text]) => (
+        <option value={value} key={value}>
+          {text}
+        </option>
+      ))}
+    </select>
+  );
+};

+ 27 - 0
src/helpers/SelectMode.js

@@ -0,0 +1,27 @@
+const types = {
+    html: "html",
+    css: "css",
+    javascript: "javascript",
+    java: "java",
+    python: "python",
+    ruby: "ruby",
+    xml: "xml",
+    sass: "sass",
+    json: "json",
+    typescript: "typescript",
+    markdown: "markdown",
+    mysql: "mysql",
+    handlebars: "handlebars",
+    golang: "golang",
+    csharp: "csharp",
+    elixir: "elixir"
+}
+
+export const SelectMode = ({listObj=types, onChange, value}) => 
+    {
+        return <select value = {value} onChange={e => onChange(e.target.value)}>
+            {Object.entries(listObj).map(([value, text]) => <option value={value} key={value}>
+                {text}
+            </option>)}
+        </select>
+    }

+ 24 - 0
src/helpers/SelectTheme.js

@@ -0,0 +1,24 @@
+const themes = {
+  textmate: "textmate",
+  monokai: "monokai",
+  xcode: "xcode",
+  twilight: "twilight",
+  terminal: "terminal",
+  github: "github",
+  tomorrow: "tomorrow",
+  kuroir: "kuroir",
+  solarized_dark: "solarized_dark",
+  solarized_light: "solarized_light",
+};
+
+export const SelectTheme = ({ listObj = themes, onChange, value }) => {
+  return (
+    <select value={value} onChange={(e) => onChange(e.target.value)}>
+      {Object.entries(listObj).map(([value, text]) => (
+        <option value={value} key={value}>
+          {text}
+        </option>
+      ))}
+    </select>
+  );
+};

+ 69 - 0
src/index.css

@@ -0,0 +1,69 @@
+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;
+}
+
+.top-pane {
+    background-color: hsl(225, 6%, 25%);
+}
+  
+.pane {
+    height: 50vh;
+    display: flex;
+}
+
+.editor-container {
+    flex-grow: 1;
+    flex-basis: 0;
+    display: flex;
+    flex-direction: column;
+    padding: .5rem;
+    background-color: hsl(225, 6%, 25%);
+}
+  
+.editor-container.collapsed {
+    flex-grow: 0;
+}
+
+.editor-container.collapsed .CodeMirror-scroll {
+    position: absolute;
+    overflow: hidden !important;
+}
+  
+.expand-collapse-btn {
+    margin-left: .5rem;
+    background: none;
+    border: none;
+    color: white;
+    cursor: pointer;
+}
+  
+.editor-title {
+    display: flex;
+    justify-content: space-between;
+    background-color: hsl(225, 6%, 13%);
+    color: white;
+    padding: .5rem .5rem .5rem 1rem;
+    border-top-right-radius: .5rem;
+    border-top-left-radius: .5rem;
+}
+  
+.CodeMirror {
+    height: 100% !important;
+}
+  
+.code-mirror-wrapper {
+    flex-grow: 1;
+    border-bottom-right-radius: .5rem;
+    border-bottom-left-radius: .5rem;
+    overflow: hidden;
+}

+ 17 - 0
src/index.js

@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+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();

BIN
src/logo.png


File diff suppressed because it is too large
+ 1 - 0
src/logo.svg


BIN
src/pages/.DS_Store


+ 29 - 0
src/pages/Cabinet.js

@@ -0,0 +1,29 @@
+import React from "react";
+import ConnectedNick from "../components/UserHeader";
+import { ConnectFormUpload } from "./FormUpload";
+import "./Main.css";
+
+export const Cabinet = () => {
+  return (
+    <>
+        <div className="card">
+            <div className='nick_style'>
+                <ConnectFormUpload  />
+                <ConnectedNick />
+            </div>
+            <div className="cart_1">
+                <a href="/work">
+                    <button className='btn'>
+                        Work Page
+                    </button>
+                </a>
+                <a href="/projects">
+                    <button className='btn'>
+                        My Projects
+                    </button>
+                </a>
+            </div>
+        </div>
+    </>
+  );
+};

+ 18 - 0
src/pages/FormReg.js

@@ -0,0 +1,18 @@
+import React, {useState} from "react";
+import "./LoginReg.css";
+
+export const FormReg = ({FormReg}) => {
+    const [login, setLogin] = useState('')
+    const [password, setPassword] = useState('')
+    const [passwordValid, setPasswordValid] = useState('')
+    return (
+        <div className='login'>
+        <h3>Registration</h3>
+        <input className='u'type ='login' placeholder='Username' value = {login} onChange = {e => setLogin(e.target.value)}/>
+        <input className='p' type ='password' placeholder='Password' value = {password} onChange = {e => setPassword(e.target.value)} />
+        <input className='p' type ='password' placeholder='RepeatYourPassword' value = {passwordValid} onChange = {e => setPasswordValid(e.target.value)} />
+        <button type="submit" className='btn btn-primary btn-block btn-large' disabled = {!login || !password || (password !== passwordValid)} onClick = {() => FormReg(login, password)}>Remember me. </button>
+        <button className='main_page'><a href = '/'>Main page</a></button>
+        </div>
+    )
+}

+ 45 - 0
src/pages/FormUpload.css

@@ -0,0 +1,45 @@
+.buttons {
+    width: 180px;
+    height: 40px;
+    text-decoration: none;
+    padding-top: 9px;
+    color: #c9b6cf;
+    text-align: center;
+    line-height: 20px;
+    display: block;
+    margin: 200px auto 15px;
+    font: normal 17px arial;
+    cursor: pointer;
+}
+  
+.buttons:not(.active) {
+    box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8), inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3), 0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
+    background-image: linear-gradient(#3b2751, #271739);
+    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
+}
+  
+.buttons:not(.active):hover,
+.buttons:not(.active):focus {
+    transition: color 200ms linear, text-shadow 500ms linear;
+    color: #fff;
+    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
+}
+
+.buttons:not(:hover) {
+      transition: 0.6s;
+}
+
+.back a {
+    text-decoration: none;
+}
+
+.back {
+    display: block;
+    width: 100px;
+    height: 20px;
+    text-decoration: none;
+    color: #c9b6cf;
+    text-align: center;
+    margin: auto;
+    cursor: pointer;
+}

+ 20 - 0
src/pages/FormUpload.js

@@ -0,0 +1,20 @@
+import { connect } from "react-redux";
+import { actionFullAva  } from "../actions/actionFullAva";
+import { useDropzone } from "react-dropzone";
+import { ConnectedAvaLogo } from "../components/Ava";
+import "./FormUpload.css";
+
+const FormUpload = ({onUpload}) => {
+    const {acceptedFiles, getRootProps, getInputProps} = useDropzone()
+    let files = acceptedFiles.map (file => onUpload(file))
+    console.log(files)
+    return (
+        <div {...getRootProps({})}>
+          <input {...getInputProps()} />
+          <ConnectedAvaLogo />
+        </div>
+     
+    );
+};
+
+export const ConnectFormUpload = connect(null, {onUpload:actionFullAva}) (FormUpload);

+ 16 - 0
src/pages/LoginForm.js

@@ -0,0 +1,16 @@
+import { useState } from "react";
+import "./LoginReg.css";
+
+export const LoginForm = ({onLogin}) => {
+    const [login, setLogin] = useState('')
+    const [password, setPassword] = useState('')
+    return (
+        <div className='login'>
+        <h3>Login</h3>
+        <input className='u'type ='login' placeholder='Username' value = {login} onChange = {e => setLogin(e.target.value)}/>
+        <input className='p' type ='password' placeholder='Password' value = {password} onChange = {e => setPassword(e.target.value)} />
+        <button type="submit" className='btn btn-primary btn-block btn-large' disabled = {!login || !password} onClick = {() => onLogin(login, password)}>Let me in. </button>
+        <button className='main_page'><a href = '/'>Main page</a></button>
+        </div>
+    )
+};

File diff suppressed because it is too large
+ 105 - 0
src/pages/LoginReg.css


+ 143 - 0
src/pages/Main.css

@@ -0,0 +1,143 @@
+#body {
+    margin: 0;
+    padding: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    min-height: 90vh;
+    font-family: 'Telefon Black', Sans-Serif;
+}
+
+.box {
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: #170422;
+  max-width: 500px;
+}
+
+.box::before {
+  content: '';
+  position: absolute;
+  top: -2px;
+  left: -2px;
+  right: -2px;
+  bottom: -2px;
+  background: #fff;
+  z-index: -1;
+}
+
+.box::after {
+  content: '';
+  position: absolute;
+  top: -2px;
+  left: -2px;
+  right: -2px;
+  bottom: -2px;
+  background: #fff;
+  z-index: -2;
+  filter: blur(40px);
+}
+
+.box::before,
+.box::after {
+  background: linear-gradient(235deg, #89ff00, #060c21, #00bcd4);
+}
+
+.content {
+  padding: 40px;
+  box-sizing: border-box;
+  color: #ffffff;
+  font-size: 20px;
+}
+
+.btn_form {
+    width: 180px;
+    height: 40px;
+    text-decoration: none;
+    color: #c9b6cf;
+    text-align: center;
+    line-height: 20px;
+    display: block;
+    margin-left: 10px;
+    margin-right: 10px;
+    font: normal 17px arial;
+    cursor: pointer;
+}
+
+.btn_form:not(.active) {
+    box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8), inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3), 0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
+    background-image: linear-gradient(#3b2751, #271739);
+    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
+}
+  
+.btn_form:not(.active):hover,
+.btn_form:not(.active):focus {
+    transition: color 200ms linear, text-shadow 500ms linear;
+    color: #fff;
+    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
+}
+
+.btn_form:not(:hover) {
+      transition: 0.6s;
+}
+
+.card {
+    width: 500px;
+    height: 360px;
+    padding-left: 100px;
+    margin-left: 470px;
+    margin-top: 100px;
+    padding-top: 10px;
+    background: linear-gradient(#f0e2ef, #faf5f5);
+    -webkit-box-shadow: inset 0 0 40px rgb(36, 16, 61);
+    -moz-box-shadow: inset 0 0 40px rgb(37, 12, 58);
+    box-shadow: inset 0 0 40px rgb(26, 10, 48);
+}
+
+.nick_style {
+    display: block;
+    width: 100px;
+    margin-left: 115px;
+}
+
+.block_search {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.btn_search {
+    width: 180px;
+    height: 40px;
+    text-decoration: none;
+    color: #b5a2bb;
+    margin-right: 200px;
+    text-align: center;
+    line-height: 20px;
+    font: normal 17px arial;
+    cursor: pointer;
+    margin-bottom: 10px;
+}
+
+.btn_search:not(.active) {
+    box-shadow: inset 0 1px 1px rgba(111, 55, 125, 0.8), inset 0 -1px 0px rgba(63, 59, 113, 0.2), 0 9px 16px 0 rgba(0, 0, 0, 0.3), 0 4px 3px 0 rgba(0, 0, 0, 0.3), 0 0 0 1px #150a1e;
+    background-image: linear-gradient(#3b2751, #271739);
+    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 -1px 0 #311d47;
+}
+  
+.btn_search:not(.active):hover,
+.btn_search:not(.active):focus {
+    transition: color 200ms linear, text-shadow 500ms linear;
+    color: #fff;
+    text-shadow: 0 0 21px rgba(223, 206, 228, 0.5), 0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
+}
+
+.btn_search:not(:hover) {
+      transition: 0.6s;
+}
+
+.block_button_back {
+    padding-top: 20px;
+}

+ 19 - 0
src/pages/Main.js

@@ -0,0 +1,19 @@
+import "./Main.css";
+
+export const Main = ({history}) => {
+    return (
+        <section id='body'>
+            <button className='btn_form' onClick={ () => history.push('./login') }>Login</button>
+            <div className='box'>
+                <div className='content'>
+                    <h1>Welcome To The CodePen</h1>
+                    <p>The best place to build, test, and discover front-end code.
+                        CodePen is a social development environment for front-end designers and developers.
+                        Build and deploy a website, show off your work, build test cases to learn and debug, and find inspiration.
+                    </p>
+                </div>
+            </div>
+            <button className='btn_form' onClick={ () => history.push('./reg') }>Reristration</button>
+        </section>
+    )
+};

+ 0 - 0
src/pages/Project.js


+ 0 - 0
src/pages/Projects.js


+ 57 - 0
src/pages/Search.js

@@ -0,0 +1,57 @@
+import { actionSearch } from "../actions/actionSearch";
+import { Link } from "react-router-dom";
+import { connect } from "react-redux";
+import { useState } from "react";
+import "./Main.css";
+
+const Search = ({ onSearch, snippets }) => {
+    const [request, setRequest] = useState('');
+    return (
+        <>
+            <Link to="/work">
+                <button>
+                    Back to Main Page
+                </button>
+            </Link>
+            <br />
+            <br />
+            <div>
+                <input
+                    value={request}
+                    onChange={(e) => setRequest(e.target.value)}
+                    type="search"
+                    placeholder="Name of project"
+                    aria-label="Search"
+                />
+                <br />
+                <button onClick={() => onSearch(request)}>
+                    Search
+                </button>
+            </div>
+
+            {snippets?.map((key, index) => (
+                <div>
+                    <div>
+                        <div>
+                            <h3>
+                                {snippets?.[index]?.title || "Project without name"}
+                            </h3>
+                            <p>
+                                <span>Description</span>&nbsp;
+                                {snippets?.[index]?.description || ""}
+                            </p>
+                            <p>{`Owner: ${snippets?.[index]?.owner?.login}`}</p>
+                            <Link to={"/project/" + snippets?.[index]?._id}>
+                                <button>Open project</button>
+                            </Link>
+                        </div>
+                    </div>
+                </div>
+            ))}
+        </>
+    )
+}
+
+const ConnectFormSearch = connect(state => ({ snippets: state?.p?.searchSnippet?.payload?.data?.SnippetFind }), { onSearch: actionSearch })(Search)
+
+export default ConnectFormSearch;

+ 16 - 0
src/pages/WorkPage.js

@@ -0,0 +1,16 @@
+import { EditorsPage } from "../components/EditorsPage";
+import { actionSnippetAdd } from "../actions/actionSnippetAdd";
+import { connect }  from "react-redux";
+import Header from "../components/Header";
+
+export const Home = () => {
+    return (
+    <>
+    <Header />
+    <ConnectEditorsPage />
+    </>
+    )
+}
+
+const ConnectEditorsPage = connect(null, {onSave:actionSnippetAdd}) (EditorsPage);
+export default ConnectEditorsPage;

+ 31 - 0
src/reducers/auth.js

@@ -0,0 +1,31 @@
+import { decode as atob } from "base-64";
+
+let signatureToken = (token) => JSON.parse(atob(token.split(".")[1]));
+
+function authReducer(state, { type, token }) {
+
+    if (state === undefined) {
+        if (localStorage.authToken) {
+            type = "LOGIN"
+            token = localStorage.authToken
+        } else {
+            return {}
+        }
+    }
+
+    if (type === "LOGIN") {
+        console.log('LOGIN')
+        localStorage.authToken = token
+        return {token, payload: signatureToken(token)}
+    }
+
+    if (type === "LOGOUT") {
+        console.log('LOGOUT')
+        localStorage.removeItem("authToken")
+        return {}
+    }
+
+    return state
+}
+
+export default authReducer;

+ 23 - 0
src/reducers/index.js

@@ -0,0 +1,23 @@
+import { createStore, combineReducers, applyMiddleware } from "redux";
+import thunk from "redux-thunk";
+import authReducer from "./auth";
+import promiseReducer from "./promise";
+import { actionSnippetFindByOwner } from "../actions/actionSnippetFindByOwner";
+import { actionFindUser } from "../actions/actionFindUser";
+
+let reducers = combineReducers({
+    p: promiseReducer,
+    a: authReducer
+})
+
+const store = createStore(reducers, applyMiddleware(thunk));
+store.subscribe(() => console.log(store.getState()));
+if (localStorage.authToken) {
+    store.dispatch(actionFindUser());
+    store.dispatch(
+      actionSnippetFindByOwner(store.getState().a.payload.sub.id)
+    );
+  }
+
+
+export default store;

+ 13 - 0
src/reducers/promise.js

@@ -0,0 +1,13 @@
+function promiseReducer(state = {}, { type, status, payload, error, name }) {
+    
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+
+    return state
+}
+
+export default promiseReducer;

+ 13 - 0
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;

+ 5 - 0
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';

BIN
src/user.png