Artem преди 3 години
родител
ревизия
25c9b2dfe9

+ 17 - 0
package-lock.json

@@ -10,6 +10,7 @@
         "@testing-library/jest-dom": "^5.14.1",
         "@testing-library/react": "^11.2.7",
         "@testing-library/user-event": "^12.8.3",
+        "axios": "^0.21.4",
         "classnames": "^2.3.1",
         "hook-easy-form": "^2.7.2",
         "jwt-decode": "^3.1.2",
@@ -4774,6 +4775,14 @@
         "node": ">=4"
       }
     },
+    "node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
     "node_modules/axobject-query": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -26659,6 +26668,14 @@
       "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz",
       "integrity": "sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA=="
     },
+    "axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "requires": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
     "axobject-query": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",

+ 1 - 0
package.json

@@ -6,6 +6,7 @@
     "@testing-library/jest-dom": "^5.14.1",
     "@testing-library/react": "^11.2.7",
     "@testing-library/user-event": "^12.8.3",
+    "axios": "^0.21.4",
     "classnames": "^2.3.1",
     "hook-easy-form": "^2.7.2",
     "jwt-decode": "^3.1.2",

+ 106 - 4
src/routes/main/index.js

@@ -1,11 +1,113 @@
-import { useSelector } from 'react-redux';
+import { useEffect } from 'react';
+import hookForm from 'hook-easy-form';
+import { useSelector, useDispatch } from 'react-redux';
+
+import { setData, getUsers } from 'store/counter/actions';
+
+import classes from './index.module.scss';
+
+const form = [
+  {
+    name: 'first_name',
+    value: '',
+    required: true,
+    options: {
+      type: 'text',
+      label: 'First Name'
+    }
+  },
+  {
+    name: 'last_name',
+    value: '',
+    required: true,
+    options: {
+      type: 'text',
+      label: 'Last Name'
+    },
+  },
+  {
+    name: 'age',
+    value: '',
+    required: true,
+    options: {
+      type: 'number',
+      label: 'Your Age'
+    },
+  },
+  {
+    name: 'gender',
+    value: '',
+    required: true,
+    options: {
+      type: 'select',
+      label: 'Pick your gender',
+      options: [
+        { id: 'none', text: '' },
+        { id: 'male', text: 'Male' },
+        { id: 'female', text: 'Female' }
+      ]
+    },
+  },
+  {
+    name: 'bio',
+    value: '',
+    required: true,
+    options: {
+      type: 'text-area',
+      label: 'Tell us about yourself'
+    },
+  },
+]
+
 
 const Main = () => {
-  const user = useSelector((s) => s.auth.user)
-  return (<div>
+  const dispatch = useDispatch();
+  const { formArray, updateEvent, submitEvent, setValueManually } = hookForm({ initialForm: form })
+
+  const user = useSelector((s) => s.auth.user);
+
+  const submit = submitEvent((d) => dispatch(setData(d)));
+
+  useEffect(() => {
+    dispatch(getUsers())
+  }, [dispatch])
+
+  return (
+    <div>
     <h1>MAIN PAGE</h1>
+    
+    <form onSubmit={submit} className={classes.form}>
+      {formArray.map(el => {
+        if (el.options.type === 'text' || el.options.type === 'number') {
+          return <label key={el.name} className={classes.label}>
+            <span>{el.options.label}</span>
+            <input name={el.name} type={el.options.type} value={el.value} onChange={updateEvent} />
+          </label>
+        }
+        if (el.options.type === 'text-area') {
+          return <label key={el.name} className={classes.label}>
+            <span>{el.options.label}</span>
+            <textarea name={el.name} value={el.value} onChange={updateEvent} />
+          </label>
+        }
+        if (el.options.type === 'select') {
+          return <label key={el.name} className={classes.label}>
+            <span>{el.options.label}</span>
+            <select name={el.name} value={el.value} onChange={(e) => setValueManually(el.name, e.target.value)} >
+              {el.options.options.map(e => <option key={e.id} value={e.id}>{e.text}</option>)}
+            </select>
+          </label>
+        }
+
+        return null;
+      })}
+
+      <button type="submit">Submit</button>
+    </form>
+    
     <pre>{JSON.stringify(user, null, 2)}</pre>
-  </div>)
+    </div>
+  )
 }
 
 export default Main;

+ 24 - 0
src/routes/main/index.module.scss

@@ -0,0 +1,24 @@
+.form {
+  display: flex;
+  flex-direction: column;
+  padding: 30px;
+  box-sizing: border-box;
+
+  button {
+    width: max-content;
+  }
+}
+
+.label {
+  display: flex;
+  flex-direction: column;
+  max-width: 30%;
+  &:not(:last-child) {
+    margin-bottom: 20px;
+  }
+
+  span {
+    font-size: 12px;
+    margin-bottom: 6px;
+  }
+}

+ 7 - 6
src/store/auth/saga.js

@@ -2,6 +2,7 @@ import { takeLatest, call, put } from 'redux-saga/effects';
 
 import * as types from './types';
 import * as actions from './actions'
+import { AxiosInstance } from 'utils/axios';
 
 import { getToken, setToLocalStorage } from 'utils/local-storage-actions';
 
@@ -12,12 +13,12 @@ const authRequest = (cred) => fetch('http://localhost:8000/auth/login', {
   }
 }).then(r => r.json())
 
-const meRequest = (token) => fetch('http://localhost:8000/users/me', {
-  headers: {
-    Authorization: `Bearer ${token}`
-  }
-}).then(r => r.json())
-
+const meRequest = (token) => AxiosInstance({
+  url: 'users/me',
+  // headers: {
+  //   Authorization: `Bearer ${token}`
+  // }
+})
 
 // const auth = (cred) => fetch('http://localhost:8000/auth/register')
 

