Pavel 7 years ago
parent
commit
23ef978cd1

+ 21 - 0
react_project/.gitignore

@@ -0,0 +1,21 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# 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*

File diff suppressed because it is too large
+ 2229 - 0
react_project/README.md


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


+ 23 - 0
react_project/package.json

@@ -0,0 +1,23 @@
+{
+  "name": "react_project",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "axios": "^0.17.1",
+    "firebase": "^4.8.1",
+    "react": "^16.2.0",
+    "react-dom": "^16.2.0",
+    "react-fontawesome": "^1.6.1",
+    "react-redux": "^5.0.6",
+    "react-router": "^4.2.0",
+    "react-router-dom": "^4.2.2",
+    "react-scripts": "1.0.17",
+    "redux": "^3.7.2"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test --env=jsdom",
+    "eject": "react-scripts eject"
+  }
+}

BIN
react_project/public/favicon.ico


+ 41 - 0
react_project/public/index.html

@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta name="theme-color" content="#000000">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
+    <!--
+      manifest.json provides metadata used when your web app is added to the
+      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    <!--
+      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>React App</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>

+ 15 - 0
react_project/public/manifest.json

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

+ 18 - 0
react_project/src/App.js

@@ -0,0 +1,18 @@
+import React, { Component } from 'react';
+import { Provider } from 'react-redux';
+import store from './store/index';
+import MainComponent from './components/MainComponent'
+
+class App extends Component {
+  render() {
+    return (
+      <div>
+        <Provider store={store}>
+          <MainComponent />
+        </Provider>
+      </div>
+    );
+  }
+}
+
+export default App;

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

@@ -0,0 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+it('renders without crashing', () => {
+  const div = document.createElement('div');
+  ReactDOM.render(<App />, div);
+});

+ 13 - 0
react_project/src/actions/index.js

@@ -0,0 +1,13 @@
+export const addTest = (testObject) => {
+  return {
+    type: 'ADD_NEW_TEST',
+    testObject
+  }
+}
+
+// export const refreshState = (newState) => {
+//   return {
+//     type: 'REFRESH_STATE',
+//     newState
+//   }
+// }

+ 136 - 0
react_project/src/components/AddTestComponent.js

@@ -0,0 +1,136 @@
+import React, { Component } from 'react';
+import { bindActionCreators } from 'redux';
+import { addTest } from '../actions/index';
+import { connect } from 'react-redux';
+import AddTestItem from './AddTestItem';
+import FontAwesome from 'react-fontawesome';
+
+class AddTestComponent extends Component {
+  constructor(props){
+    super(props);
+    this.state = {
+      itemsCount: 1,
+      testObject: {
+        title: '',
+        description: '',
+        keyword: '',
+        readMore: '',
+        questions: [{},{}],
+        imageInputValue: '',
+      },
+      isShowImg: true
+    }
+  }
+  render(){
+    window.AddTestStateQuestions = this.state.testObject.questions;
+    return(
+      <div className="add-test-component">
+        <div className="add-test-container">
+          <form onSubmit={this.submitForm} action="#">
+            <div className="add-test-header">
+              <span className="title-text">Enter the name of the test:</span>
+              <input onInput={this.updateTitle} type="text" className="title-input"/>
+              <span className="title-text">Enter the keyword:</span>
+              <input onInput={this.updateKeyword} type="text" className="title-input"/>
+              <span className="title-text">Read more:</span>
+              <textarea onInput={this.updateReadMore} type="text" className="title-input"></textarea>
+              <span className="title-text">Enter the description of the test:</span>
+              <input onInput={this.updateDescription} type="text" className="description-input"/>
+            </div>
+            {
+              [...Array(this.state.itemsCount)].map((item,i)=>{
+                return (
+                  <div key={i}>
+                    <AddTestItem questionNumber={++i} takeInfo={this.takeInfo.bind(this)}/>
+                  </div>
+                )
+              })
+            }
+
+            <div className="submit-form">
+              <div className="submit-inner-wrapper">
+                <div className="question-edit-buttons">
+                  <button type="button" onClick={this.addQuestion}>Add question</button>
+                  <button type="button" onClick={this.deleteQuestion}>Delete question</button>
+                </div>
+                <div className="select-image">
+                  <input onInput={this.imageInput} type="text" placeholder="enter the image url.."/>
+                  {
+                    this.state.isShowImg ? <img onError={this.imageError} ref="image" className="preview" style={{width: '75%'}} src={this.state.testObject.imageInputValue}/>: null
+                  }
+                </div>
+              </div>
+              <div className="send-input">
+                <input ref="submitButton" type="submit" value="Send"/>
+              </div>
+            </div>
+          </form>
+        </div>
+      </div>
+    )
+  }
+  // custom methods
+  submitForm = (e) => {
+    e.preventDefault();
+    this.props.addTest(this.state.testObject);
+  }
+  takeInfo(receivedData,index) {
+    index--;
+    console.log(receivedData);
+    var object = this.state.testObject;
+    object.questions[index] = receivedData;
+    this.setState({testObject: object});
+  }
+  updateTitle = (event) => {
+    var object = this.state.testObject;
+    object.title = event.target.value;
+    this.setState({testObject: object});
+  }
+  updateDescription = (event) => {
+    var object = this.state.testObject;
+    object.description = event.target.value;
+    this.setState({testObject: object});
+  }
+  updateKeyword = (event) => {
+    var object = this.state.testObject;
+    object.keyword = event.target.value;
+    this.setState({testObject: object});
+  }
+  updateReadMore = (event) => {
+    var object = this.state.testObject;
+    object.readMore = event.target.value;
+    this.setState({testObject: object});
+  }
+  imageInput = (event) => {
+    var object = this.state.testObject;
+    object.imageInputValue = event.target.value;
+    this.setState({testObject: object});
+    this.setState({isShowImg: true})
+  }
+  imageError = () => {
+    this.setState({isShowImg: false})
+  }
+  addQuestion = () => {
+    this.setState((prevState,props)=>({itemsCount: ++prevState.itemsCount}));
+  }
+  deleteQuestion = () => {
+    var object = this.state.testObject;
+    object.questions.pop();
+    this.setState({testObject: object});
+    this.setState((prevState,props)=>({itemsCount: --prevState.itemsCount}));
+  }
+}
+
+
+const mapStateToProps = (state) => {
+  return {
+    tests: state,
+  }
+}
+const mapDispatchToProps = (dispatch) => {
+  return {
+    addTest: bindActionCreators(addTest, dispatch)
+  }
+}
+
+export default connect(mapStateToProps,mapDispatchToProps)(AddTestComponent);

