Евгения Акиншина hace 3 años
padre
commit
b8d0840ad9

BIN
public/favicon.ico


BIN
public/logo192.png


BIN
public/logo512.png


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

+ 12 - 0
src/actions/actionFullLogin.js

@@ -0,0 +1,12 @@
+import { actionLogin } from "./actionLogin";
+import { actionAuthLogin } from "./actionAuthLogin";
+
+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()
+    };
+};

+ 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!')
+    };
+};

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

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

+ 4 - 0
src/actions/actionSnippetUpsert.js

@@ -0,0 +1,4 @@
+import { actionPromise } from "./actionPromise";
+import { SnippetUpsert } from "./requests";
+
+export const actionSnippetUpsert = (title, description, files) => actionPromise("SnippetUpsert", SnippetUpsert(title, description, files));

+ 18 - 0
src/actions/actionUploadFile.js

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

+ 0 - 255
src/actions/index.js

@@ -1,255 +0,0 @@
-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())
-
-let gql = getGQL("/graphql")
-
-let 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
-}
-
-
-let 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 actionAuthLogout = () => ({type: 'LOGOUT'})
-const actionAuthLogin = token => ({type: 'LOGIN', token})
-const actionLogin = (login, password) => actionPromise("login", log(login, password))
-const actionReg = (login, password) => actionPromise("reg", reg(login, password))
-
-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()
-    }
-}
-
-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!')
-    }
-}
-
-// let ChangePass = async(login, password, newPassword) => {
-//     let query = `mutation changePass($login:String!, $password:String!, $newPassword:String!) {
-//     changePassword(
-//         login: $login,
-//         password: $password,
-//         newPassword: $newPassword
-
-//     ){
-//        _id
-//     }`
-
-//     let variables = {"login":login, "password":password, "newPassword":newPassword}
-
-//     let res = await gql(query, variables)
-//     return res 
-// }
-
-// export const actionchangePass = (login, password, newPassword) => actionPromise("changePass", ChangePass(login, password, newPassword))
-
-let SnippetUpsert = async (title, description, files) => {
-    let query = `mutation SnippetUpsert($snippet:SnippetInput) {
-      SnippetUpsert(snippet:$snippet){
-        _id
-      }
-    }`
-  
-    let variables = { snippet: { title, description, files:files }};
-  
-    let res = await gql(query, variables)
-    return res
-}
-
-export const actionSnippetUpsert = (title, description, files) => actionPromise("SnippetUpsert", SnippetUpsert(title, description, files))
-
-const UserFind = async (_id) => {
-    let query = `UserFind(query:'[{}])' {
-        UserFind(query:$query) {
-            _id login avatar{
-                url
-            }
-        }
-  }`
-      let variables = { query: JSON.stringify([{ _id }])}
-
-      let res = await gql(query, variables)
-      return res
-}
-
-export const actionUserFind = (_id) => actionPromise("UserFind", UserFind(_id))
-
-export const imgFind = async () => {
-    return await gql(`query ImageFind{
-      ImageFind(query:"[{}]"){
-        url owner{
-          nick
-        }
-      }
-    }`)
-}
-
-export const actionImgFind = () => async (dispatch) => {
-    return await dispatch(actionPromise("img", imgFind()));
-}
-
-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 actionSetAva = (idUser, id) => actionPromise("setAva", setAva(idUser, id))
-
-const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name})
-const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
-const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
-
-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))
-        }
-    }
-
-let up = async(file) => {
-    let formData = new FormData()
-    formData.append('photo', file)
-    return fetch('/upload', {
-            method: "POST",
-            headers: localStorage.authToken ? {Authorization: 'Bearer ' + localStorage.authToken} : {},
-            body: formData
-        }).then(res => res.json())
-    }
-
-export const actionUploadFile = (file) => actionPromise("upload", up(file))
-
-export const actionFullAvatar = (file) => async (dispatch) => {
-    let result = await dispatch(actionUploadFile(file));
-    await dispatch(actionSetAva(result._id));
-    await dispatch(actionUserFind());
-}
-  
-//   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;
-//   }
-  
-//   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;
-//   }
-  
-//   let 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 actionSnippetAdd =
-//     (title, description, files , idSnippet) => async (dispatch) => {
-//       return await dispatch(
-//         actionPromise("addSnippet",
-//         snippetAdd(title, description, files , idSnippet))
-//       );
-//     };
-  
-//     export const actionSnippetFindByOwner =
-//     (id) => async (dispatch) => {
-//       return await dispatch(
-//         actionPromise("findSnippet",
-//         snippetByOwner(id))
-//       );
-//     };
-//     export const actionSnippetById =
-//     (id) => async (dispatch) => {
-//       return await dispatch(
-//         actionPromise("findSnippetById",
-//         snippetById(id))
-//       );
-//     };