+ 25 - 5
src/store/counter/actions.js

@@ -1,11 +1,31 @@
 import * as types from './types';
 
-export const increase = (payload) => ({
-  type: types.INCREASE,
+export const setData = (payload) => ({
+  type: types.SET_DATA,
   payload
 })
 
-export const decrease = (payload) => ({
-  type: types.DECREASE,
+export const setDataSuccess = (payload) => ({
+  type: types.SET_DATA_SUCCESS,
   payload
-})
+})
+
+export const setDataFail = (payload) => ({
+  type: types.SET_DATA_FAIL,
+  payload
+})
+
+export const getUsers = (payload) => ({
+  type: types.GET_USERS,
+  payload
+})
+
+export const getUsersSuccess = (payload) => ({
+  type: types.GET_USERS_SUCCESS,
+  payload
+})
+
+export const getUsersFail = (payload) => ({
+  type: types.GET_USERS_FAIL,
+  payload
+})

+ 12 - 5
src/store/counter/reducer.js

@@ -1,16 +1,23 @@
 import * as types from './types';
 
 const initialState = {
-  count: 0
+  users: [],
 }
 
 export const countReducer = (state = initialState, action) => {
   switch(action.type) {
-    case types.INCREASE: {
-      return { ...state, count: state.count + 1 }
+    case types.GET_USERS_SUCCESS: {
+      console.log('ss', action.payload);
+      console.log('ww', Object.keys(action.payload));
+      const users = Object.keys(action.payload).map(id => {
+        const { uid, ...rest } = action.payload[id];
+        return { ...rest, id }
+      })
+
+      return { ...state, users }
     }
-    case types.DECREASE: {
-      return { ...state, count: state.count - 1 }
+    case types.GET_USERS_FAIL: {
+      return { ...state }
     }
 
     default: {

+ 47 - 0
src/store/counter/saga.js

@@ -0,0 +1,47 @@
+import { takeLatest, call, put } from 'redux-saga/effects';
+
+import * as types from './types';
+import * as actions from './actions'
+
+import { getToken } from 'utils/local-storage-actions';
+
+const setRequest = (data, token) => {
+  return fetch('https://test-request-4b9d7.firebaseio.com/data.json', {
+    method: "POST",
+    headers: {
+      Authorization: `Bearer ${token}`,
+      'Content-Type': 'application/json'
+    },
+    body: JSON.stringify(data),
+  }).then(r => r.json())
+}
+
+const getUsersRequest = () => fetch('https://first-fly.firebaseio.com/users.json').then(r => r.json())
+
+function* setData({ payload }) {
+  const token = getToken();
+  try {
+    const result = yield call(setRequest, payload, token);
+    console.log('result', result);
+
+    // yield put(actions.getUsersSuccess(result))
+  } catch (error) {
+    // yield put(actions.getUsersFail(error))
+  }
+}
+
+function* getUsers({ payload }) {
+  try {
+    const result = yield call(getUsersRequest);
+    console.log('result', result);
+
+    yield put(actions.getUsersSuccess(result))
+  } catch (error) {
+    yield put(actions.getUsersFail(error))
+  }
+}
+
+export default function* data() {
+  yield takeLatest(types.SET_DATA, setData);
+  yield takeLatest(types.GET_USERS, getUsers);
+}

+ 7 - 2
src/store/counter/types.js

@@ -1,2 +1,7 @@
-export const INCREASE = 'INCREASE';
-export const DECREASE = 'DECREASE';
+export const SET_DATA = 'SET_DATA';
+export const SET_DATA_SUCCESS = 'SET_DATA_SUCCESS';
+export const SET_DATA_FAIL = 'SET_DATA_FAIL';
+
+export const GET_USERS = 'GET_USERS';
+export const GET_USERS_SUCCESS = 'GET_USERS_SUCCESS';
+export const GET_USERS_FAIL = 'GET_USERS_FAIL';

+ 2 - 0
src/store/saga.js

@@ -2,8 +2,10 @@ import { fork } from 'redux-saga/effects';
 
 import films from './fake/saga';
 import auth from './auth/saga';
+import data from './counter/saga';
 
 export default function* rootSaga() {
   yield fork(films)
   yield fork(auth)
+  yield fork(data)
 }

+ 36 - 0
src/utils/axios.js

@@ -0,0 +1,36 @@
+import axios from 'axios';
+
+import { getToken } from 'utils/local-storage-actions';
+
+const instance = axios.create({
+  baseURL: 'http://localhost:8000',
+  headers: { 'Content-Type': 'application/json' },
+});
+
+instance.interceptors.request.use(function (config) {
+  console.log('config', config);
+  const token = getToken();
+
+  config.headers.Authorization = `Bearer ${token}`
+  config.headers['Accepted-language'] = 'ru';
+
+  // Do something before request is sent
+  return config;
+}, function (error) {
+  // Do something with request error
+  return Promise.reject(error);
+});
+
+// Add a response interceptor
+instance.interceptors.response.use(function (response) {
+  // Any status code that lie within the range of 2xx cause this function to trigger
+  // Do something with response data
+  return response;
+}, function (error) {
+  // Any status codes that falls outside the range of 2xx cause this function to trigger
+  // Do something with response error
+  return Promise.reject(error);
+});
+
+
+export const AxiosInstance = ({ url, method = 'GET', params, data }) => instance({ url, method, params, data });