+ 52 - 0
react_project/src/components/AddTestItem.js

@@ -0,0 +1,52 @@
+import React, { Component } from 'react';
+import FontAwesome from 'react-fontawesome';
+
+export default class AddTestItem extends Component {
+  constructor(props){
+    super(props);
+    this.state = {
+      question: '',
+      answerCount: [{value: ''},{value: ''}],
+    }
+
+  }
+  render(){
+
+    return(
+      <div className="add-test-item">
+        <span className="question-counter">{this.props.questionNumber}.</span>
+        <input className ="question-input" value={this.state.question} onInput={this.questionChange} ref="question" type="text" placeholder="enter the question.."/>
+        <FontAwesome name="question" className="question-mark" />
+        {
+          this.state.answerCount.map((item,i)=>{
+            return (<div  className="answer-input-wrapper" key={i}><FontAwesome name="circle-o" className="answer-circle"/><input onChange={this.sendData} placeholder="" value={item.value} onInput={(e)=>this.inputChange(i,e)} className ="answer-input" type="text"/><FontAwesome onClick={()=>this.deleteAnswer(i)} name='times' className="icon"/></div>)
+          })
+        }
+        <button type="button" onClick={this.addAnswer}>Add answer</button>
+      </div>
+    )
+  }
+  //custom methods
+  addAnswer = () => {
+    this.setState((prevState,props)=>({answerCount: prevState.answerCount.concat({value: ''})}));
+  }
+  deleteAnswer = (i) => {
+    this.setState((prevState)=>({answerCount: prevState.answerCount.filter((item,index)=>(i!=index))}))
+  }
+  inputChange = (i,e) => {
+    var arr = this.state.answerCount;
+    arr[i].value = e.target.value;
+    this.setState({answerCount: arr});
+    setTimeout(()=>{
+        this.props.takeInfo(this.state,this.props.questionNumber);
+    },100)
+  }
+  questionChange = (e) => {
+    this.setState({question: e.target.value});
+    setTimeout(()=>{
+        this.props.takeInfo(this.state,this.props.questionNumber);
+    },100)
+  }
+
+
+}

+ 49 - 0
react_project/src/components/MainComponent.js

@@ -0,0 +1,49 @@
+import React, { Component } from 'react';
+import { Router, Route, Link, NavLink} from 'react-router-dom';
+import FontAwesome from 'react-fontawesome';
+import createHistory from 'history/createBrowserHistory';
+import MainPageComponent from './MainPageComponent';
+import AddTestComponent from './AddTestComponent';
+import TestComponent from './TestComponent';
+
+
+export default class MainComponent extends Component {
+  constructor(props){
+    super(props);
+    this.state = {
+
+      isAdaptiveMenuOpen: false
+    }
+  }
+  render(){
+    return(
+      <Router history={createHistory()}>
+        <div className="main-component">
+          <nav>
+            <div className="container">
+              <div className="nav-adaptive-btn">
+                <FontAwesome name="bars" onClick={this.toggleAdaptiveMenu}/>
+              </div>
+              <div ref="navLinks" className={`nav-links ${this.state.isAdaptiveMenuOpen? 'open-adaptive-menu':''}`}>
+                <li><NavLink exact to="/" activeStyle={{backgroundColor: 'rgb(232,232,232)'}}>Main Page</NavLink></li>
+                <li><NavLink to="/addTest" activeStyle={{backgroundColor: 'rgb(232,232,232)'}}>Add New Test</NavLink></li>
+              </div>
+            </div>
+          </nav>
+
+            <Route exact path="/" component={MainPageComponent}></Route>
+            <Route path="/addTest" component={AddTestComponent}></Route>
+            <Route path="/test/:id" component={TestComponent}></Route>
+        </div>
+      </Router>
+    )
+  }
+  // custom functions
+  toggleAdaptiveMenu = () => {
+    if(this.state.isAdaptiveMenuOpen){
+      this.setState({isAdaptiveMenuOpen: false});
+    } else{
+      this.setState({isAdaptiveMenuOpen: true});
+    }
+  }
+}

+ 70 - 0
react_project/src/components/MainPageComponent.js