+ 71 - 0
src/actions/requests.js

@@ -0,0 +1,71 @@
+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 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 SnippetUpsert = async (title, description, files) => {
+    let query = `mutation SnippetUpsert($snippet:SnippetInput) {
+      SnippetUpsert(snippet:$snippet){
+        _id
+      }
+    }`
+  
+    let variables = { snippet: { title, description, files:files }}
+  
+    let res = await gql(query, variables)
+    return res
+};

+ 3 - 7
src/components/Avatar.js

@@ -1,14 +1,10 @@
 import React from "react";
 import { connect } from "react-redux";
-import icon from '../user.png';
+import icon from "../user.png";
 import './Header.css';
 
 function AvaLogo({ link }) {
-    return <img src= {link ? ('http://localhost:3000/' + link) : icon} className = 'avatar' alt='user_photo'></img>
+    return <img src= {link ? 'http://localhost:3000/' + link: icon} className = 'avatar' alt=''></img>
 }
 
-const ConnectedAvaLogo = connect((state) => ({link: state?.p?.payload?.data?.avatar?.url}))(AvaLogo);
-
-export default ConnectedAvaLogo;
-
-// https://w7.pngwing.com/pngs/946/367/png-transparent-gray-cat-illustration-cat-pusheen-kitten-cuteness-desktop-cute-love-animals-pet.png
+export const ConnectedAvaLogo = connect((state) => ({link: state?.p?.findUser?.payload?.data?.UserFindOne?.avatar?.url}))(AvaLogo);

+ 6 - 0
src/components/EditorsPage.js

@@ -58,6 +58,12 @@ export const EditorsPage = ({onSave}) => {
             }
             }>+</button>
 
+        <button className='button_plus' onClick={newArray => { let editors2 = [...editors]
+            editors2.pop(newArray);
+            setEditors(editors2);
+            }
+            }>-</button>
+
         <div className="pane">
             <iframe
                 srcDoc={srcDoc}

+ 0 - 24
src/components/FormDate.js

@@ -1,24 +0,0 @@
-import { useCallback } from 'react';
-import { useDropzone } from 'react-dropzone';
-import './FormDate.css'
-
-export const FormDate = ({onUpload}) => {
-    const onDrop = useCallback(acceptedFiles => {
-      acceptedFiles.map((file) => onUpload(file))
-    }, [onUpload])
-    const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
-  
-    return (
-        <>
-        <div {...getRootProps()}>
-            <input {...getInputProps()} />
-            {
-            isDragActive ?
-                <p> Drop the files here ...</p> :
-                <p className='buttons'> Download photo</p>
-            }
-        </div>
-        <button className='back'><a href = '/cabinet'>Go Back</a></button>
-      </>
-    )
-}

+ 6 - 10
src/components/Header.css