@@ -0,0 +1,70 @@
+import React, { Component } from 'react';
+import { bindActionCreators } from 'redux';
+import { addTest } from '../actions/index';
+import TestComponent from './TestComponent';
+import { connect } from 'react-redux';
+import { Router, Route, Link, NavLink} from 'react-router-dom';
+
+window.once = true;
+class MainPageComponent extends Component {
+  constructor(props){
+    super(props);
+  }
+  render(){
+
+    if(window.once){
+      console.log('fuck')
+      setTimeout(()=>{
+        window.once = false;
+        document.querySelector('li:nth-of-type(1) a').click();
+        setTimeout(()=>{
+          if(!document.querySelector('.main-page-item')){
+            document.querySelector('li:nth-of-type(1) a').click();
+          }
+        },1500)
+      },1000)
+    }
+    return(
+      <div className="main-page container">
+        {
+          this.props.tests.map((item,i)=>{
+            return (
+              <div className="main-page-item" key={i}>
+                <Link to={`/test/${i}`}>
+                  <div className="main-page-item-img">
+                    <img src={item.imageInputValue}/>
+                    <img src="https://cdn.onlinewebfonts.com/svg/img_382094.png" className="cover-icon"/>
+                  </div>
+                  <div className="main-page-item-description">
+                      <div className="main-page-item-title">{item.title}</div>
+                      <div className="main-page-item-info">{item.description}</div>
+                  </div>
+                </Link>
+              </div>
+            )
+          })
+        }
+
+      </div>
+    )
+  }
+  // custom functions
+
+}
+
+
+
+
+const mapStateToProps = (state) => {
+  return {
+    tests: state,
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(MainPageComponent);

+ 184 - 0
react_project/src/components/TestComponent.js

@@ -0,0 +1,184 @@
+import React, { Component } from 'react';
+import { Router, Route, Link, NavLink} from 'react-router-dom';
+import createHistory from 'history/createBrowserHistory';
+import { connect } from 'react-redux';
+import TestComponentItem from './TestComponentItem';
+import FontAwesome from 'react-fontawesome';
+
+class TestComponent extends Component {
+  constructor(props){
+    super(props);
+    this.state = {
+      readMore: false,
+      showReadMoreControls: false,
+      showResults: false,
+      answerArray: [],
+      answerNumber: '',
+      questionNumber: 0,
+      zIndex: 1,
+      zIndex2: 0,
+      questionNumber2: 0,
+      testObject: this.props.tests[+this.props.match.params.id]
+    }
+  }
+  render(){
+
+      if(this.state.readMore){
+        var isReadMore = (
+          <div className="read-more-text">
+            {this.state.testObject.readMore}
+            <div><FontAwesome name="angle-up" onClick={()=>{this.readMoreUp()}}/></div>
+          </div>);
+      }else{
+        var isReadMore = (
+          <div>
+            <span>Read more about {this.state.testObject.keyword}</span>
+            <FontAwesome name="angle-down" onClick={()=>{this.readMoreDown()}}/>
+          </div>
+        );
+      }
+
+
+    window.TestComponentState = this.state;
+      if(this.state.showResults){
+        return(
+          <div className="results-block">
+              <div className="result-text">
+                Your score as a percentage for <span>{this.state.testObject.keyword}</span>:
+              </div>
+              <div className="result-number">
+                {this.state.answerNumber}%
+                <FontAwesome name="circle-thin" />
+              </div>
+              <div className="result-slider">
+                <div className="result-slider-line" style={{width: `${this.state.answerNumber}%`}}></div>
+              </div>
+              <div ref="readMore" className="read-more" style={ this.state.showReadMoreControls ? {maxHeight: '90px', opacity: '1'} : {}}>
+                {isReadMore}
+              </div>
+          </div>
+        )
+      }
+      else{
+        return(
+          <div className="test-component">
+            <TestComponentItem showResults={this.showResults} questionsLength={this.state.testObject.questions.length} answerArray={this.state.answerArray} answer={this.answer} nextQuestion={this.nextQuestion} questionNumber={this.state.questionNumber2} zIndex={this.state.zIndex2} changeZIndex={this.changeZIndex} info={this.state.testObject.questions[this.state.questionNumber2]} />
+            <TestComponentItem showResults={this.showResults} questionsLength={this.state.testObject.questions.length} answerArray={this.state.answerArray} answer={this.answer} nextQuestion2={this.nextQuestion2} questionNumber={this.state.questionNumber} zIndex={this.state.zIndex} changeZIndex={this.changeZIndex} info={this.state.testObject.questions[this.state.questionNumber]} />
+          </div>
+        )
+      }
+  }
+  //custom functions
+  showResults = (canIClick) => {
+    if(canIClick){
+      this.setState({showResults: true});
+      this.resultScore();
+    }
+  }
+  nextQuestion = () => {
+    this.setState((prevState)=>({questionNumber: prevState.questionNumber + 2}))
+  }
+  nextQuestion2 = () => {
+    if(this.state.questionNumber == 0){
+      this.setState((prevState)=>({questionNumber2: ++prevState.questionNumber2}))
+    }
+    else{
+      this.setState((prevState)=>({questionNumber2: prevState.questionNumber2 + 2}))
+    }
+
+  }
+  changeZIndex = () => {
+    if(this.state.zIndex == 1){
+      this.setState({zIndex: 0,zIndex2: 1});
+    }
+    else{
+      this.setState({zIndex: 1,zIndex2: 0})
+    }
+  }
+  answer = (questionNumber,i) => {
+    // e.target.previousSibling.className = 'answer-circle answer-circle-active';
+    var arr = this.state.answerArray;
+    arr[questionNumber] = i;
+    this.setState({answerArray: arr});
+  }
+  resultScore = (i = 0) => {
+    var totalScore = this.state.answerArray.reduce((item1,item2)=>{
+      return item1 + item2;
+    })
+    var percentage = Math.floor(totalScore / (this.state.testObject.questions.length * 4) * 100); // works if only i have 5 questions in each test
+    this.setState({answerNumber: i});
+    if(i < percentage){
+      setTimeout(()=>{
+        this.resultScore(++i);
+      },50)
+    } else{
+      setTimeout(()=>{
+        this.setState({showReadMoreControls: true});
+      },500)
+    }
+  }
+  readMoreDown = () => {
+        this.refs.readMore.style.opacity = '0';
+      setTimeout(()=>{
+        this.refs.readMore.style.transition = 'opacity 1s, max-height 4s ease'
+        this.refs.readMore.style.maxHeight = '800px';
+        this.setState({readMore: true});
+        this.refs.readMore.style.opacity = '1';
+      },1000)
+      if(document.documentElement.scrollTop || document.body.scrollTop == 0){
+        setTimeout(()=>{
+          this.scrollDown(document.documentElement.scrollTop || document.body.scrollTop)
+        },1300)
+      }
+  }
+  readMoreUp = (i) => {
+      this.refs.readMore.style.maxHeight = '90px';
+      this.refs.readMore.style.opacity = '0';
+      setTimeout(()=>{
+        if(this.refs.readMore){
+          this.setState({readMore: false});
+          this.refs.readMore.style.opacity = '1';
+        }
+      },4000)
+      // if(i == document.documentElement.scrollTop || document.body.scrollTop){
+      //   this.setState({readMore: false});
+      // }
+      // window.scrollTo(0, i);
+      // if(i < document.body.scrollHeight){
+      //   setTimeout(()=>{
+      //     this.readMoreDown(i - 3);
+      //   },3)
+      // }
+  }
+  scrollDown = (i) => {
+      window.scrollTo(0, i);
+      if(i < document.body.scrollHeight){
+        setTimeout(()=>{
+          this.scrollDown(i + 3);
+        },1)
+      }
+
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    tests: state,
+  }
+}
+
+export default connect(mapStateToProps)(TestComponent);
+
+// if(i < document.body.scrollHeight){
+//   if(i / document.body.scrollHeight > 0.2){
+//     setTimeout(()=>{
+//       this.readMore(i + 1);
+//       console.log(`from decelerate ${2 * (1 + 4 * (i / document.body.scrollHeight)*(i / document.body.scrollHeight))}`)
+//     },2 * (1 + 4 * (i / document.body.scrollHeight)*(i / document.body.scrollHeight)))
+//   } else{
+//     setTimeout(()=>{
+//       this.readMore(i + 1);
+//       console.log(`from accelerate ${5 - (1 + 4 * (i / document.body.scrollHeight)*(i / document.body.scrollHeight))}`)
+//     },5 - (1 + 4 * (i / document.body.scrollHeight)*(i / document.body.scrollHeight)))
+//   }
+// }

+ 65 - 0
react_project/src/components/TestComponentItem.js

@@ -0,0 +1,65 @@
+import React, { Component } from 'react';
+import FontAwesome from 'react-fontawesome';
+
+export default class TestComponentItem extends Component {
+  constructor(props){
+    super(props);
+    this.state = {
+
+      canIClick: true
+    }
+  }
+  render(){
+    return(
+      <div ref="TestComponentItem" className="test-component-item" style={{zIndex: this.props.zIndex}}>
+          <div className="item-component-item-header">
+            <span className="test-component-item-number">{`${this.props.questionNumber + 1}. `}</span>
+            <span className="test-component-question">{this.props.info.question}</span>
+          </div>
+          <div className="test-component-item-answers">
+            {
+              this.props.info.answerCount.map((item, i) => {
+                return <div onClick={()=>{this.props.answer(this.props.questionNumber,i)}} key={i}>
+                  <FontAwesome name="circle-o" className={`answer-circle ${i == this.props.answerArray[this.props.questionNumber] ? 'answer-circle-active' : ''}`} id="icon"/>
+                  <label htmlFor="icon">{item.value}</label>
+                </div>
+              })
+            }
+          </div>
+          <div className="next-question">
+            {
+              this.props.questionsLength != this.props.questionNumber + 1 ? <button onClick={this.nextQuestion} type="button">Next</button>:<button onClick={()=>{this.props.showResults(this.state.canIClick)}} type="button">Show results</button>
+            }
+          </div>
+      </div>
+    )
+  }
+  
+  //custom functions
+
+  nextQuestion = () => {
+    if(this.state.canIClick){
+      this.setState({canIClick: false});
+      setTimeout(()=>{
+        this.setState({canIClick: true});
+      },2000)
+      setTimeout(()=>{
+        this.props.changeZIndex();
+      },960)
+      var classInterval = setTimeout(()=>{
+        if(this.refs.TestComponentItem){
+          this.refs.TestComponentItem.className = 'test-component-item';
+        }
+
+      },2000)
+      if(this.props.questionNumber % 2 == 0){
+        this.props.nextQuestion2();
+      }
+      else{
+        this.props.nextQuestion();
+      }
+      this.refs.TestComponentItem.className = 'test-component-item test-component-animation';
+    }
+  }
+
+}

File diff suppressed because it is too large
+ 4 - 0
react_project/src/css/font-awesome.min.css


+ 694 - 0
react_project/src/css/index.css

@@ -0,0 +1,694 @@
+/* @import url('https://fonts.googleapis.com/css?family=Lato'); */
+@import url('https://fonts.googleapis.com/css?family=Cabin:400,700|Lato:400,700|Open+Sans:400,700');
+@import url('./normalize.min.css');
+
+
+html {
+  font-size: 10px;
+}
+
+body * {
+  box-sizing: border-box;
+  font-family: 'Lato', sans-serif;
+  color: #727272;
+  outline: none;
+}
+
+body {
+  background: linear-gradient(to bottom, rgb(232, 232, 232), rgb(245,245,245));
+  margin: 0;
+  padding: 0;
+  font-family: sans-serif;
+  min-height: 100vh;
+}
+
+body button{
+  user-select: none;
+}
+
+body input[type="text"] {
+  color: rgb(130,130,130);
+  box-shadow: inset 1px 1px 3px #e5e5e5!important;
+  background-color: linear-gradient(to top, rgb(232,232,232),rgb(260, 260, 260))!important;
+}
+
+body input[type="text"]::placeholder {
+  color: rgb(180,180,180);
+}
+
+.clearfix::after {
+  clear: both;
+  display: block;
+  width: 100%;
+  content: '';
+}
+
+.container {
+  max-width: 1170px;
+  margin: 0 auto;
+}
+
+.main-component nav {
+  background-color: #F7F7F7;
+}
+
+.main-component nav li {
+  display: inline-block;
+  list-style-type: none;
+}
+
+.main-component nav li a {
+  user-select: none;
+  display: inline-block;
+  text-decoration: none;
+  padding: 20px 30px;
+  background-color: #F7F7F7;
+  transition: all 0.5s ease;
+  color: #727272;
+  font-family: 'Lato', sans-serif;
+  font-size: 1.2rem;
+  text-transform: uppercase;
+  outline: none;
+}
+
+.main-component nav li a:hover {
+  background-color: #f2f2f2;
+}
+
+.nav-adaptive-btn {
+  display: none;
+  height: 63px;
+}
+
+.open-adaptive-menu{
+  height: auto;
+}
+
+.nav-adaptive-btn span {
+  position: absolute;
+  left: 30px;
+  top: 18px;
+  font-size: 30px;
+  color: #848484;
+  cursor: pointer;
+}
+
+.add-test-container {
+  max-width: 768px;
+  margin: 0 auto;
+  background-color: #F7F7F7;
+  margin-top: 30px;
+  border-radius: 10px;
+  padding: 30px 20px 15px;
+  box-shadow: 0px 0px 20px #cacaca;
+  margin-bottom: 30px;
+}
+
+.add-test-component {
+  /* background-color: #EDEDED; */
+  padding-top: 10px;
+}
+
+.add-test-component span.title-text {
+  display: block;
+  font-size: 1.4rem;
+  color: rgb(170, 170, 170);
+  margin-bottom: 10px;
+  text-align: center;
+  font-family: 'Lato', sans-serif;
+  font-weight: bold;
+  letter-spacing: 0.037em;
+}
+.add-test-component span.title-text:nth-of-type(2) {
+  margin-top: 15px;
+  margin-bottom: 0px;
+}
+
+.add-test-component .add-test-header {
+  text-align: center;
+}
+
+.add-test-component .title-input, .description-input {
+  width: 80%;
+  height: 34px;
+  margin-left: 10%;
+}
+
+.add-test-component textarea.title-input {
+  resize: vertical;
+}
+
+.add-test-component .description-input {
+  margin-top: 10px;
+  margin-bottom: 40px;
+  width: 80%;
+  margin-left: 10%;
+}
+
+.add-test-component button {
+  font-weight: bold;
+  font-family: "Lato", sans-serif;
+  font-size: 1.05rem;
+  width: 40%;
+}
+
+.add-test-component button, .add-test-component input{
+  width: 60%;
+  height: 34px;
+  border-radius: 5px;
+  color: rgb(142, 142, 142);
+  border: 1px solid rgb(232, 232, 232);
+  margin: 10px;
+}
+
+.add-test-component .submit-form {
+  padding-top: 15px;
+  display: flex;
+}
+
+.add-test-component .submit-inner-wrapper {
+  width: 60%;
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.add-test-component .select-image {
+  width: 100%;
+}
+
+.add-test-component .select-image input {
+  display: block;
+  width: 75%;
+  background-color: rgb(232,232,232);
+  padding-left: 8px;
+  font-weight: normal!important;
+  font-family: 'Open Sans', sans-serif!important;
+}
+
+.add-test-component img.preview {
+  border: 3px solid #ededed;
+  margin-left: 10px;
+  margin-top: 7px;
+}
+
+
+.add-test-component .question-edit-buttons {
+  width: 100%;
+}
+
+.add-test-component .submit-form button {
+  width: 35%;
+}
+
+.add-test-component .send-input {
+  text-align: right;
+  width: 40%;
+}
+
+
+
+.add-test-component .submit-form input {
+  font-weight: bold;
+  font-family: "Lato", sans-serif;
+  font-size: 1.05rem;
+  box-shadow: inset 1px 1px 4px rgb(220, 220, 220)!important;
+}
+
+.add-test-item {
+  position: relative;
+  border-bottom: 1px solid #e8e8e8;
+  padding-top: 13px;
+}
+
+.add-test-item:nth-first-of-type,  .add-test-item:nth-last-of-type {
+    border-top: 1px solid #e8e8e8;
+}
+
+.add-test-item span.question-counter {
+  position: absolute;
+  left: 9px;
+  top: 20px;
+  font-size: 1.9rem;
+  font-weight: bold;
+  color: rgb(195, 195, 195);
+}
+
+.add-test-item span.question-mark{
+  top: 18px;
+  right: 140px;
+  width: 30px;
+  position: absolute;
+  text-align: right;
+  color: rgb(210,210,210);
+  font-size: 37px;
+}
+
+.add-test-item .answer-circle {
+  position: absolute;
+  left: 11%;
+  top: 13%;
+  font-size: 24px;
+  color: rgb(205, 205, 205);
+}
+
+.add-test-item input, .title-input, .description-input{
+  display: block;
+  width: 70%;
+  font-size: 1rem;
+  padding: 0 10px;
+  height: 34px;
+  margin: 10px;
+  border-radius: 8px;
+  border: 1px solid rgb(232, 232, 232);
+  background: linear-gradient(to top, rgb(239,239,239),rgb(242,242,242));
+  color: #4f4f4f;
+  box-shadow: inset 1px 1px 3px #e5e5e5;
+  font-family: 'Open Sans', sans-serif;
+}
+
+.answer-input-wrapper {
+  position: relative;
+}
+
+.add-test-item span.icon{
+  position: absolute;
+  left: 625px;
+  bottom: -3px;
+  font-size: 22px;
+  height: 30px;
+  width: 30px;
+  color: rgb(210, 210, 210);
+  cursor: pointer;
+  transition: all 0.1s ease;
+}
+
+.add-test-item span.icon:hover::before{
+  transform: scale(1.05);
+  color: rgb(185, 185, 185);
+}
+
+.add-test-item .question-input {
+  margin-left: 50px;
+  margin-bottom: 15px;
+}
+
+.add-test-item .question-input::placeholder {
+  color: rgb(180,180,180);
+}
+
+.add-test-item .answer-input {
+  margin: 10px auto;
+}
+
+.add-test-item .answer-input::placeholder {
+  color: rgb(180,180,180);
+}
+
+
+.add-test-item button{
+  display: block;
+  margin: 25px auto;
+  width: 25%;
+  height: 34px;
+  border-radius: 5px;
+  border: 1px solid rgb(232, 232, 232);
+}
+
+/* main-page */
+.main-page {
+  padding-top: 50px;
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.main-page-item {
+  box-shadow: 0 0 20px #cacaca;
+  width: 30%;
+  margin-right: 1%;
+  margin-bottom: 58.5px;
+}
+
+.main-page-item {
+  width: 30%;
+  margin-right: 5%;
+  transition: all 0.3s ease;
+}
+
+.main-page-item:nth-child(3n) {
+  margin-right: 0;
+}
+
+.main-page-item a {
+  text-decoration: none;
+}
+
+.main-page-item img{
+  width: 100%;
+  transform: scale(1);
+  transition: all 0.3s ease;
+}
+
+.main-page-item img.cover-icon {
+  position: absolute;
+  width: 20%;
+  left: 40%;
+  top: 40%;
+  opacity: 0;
+  transition: opacity 0.3s ease;
+}
+
+.main-page-item .main-page-item-img {
+  position: relative;
+  overflow: hidden;
+}
+
+.main-page-item .main-page-item-img::before {
+  top: 0;
+  left: 0;
+  content: '';
+  display: block;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  background-color: #333;
+  z-index: 1;
+  opacity: 0;
+  transition: all 0.3s ease;
+}
+
+.main-page-item .main-page-item-description {
+  height: 95px;
+  background-color: #fefefe;
+  margin-top: -3px;
+  padding: 15px;
+  box-shadow: inset 0 -2px 4px #cacaca;
+}
+
+.main-page-item:hover img {
+  transform: scale(1.1);
+}
+
+.main-page-item:hover img.cover-icon {
+  opacity: 0.3;
+}
+
+.main-page-item:hover .main-page-item-img::before {
+  opacity: 0.2;
+}
+
+.main-page-item:hover{
+  transform: scale(1.04);
+  box-shadow: 0 0 30px #cacaca;
+}
+
+.main-page-item .main-page-item-description .main-page-item-title {
+  font-size: 1.4rem;
+  margin-top: 5px;
+  font-weight: bold;
+}
+
+.main-page-item .main-page-item-description .main-page-item-info {
+  margin-top: 7px;
+}
+/* main-page */
+
+/* test-component */
+.test-component {
+  perspective: 950px;
+  user-select: none;
+}
+.test-component-item {
+  z-index: 1;
+  position: absolute;
+  margin: auto;
+  left: 0;
+  right: 0;
+  max-width: 640px;
+  margin: 0 auto;
+  background-color: #F7F7F7;
+  margin-top: 90px;
+  border-radius: 10px;
+  padding: 30px;
+  box-shadow: 0px 0px 20px #cacaca;
+  margin-bottom: 30px;
+  text-align: center;
+}
+
+.test-component-animation {
+  animation: testComponentAnimation 2s;
+  transform-origin: 0% 0%;
+}
+
+@keyframes testComponentAnimation {
+  0%{
+    transform: rotateX(0deg);
+  }
+  100%{
+    transform: rotateX(360deg);
+  }
+}
+
+.item-component-item-header {
+  border-bottom: 1px solid #EDEDED;
+  padding-bottom: 20px;
+  display: inline-block;
+  margin-left: -60px;
+}
+
+.test-component-item-number {
+  font-size: 1.7rem;
+  font-weight: bold;
+  color: rgb(160, 160, 160);
+  margin-left: 30px;
+}
+
+.test-component-question {
+  font-size: 1.6rem;
+  font-weight: bold;
+  color: rgb(160, 160, 160);
+}
+
+.test-component-item-answers {
+  font-size: 1.4rem;
+  padding-left: 80px;
+  color: rgb(165, 165, 165);
+  margin: 20px 0 8px;
+}
+
+.test-component-item-answers div{
+  margin: 5px 0px;
+  text-align: left;
+  cursor: pointer;
+}
+
+.test-component-item-answers .answer-circle {
+  font-size: 24px;
+  color: rgb(210, 210, 210);
+  margin-right: 10px;
+  position: relative;
+  top: 1px;
+  transition: all 0.3s ease;
+}
+
+span.answer-circle-active {
+  color: #42AB9E!important;
+}
+
+.next-question button {
+  display: block;
+  margin: 30px auto 5px;
+  width: 45%;
+  height: 34px;
+  border-radius: 5px;
+  border: 1px solid rgb(232, 232, 232);
+  font-weight: bold;
+  font-family: "Lato", sans-serif;
+  font-size: 1.05rem;
+}
+
+.results-block {
+  /* position: absolute; */
+  margin: auto;
+  left: 0;
+  right: 0;
+  max-width: 640px;
+  margin: 0 auto;
+  background-color: #F7F7F7;
+  margin-top: 70px;
+  border-radius: 10px;
+  padding: 40px;
+  padding-bottom: 0;
+  box-shadow: 0px 0px 20px #cacaca;
+  margin-bottom: 30px;
+  text-align: center;
+  transition: all 1s ease;
+}
+
+.result-text {
+  font-size: 1.5rem;
+  text-align: center;
+  font-family: 'Open Sans', sans-serif;
+  margin-bottom: 100px;
+  color: rgb(140,140,140);
+}
+
+.result-text span {
+  font-weight: bold;
+  color: rgb(150,150,150);
+}
+
+.result-number {
+  position: relative;
+  font-size: 3.5rem;
+  text-align: center;
+  color: rgb(170,170,170);
+  margin-bottom: 80px;
+}
+
+.result-number span{
+  position: absolute;
+  left: 0;
+  right: 0;
+  width: 190px;
+  height: 190px;
+  margin: 0 auto;
+  top: -60px;
+  font-size: 190px;
+  color: rgb(200,200,200);
+  opacity: 0.37;
+  /* transform-origin: center center;
+  animation: resultCircleAnimation 10s infinite linear; */
+}
+
+/* @keyframes resultCircleAnimation {
+  0%{
+    transform: rotateZ(0deg);
+    transform: scale(1);
+  }
+  25%{
+    transform: rotateZ(90deg);
+  }
+  50%{
+
+    transform: scale(1.1);
+    transform: rotateZ(180deg);
+  }
+  100%{
+    transform: rotateZ(360deg);
+    transform: scale(1);
+  }
+} */
+
+.result-slider {
+  display: inline-block;
+  height: 20px;
+  width: 40%;
+  border-radius: 10px;
+  background-color: rgb(230,230,230);
+  box-shadow: inset 0 0 10px #cacaca;
+  margin-bottom: 50px;
+}
+
+.result-slider-line {
+  background-color: rgb(195,195,195);
+  height: 100%;
+  border-radius: 10px;
+}
+
+.read-more {
+  opacity: 0;
+  max-height: 0px;
+  transition: opacity 1s, max-height 2s ease;
+  overflow: hidden;
+}
+
+.read-more span:nth-of-type(1) {
+  font-size: 1.2rem;
+  display: block;
+}
+
+.read-more span:nth-of-type(2) {
+  font-size: 60px;
+  cursor: pointer;
+  opacity: 0.5;
+  animation: arrowDownAnimation 1s infinite;
+}
+
+.read-more-text span.fa-angle-up {
+
+  font-size: 60px;
+  cursor: pointer;
+  opacity: 0.5;
+  animation: arrowDownAnimation 1s infinite;
+}
+
+.read-more-text div {
+  text-align: center;
+}
+
+.read-more-text{
+  display: block;
+  padding: 10px 0 10px;
+  text-align: left;
+  font-size: 1.2rem;
+
+}
+
+@keyframes arrowDownAnimation {
+  0%{
+    transform: translateY(0px);
+  }
+  50%{
+    transform: translateY(4px);
+  }
+  100%{
+    transform: translateY(0px)
+  }
+}
+
+/* test-component */
+
+@media screen and (max-width: 1170px) {
+  .main-page {
+    padding-top: 50px;
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-around;
+  }
+
+  .main-page-item {
+    width: 400px;
+    margin-right: 0%;
+  }
+
+  .main-component nav {
+
+  }
+
+  .nav-adaptive-btn {
+    display: block;
+  }
+
+  .open-adaptive-menu{
+    max-height: 200px!important;
+  }
+
+  .nav-links {
+    position: absolute;
+    z-index: 2;
+    width: 100%;
+    max-height: 0px;
+    transition: max-height .5s ease;
+    overflow: hidden;
+  }
+
+  .main-component nav li {
+    width: 100%;
+  }
+
+  .main-component nav li a {
+    width: 100%;
+    padding: 25px 0;
+    text-align: center;
+  }
+}

+ 50 - 0
react_project/src/css/normalize.min.css

@@ -0,0 +1,50 @@
+/*! normalize.css v1.0.1 | MIT License | git.io/normalize */
+article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block}
+audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
+audio:not([controls]){display:none;height:0}
+[hidden]{display:none}
+html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}
+html,button,input,select,textarea{font-family:sans-serif}
+body{margin:0}
+a:focus{outline:thin dotted}
+a:active,a:hover{outline:0}
+h1{font-size:2em;margin:.67em 0}
+h2{font-size:1.5em;margin:.83em 0}
+h3{font-size:1.17em;margin:1em 0}
+h4{font-size:1em;margin:1.33em 0}
+h5{font-size:.83em;margin:1.67em 0}
+h6{font-size:.75em;margin:2.33em 0}
+abbr[title]{border-bottom:1px dotted}
+b,strong{font-weight:bold}
+blockquote{margin:1em 40px}
+dfn{font-style:italic}
+mark{background:#ff0;color:#000}
+p,pre{margin:1em 0}
+code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}
+pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}
+q{quotes:none}
+q:before,q:after{content:'';content:none}
+small{font-size:80%}
+sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
+sup{top:-0.5em}
+sub{bottom:-0.25em}
+dl,menu,ol,ul{margin:1em 0}
+dd{margin:0 0 0 40px}
+menu,ol,ul{padding:0 0 0 40px}
+nav ul,nav ol{list-style:none;list-style-image:none}
+img{border:0;-ms-interpolation-mode:bicubic}
+svg:not(:root){overflow:hidden}
+figure{margin:0}
+form{margin:0}
+fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
+legend{border:0;padding:0;white-space:normal;*margin-left:-7px}
+button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}
+button,input{line-height:normal}
+button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}
+button[disabled],input[disabled]{cursor:default}
+input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}
+input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
+input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
+button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
+textarea{overflow:auto;vertical-align:top}
+table{border-collapse:collapse;border-spacing:0}

+ 8 - 0
react_project/src/firebaseConfig.js

@@ -0,0 +1,8 @@
+export const config = {
+    apiKey: "AIzaSyC78cAI3nrO_5nX-4o_O7ybWGEkv8UwwA8",
+    authDomain: "react-project-a3c86.firebaseapp.com",
+    databaseURL: "https://react-project-a3c86.firebaseio.com",
+    projectId: "react-project-a3c86",
+    storageBucket: "",
+    messagingSenderId: "948517234045"
+};

BIN
react_project/src/images/d799605a87a4be68f9637a6aecb13656.png


+ 12 - 0
react_project/src/index.js

@@ -0,0 +1,12 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './css/index.css';
+import App from './App';
+import registerServiceWorker from './registerServiceWorker';
+import firebase from 'firebase';
+
+
+
+
+ReactDOM.render(<App />, document.getElementById('root'));
+registerServiceWorker();

+ 54 - 0
react_project/src/reducers/index.js

@@ -0,0 +1,54 @@
+import firebase from 'firebase';
+import { config } from '../firebaseConfig';
+import { combineReducers } from 'redux';
+import { refreshState } from '../actions/index';
+import axios from 'axios';
+
+var app = firebase.initializeApp(config);
+var database = app.database().ref().child('tests');
+
+var databaseTests = [];
+database.on('value', snap => {
+  var retrievedObject = snap.val();
+  if(retrievedObject){
+    var keys = Object.keys(retrievedObject);
+    for (var i = 0; i < keys.length; i++) {
+      databaseTests[i] = retrievedObject[keys[i]];
+    }
+  }
+})
+
+// axios.get('https://react-project-a3c86.firebaseio.com/tests.json').then(function(result){
+//   var retrievedObject = result.data;
+//   if(retrievedObject){
+//     var keys = Object.keys(retrievedObject);
+//     for (var i = 0; i < keys.length; i++) {
+//       databaseTests[i] = retrievedObject[keys[i]];
+//     }
+//   }
+// })
+
+const addTestReducer = (state = databaseTests, action) => {
+  if( action.type === 'ADD_NEW_TEST'){
+    database.push().set(action.testObject);
+    return state //.concat(action.testObject);
+  }
+  // else if(action.type === 'REFRESH_STATE'){
+  //   return
+  // }
+  return state;
+}
+
+// const refreshStore = (state = databaseTests, action) => {
+//   if( action.type === 'REFRESH_STATE'){
+//     return action.snap
+//   }
+//   return state;
+// }
+
+// export default combineReducers({
+//    addTestReducer,
+//    refreshStore
+// })
+
+export default addTestReducer;