@@ -5,6 +5,7 @@ body{
 nav {
     display: flex;
     margin-bottom: 30px;
+    padding: 5px;
     background: linear-gradient(#a088bd, #c7a9aa);
 }
 
@@ -12,8 +13,8 @@ nav {
     width: 100px;
     margin-right: 1135px;
     border: 1.5px solid rgb(72, 70, 75);
-    margin-top: 5px;
-    margin-left: 20px;
+    margin-top: 10px;
+    margin-left: 15px;
 }
 
 .img_logo:hover {
@@ -27,19 +28,14 @@ nav {
     font-size: 20px;
 }
 
-.logaut {
-    padding-top: 20px;
-    height: 100px;
-}
-
 .logaut button {
     position: relative;
     display: inline-block;
-    width: 6em;
+    width: 6.5em;
     height: 2.5em;
     cursor: pointer;
     margin-top: 7px;
-    line-height: 2em;
+    margin-bottom: 5px;
     vertical-align: middle;
     font-weight: 550;
     text-align: center;
@@ -61,7 +57,7 @@ nav {
 } 
 
 .avatar {
-   margin-top: 20px;
+   margin-top: 25px;
    margin-right: 10px;
    width: 70px;
    height: 70px;

+ 2 - 0
src/components/Header.js

@@ -1,11 +1,13 @@
 import './Header.css';
 import ImgLogo from './Logo';
 import ConnectedNick from './Nick';
+import { ConnectFormUpload } from "../pages/FormUpload";
 
 const Header = () => {
     return (
 <nav className="navbar_header">
     <ImgLogo />
+    <ConnectFormUpload />
     <ConnectedNick/>
 </nav>
     )

+ 7 - 6
src/components/Nick.js

@@ -1,22 +1,23 @@
 import { connect } from "react-redux";
-import { actionAuthLogout } from "../actions";
-import ConnectedAvaLogo from "./Avatar";
+import { actionAuthLogout } from '../actions/actionAuthLogout';
 import './Header.css';
 
 const NickName = ({ nick, onLogOut }) => {
     return (
       <>
-        <a href = '/cabinet'>
-        <ConnectedAvaLogo />
-        </a>
         <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);
+const ConnectedNick = connect((state) => ({nick: state?.a?.payload?.sub?.login, logedIn: state.a.token}),{onLogOut:actionAuthLogout})(NickName);
 export default ConnectedNick;

+ 2 - 5
src/components/Routers.js

@@ -2,10 +2,10 @@ import React from 'react';
 import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
 import createHistory from "history/createBrowserHistory";
 import { connect } from 'react-redux';
-import { actionFullLogin, actionFullRegister, actionFullAvatar } from '../actions';
+import { actionFullLogin } from '../actions/actionFullLogin';
+import { actionFullRegister } from '../actions/actionFullRegister';
 import { LoginForm } from '../pages/LoginForm';
 import { FormReg } from '../pages/FormReg';
-import { FormDate } from '../components/FormDate';
 import { Main } from '../pages/Main';
 import { Home } from '../pages/HomePage';
 import { Cabinet } from '../pages/Cabinet';
@@ -14,8 +14,6 @@ const ConnectLoginForm = connect(null, {onLogin:actionFullLogin}) (LoginForm);
 
 const ConnectFormReg = connect(null, {FormReg:actionFullRegister}) (FormReg);
 
-export const ConnectFormUpload = connect(null, {onUpload:actionFullAvatar}) (FormDate);
-
 export let history = createHistory();
 
 const Routs = ({token}) => {
@@ -25,7 +23,6 @@ const Routs = ({token}) => {
             {token && <Switch>
                 <Redirect from="/login" to="/home" />
                 <Route exact path='/home' component={Home} />
-                <Route path='/upload' component={ConnectFormUpload}/>
                 <Route path='/cabinet' component={Cabinet}/>
             </Switch>}
             {!token && <Switch>

+ 2 - 2
src/components/User.js

@@ -1,4 +1,4 @@
-import { connect } from "react-redux"
+import { connect } from "react-redux";
 
 const Nick =  ({nick}) => {
     return (
@@ -11,5 +11,5 @@ const Nick =  ({nick}) => {
       )
     }
     
-const ConnectNickName = connect(state => ({nick:state?.a?.payload?.sub?.login || 0 }))(Nick)
+const ConnectNickName = connect(state => ({nick: state?.a?.payload?.sub?.login || 0 }))(Nick);
 export default ConnectNickName;

+ 0 - 13
src/helpers/others.js

@@ -1,13 +0,0 @@
-// const Preloadig = ({promiseName, promiseState, children}) => {
-//             //если данные есть - проверить promiseState на предмет наличия ключа из promiseName
-//             //и статуса RESOLVED 
-//             //то нарисовать children
-//             //byfxt dthyenm null/нарисовать прелоадер
-// }
-
-// const CPreloading = connect(state => ({promiseState: state.promise}))(Preloadig)
-
-
-// {<CPreloading promiseName="category">
-//     <CCategory />
-// </ CPreloading>}

+ 3 - 8
src/pages/Cabinet.js

@@ -1,5 +1,6 @@
 import React from "react";
 import ConnectedNick from '../components/Nick';
+import { ConnectFormUpload } from "./FormUpload";
 import './Main.css';
 
 export const Cabinet = () => {
@@ -7,23 +8,17 @@ export const Cabinet = () => {
     <>
         <div className="card">
             <div className='nick_style'>
+                <ConnectFormUpload  />
                 <ConnectedNick />
             </div>
             <div className="cart_1">
-                <a href="/upload">
-                    <button className='btn'>
-                        Add Your Photo
-                    </button>
-                </a>
-            </div>
-            <div className="cart_2">
                 <a href="/projects">
                     <button className='btn'>
                         My Projects
                     </button>
                 </a>
             </div>
-            <div className="cart_3">
+            <div className="cart_2">
                 <a href="/home">
                     <button className='btn'>
                         Work Page

src/components/FormDate.css → src/pages/FormUpload.css


+ 19 - 0
src/pages/FormUpload.js

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

+ 1 - 1
src/pages/HomePage.js

@@ -1,5 +1,5 @@
 import { EditorsPage } from '../components/EditorsPage';
-import { actionSnippetUpsert } from '../actions';
+import { actionSnippetUpsert } from '../actions/actionSnippetUpsert';
 import { connect }  from 'react-redux';
 import Header from '../components/Header';
 

+ 1 - 1
src/pages/Main.css

@@ -89,7 +89,7 @@
     padding-left: 100px;
     margin-left: 470px;
     margin-top: 100px;
-    padding-top: 10px;
+    padding-top: 30px;
     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);

BIN
src/user.png