+ 108 - 0
react_project/src/registerServiceWorker.js

@@ -0,0 +1,108 @@
+// In production, we register a service worker to serve assets from local cache.
+
+// This lets the app load faster on subsequent visits in production, and gives
+// it offline capabilities. However, it also means that developers (and users)
+// will only see deployed updates on the "N+1" visit to a page, since previously
+// cached resources are updated in the background.
+
+// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
+// This link also includes instructions on opting out of this behavior.
+
+const isLocalhost = Boolean(
+  window.location.hostname === 'localhost' ||
+    // [::1] is the IPv6 localhost address.
+    window.location.hostname === '[::1]' ||
+    // 127.0.0.1/8 is considered localhost for IPv4.
+    window.location.hostname.match(
+      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
+    )
+);
+
+export default function register() {
+  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+    // The URL constructor is available in all browsers that support SW.
+    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
+    if (publicUrl.origin !== window.location.origin) {
+      // Our service worker won't work if PUBLIC_URL is on a different origin
+      // from what our page is served on. This might happen if a CDN is used to
+      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
+      return;
+    }
+
+    window.addEventListener('load', () => {
+      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+
+      if (isLocalhost) {
+        // This is running on localhost. Lets check if a service worker still exists or not.
+        checkValidServiceWorker(swUrl);
+      } else {
+        // Is not local host. Just register service worker
+        registerValidSW(swUrl);
+      }
+    });
+  }
+}
+
+function registerValidSW(swUrl) {
+  navigator.serviceWorker
+    .register(swUrl)
+    .then(registration => {
+      registration.onupdatefound = () => {
+        const installingWorker = registration.installing;
+        installingWorker.onstatechange = () => {
+          if (installingWorker.state === 'installed') {
+            if (navigator.serviceWorker.controller) {
+              // At this point, the old content will have been purged and
+              // the fresh content will have been added to the cache.
+              // It's the perfect time to display a "New content is
+              // available; please refresh." message in your web app.
+              console.log('New content is available; please refresh.');
+            } else {
+              // At this point, everything has been precached.
+              // It's the perfect time to display a
+              // "Content is cached for offline use." message.
+              console.log('Content is cached for offline use.');
+            }
+          }
+        };
+      };
+    })
+    .catch(error => {
+      console.error('Error during service worker registration:', error);
+    });
+}
+
+function checkValidServiceWorker(swUrl) {
+  // Check if the service worker can be found. If it can't reload the page.
+  fetch(swUrl)
+    .then(response => {
+      // Ensure service worker exists, and that we really are getting a JS file.
+      if (
+        response.status === 404 ||
+        response.headers.get('content-type').indexOf('javascript') === -1
+      ) {
+        // No service worker found. Probably a different app. Reload the page.
+        navigator.serviceWorker.ready.then(registration => {
+          registration.unregister().then(() => {
+            window.location.reload();
+          });
+        });
+      } else {
+        // Service worker found. Proceed as normal.
+        registerValidSW(swUrl);
+      }
+    })
+    .catch(() => {
+      console.log(
+        'No internet connection found. App is running in offline mode.'
+      );
+    });
+}
+
+export function unregister() {
+  if ('serviceWorker' in navigator) {
+    navigator.serviceWorker.ready.then(registration => {
+      registration.unregister();
+    });
+  }
+}

+ 6 - 0
react_project/src/store/index.js

@@ -0,0 +1,6 @@
+import { createStore } from 'redux';
+import reducers from '../reducers/index';
+
+const store = createStore(reducers);
+window.store = store;
+export default store;