🔥 ⚡ Major Update
* Adds Actions, Redicers and Middlewares * Adds Http Service * Adds Cancel option for Http Service * Adds HOC's for API Loader, Sidebar and Headers * Adds Random key generator for Routes
This commit is contained in:
16
src/actions/login.action.js
Normal file
16
src/actions/login.action.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { LOGIN_REQUEST, LOGIN_SUCCESS } from "../utils/constants";
|
||||||
|
|
||||||
|
|
||||||
|
export const loginRequest = (payload)=>{
|
||||||
|
return {
|
||||||
|
type: LOGIN_REQUEST,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loginSuccess = (payload)=>{
|
||||||
|
return {
|
||||||
|
type: LOGIN_SUCCESS,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import AdminContainer from "../modules/admin/admin.container";
|
import AdminContainer from "../modules/admin/admin.container";
|
||||||
import SuperAdminContainer from "../modules/superadmin/superadmin.container";
|
import SuperAdminContainer from "../modules/superadmin/superadmin.container";
|
||||||
|
import DashBoardContainer from "../modules/dashboard/dashboard.container";
|
||||||
|
|
||||||
export const AppRoutes = [
|
export const AppRoutes = [
|
||||||
{
|
{
|
||||||
@@ -11,5 +12,10 @@ export const AppRoutes = [
|
|||||||
path: '/superadmin',
|
path: '/superadmin',
|
||||||
component: SuperAdminContainer,
|
component: SuperAdminContainer,
|
||||||
permission: ['admin', 'superadmin', 'user']
|
permission: ['admin', 'superadmin', 'user']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dashboard',
|
||||||
|
component: DashBoardContainer,
|
||||||
|
permission: ['user']
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Route, Redirect } from "react-router-dom";
|
import { Route, Redirect } from "react-router-dom";
|
||||||
import Storage from "../services/storage.service";
|
import Storage from "../services/storage.service";
|
||||||
|
import Permissions from "./permission.router";
|
||||||
|
import { RandomKey } from "../utils/random.key";
|
||||||
|
|
||||||
export const CustomRouter = ({ xComponent: Component, ...xProps }) => {
|
export const CustomRouter = ({ xComponent: Component, ...xProps }) => {
|
||||||
return (
|
return (
|
||||||
@@ -19,7 +21,7 @@ export const CustomRouter = ({ xComponent: Component, ...xProps }) => {
|
|||||||
|
|
||||||
return <Redirect to="/dashboard" />;
|
return <Redirect to="/dashboard" />;
|
||||||
}
|
}
|
||||||
return <Component {...returnProps} />;
|
return <Permissions {...returnProps} key={RandomKey.generate()} Component={Component}/>;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Route, Redirect } from "react-router-dom";
|
import { Route, Redirect } from "react-router-dom";
|
||||||
|
|
||||||
// export const Permi
|
export const Permissions = ({Component: Component, ..._props})=>{
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Route
|
||||||
|
render={props => {
|
||||||
|
let returnProps = {..._props, ...props};
|
||||||
|
// let token = Storage.get("token");
|
||||||
|
let pathName = props.match.path;
|
||||||
|
console.log(returnProps);
|
||||||
|
return <Component {...returnProps} />;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Permissions;
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
import React, { Suspense } from "react";
|
import React, { Suspense } from "react";
|
||||||
import { Provider } from "react-redux";
|
import { Provider, connect } from "react-redux";
|
||||||
import { ConnectedRouter } from "connected-react-router";
|
import { ConnectedRouter } from "connected-react-router";
|
||||||
import { Switch, Redirect, Route } from "react-router-dom";
|
import { Switch, Redirect, Route, withRouter } from "react-router-dom";
|
||||||
import { CustomRouter } from "./custom.router";
|
import { CustomRouter } from "./custom.router";
|
||||||
import LoginContainer from "../modules/login/login.container";
|
import LoginContainer from "../modules/login/login.container";
|
||||||
import DashBoardContainer from "../modules/dashboard/dashboard.container";
|
import DashBoardContainer from "../modules/dashboard/dashboard.container";
|
||||||
import { AppRoutes } from "./app.routes";
|
import { AppRoutes } from "./app.routes";
|
||||||
|
import MasterComponent from "../master/master.component";
|
||||||
|
|
||||||
const Routes = ({ store, history }) => {
|
const Routes = ({ store, history }) => {
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
<MasterComponent>
|
||||||
<ConnectedRouter history={history}>
|
<ConnectedRouter history={history}>
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={<div style={{ display: "none" }}> Loading ...</div>}
|
fallback={<div style={{ display: "none" }}> Loading ...</div>}
|
||||||
@@ -28,6 +30,7 @@ const Routes = ({ store, history }) => {
|
|||||||
</Switch>
|
</Switch>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</ConnectedRouter>
|
</ConnectedRouter>
|
||||||
|
</MasterComponent>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,3 +6,18 @@ body {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid{
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content{
|
||||||
|
margin-left: 92px;
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Route
|
Route
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import Routes from './core/routes';
|
import Routes from './core/routes';
|
||||||
|
import HttpService from './services/http.service';
|
||||||
|
|
||||||
require("es6-promise").polyfill();
|
require("es6-promise").polyfill();
|
||||||
|
|
||||||
@@ -18,6 +19,9 @@ require("es6-promise").polyfill();
|
|||||||
|
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
store.runSaga(rootMiddleware);
|
store.runSaga(rootMiddleware);
|
||||||
|
HttpService.reduxStore = store;
|
||||||
|
HttpService.httpInterceptor();
|
||||||
|
// console.log(HttpService.httpInterceptor)
|
||||||
|
|
||||||
const XRouter = () => {
|
const XRouter = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
36
src/master/master.component.js
Normal file
36
src/master/master.component.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createLoadingSelector, createNotificationSelector } from '../services/selectors';
|
||||||
|
import WithLoader from '../shared/loader.hoc';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
let loadingSelector = createLoadingSelector([
|
||||||
|
'LOGIN',
|
||||||
|
'COMMON'
|
||||||
|
]);
|
||||||
|
|
||||||
|
let errorSelector = createNotificationSelector([
|
||||||
|
'LOGIN',
|
||||||
|
'COMMON'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const MasterComponent = (props) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
isLoading: loadingSelector(state),
|
||||||
|
error: errorSelector(state)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(
|
||||||
|
WithLoader(MasterComponent)
|
||||||
|
);
|
||||||
|
|
||||||
29
src/middlewares/login.middleware.js
Normal file
29
src/middlewares/login.middleware.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { put, call, fork, takeEvery } from "redux-saga/effects";
|
||||||
|
import { LOGIN_REQUEST, LOGIN_SUCCESS } from "../utils/constants";
|
||||||
|
import { loginMock } from "../modules/login/login.service";
|
||||||
|
import { history } from '../core/store';
|
||||||
|
function* loginWatcher() {
|
||||||
|
yield takeEvery(LOGIN_REQUEST, loginWorker);
|
||||||
|
}
|
||||||
|
|
||||||
|
function* loginWorker(action) {
|
||||||
|
let { email, password } = action;
|
||||||
|
let res = yield call(loginApi, { email, password });
|
||||||
|
if (res && res.data) {
|
||||||
|
let { token } = res.data;
|
||||||
|
yield put({
|
||||||
|
type: LOGIN_SUCCESS,
|
||||||
|
payload: {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
history.push('/dashboard');
|
||||||
|
}
|
||||||
|
console.log(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loginApi(params) {
|
||||||
|
return loginMock(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LoginSaga = [fork(loginWatcher)];
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { all } from "redux-saga/effects";
|
import { all } from "redux-saga/effects";
|
||||||
|
import { LoginSaga } from "./login.middleware";
|
||||||
|
|
||||||
export default function* rootMiddleware() {
|
export default function* rootMiddleware() {
|
||||||
yield all([
|
yield all([
|
||||||
//...LoginSaga,
|
...LoginSaga,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,33 @@
|
|||||||
import React, {Component} from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from "react-redux";
|
||||||
class AdminContainer extends Component{
|
import WithHeaderFooter from '../../shared/header_footer.hoc';
|
||||||
constructor(props){
|
import WithSidebar from '../../shared/sidebar.hoc';
|
||||||
|
class AdminContainer extends Component {
|
||||||
|
constructor(props) {
|
||||||
console.log(props);
|
console.log(props);
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
return(
|
return (
|
||||||
<div>Admin Container</div>
|
<div className="container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
Admin Container
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminContainer;
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(
|
||||||
|
WithSidebar(WithHeaderFooter(AdminContainer))
|
||||||
|
);
|
||||||
@@ -1,12 +1,28 @@
|
|||||||
import React, {Component} from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import WithHeaderFooter from '../../shared/header_footer.hoc';
|
||||||
|
import WithSidebar from '../../shared/sidebar.hoc';
|
||||||
|
class DashBoardContainer extends Component {
|
||||||
|
|
||||||
class DashBoardContainer extends Component{
|
render() {
|
||||||
|
return (
|
||||||
render(){
|
<div className="container-fluid">
|
||||||
return(
|
<div className="row">
|
||||||
<div>Dashboard</div>
|
<div className="col-md-12">
|
||||||
|
Dashboard
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DashBoardContainer;
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(
|
||||||
|
WithSidebar(WithHeaderFooter(DashBoardContainer))
|
||||||
|
);
|
||||||
@@ -1,12 +1,50 @@
|
|||||||
import React,{Component} from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import WithFooter from '../../shared/footer.hoc';
|
||||||
|
import { loginMock } from './login.service';
|
||||||
|
import { loginRequest } from '../../actions/login.action';
|
||||||
|
import HttpService from '../../services/http.service';
|
||||||
|
class LoginContainer extends Component {
|
||||||
|
|
||||||
class LoginContainer extends Component{
|
state = {
|
||||||
|
|
||||||
render(){
|
};
|
||||||
return(
|
|
||||||
<div>Login</div>
|
onLogin = ()=>{
|
||||||
|
let params = {
|
||||||
|
email: "eve.holt@reqres.in",
|
||||||
|
password: "cityslicka"
|
||||||
|
};
|
||||||
|
|
||||||
|
// loginMock(params).then(res=>{
|
||||||
|
// console.log(res);
|
||||||
|
// })
|
||||||
|
this.props.dispatch(loginRequest(params));
|
||||||
|
// HttpService.cancelRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
Login
|
||||||
|
</div>
|
||||||
|
<div className="col-md-12">
|
||||||
|
<button className="btn btn-primary" onClick={this.onLogin}>Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LoginContainer;
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(
|
||||||
|
WithFooter(LoginContainer)
|
||||||
|
);
|
||||||
9
src/modules/login/login.service.js
Normal file
9
src/modules/login/login.service.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import HttpService from '../../services/http.service';
|
||||||
|
|
||||||
|
export const loginMock = (params)=>{
|
||||||
|
return HttpService.fetch({
|
||||||
|
url: 'https://reqres.in/api/login',
|
||||||
|
method: 'post',
|
||||||
|
data: params
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,12 +1,57 @@
|
|||||||
import React, {Component} from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import WithHeaderFooter from '../../shared/header_footer.hoc';
|
||||||
|
import WithSidebar from '../../shared/sidebar.hoc';
|
||||||
|
import { COMMON_REQUEST, COMMON_CANCEL } from '../../utils/constants';
|
||||||
|
import HttpService from '../../services/http.service';
|
||||||
|
class SuperAdminContainer extends Component {
|
||||||
|
|
||||||
class SuperAdminContainer extends Component{
|
componentDidMount() {
|
||||||
|
|
||||||
render(){
|
}
|
||||||
return(
|
|
||||||
<div>SuperAdminContainer</div>
|
startRequest = () => {
|
||||||
|
this.props.dispatch({
|
||||||
|
type: COMMON_REQUEST
|
||||||
|
});
|
||||||
|
let params = {
|
||||||
|
url: 'https://reqres.in/api/users?page=2'
|
||||||
|
}
|
||||||
|
HttpService.fetch(params).then(res=>{
|
||||||
|
console.log(res);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
stopRequest = () => {
|
||||||
|
// this.props.dispatch({
|
||||||
|
// type: COMMON_CANCEL
|
||||||
|
// })
|
||||||
|
HttpService.cancelRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
SuperAdminContainer
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-1"><button className="btn btn-primary" onClick={this.startRequest}>Start Request</button></div>
|
||||||
|
<div className="col-md-1"><button className="btn btn btn-danger" onClick={this.stopRequest}>Stop Request</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SuperAdminContainer;
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(
|
||||||
|
WithSidebar(WithHeaderFooter(SuperAdminContainer))
|
||||||
|
);
|
||||||
18
src/reducers/error.reducer.js
Normal file
18
src/reducers/error.reducer.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export default function ErrorReducer(state = {}, action) {
|
||||||
|
const { type, error } = action;
|
||||||
|
const matches = /(.*)_(REQUEST|FAILED|ERROR)/.exec(
|
||||||
|
type
|
||||||
|
);
|
||||||
|
if (!matches) return state;
|
||||||
|
const [, requestName, requestState] = matches;
|
||||||
|
return {
|
||||||
|
errorMessage:
|
||||||
|
requestState === "FAILED" || requestState === "ERROR"
|
||||||
|
? error
|
||||||
|
? error
|
||||||
|
: ""
|
||||||
|
: "",
|
||||||
|
[requestName]: requestState === "FAILED" || requestState === "ERROR" ? true: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
12
src/reducers/loading.reducer.js
Normal file
12
src/reducers/loading.reducer.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export default function LoadingReducer(state = {}, action) {
|
||||||
|
console.log("Reducer",action)
|
||||||
|
const { type } = action;
|
||||||
|
const matches = /(.*)_(REQUEST|SUCCESS|FAILED|ERROR|SUBMIT|CANCEL)/.exec(type);
|
||||||
|
if (!matches) return state;
|
||||||
|
const [, requestName, requestState] = matches;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[requestName]: (requestState === 'REQUEST' || requestState === 'SUBMIT')
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
src/reducers/login.reducer.js
Normal file
21
src/reducers/login.reducer.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { LOGIN_REQUEST, LOGIN_SUCCESS } from "../utils/constants";
|
||||||
|
import Storage from "../services/storage.service";
|
||||||
|
|
||||||
|
|
||||||
|
let initialState = {
|
||||||
|
token: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LoginReducer = (state=initialState, action)=>{
|
||||||
|
switch (action.type) {
|
||||||
|
case LOGIN_REQUEST: return state;
|
||||||
|
case LOGIN_SUCCESS:
|
||||||
|
|
||||||
|
Storage.set('token', action.payload.token);
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
token: action.payload.token
|
||||||
|
};
|
||||||
|
default: return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import { combineReducers } from "redux";
|
import { combineReducers } from "redux";
|
||||||
import { connectRouter } from "connected-react-router";
|
import { connectRouter } from "connected-react-router";
|
||||||
|
import LoadingReducer from "./loading.reducer";
|
||||||
|
import ErrorReducer from "./error.reducer";
|
||||||
|
import { LoginReducer } from "./login.reducer";
|
||||||
|
|
||||||
const createRootReducer = history =>
|
const createRootReducer = history =>
|
||||||
combineReducers({
|
combineReducers({
|
||||||
router: connectRouter(history),
|
router: connectRouter(history),
|
||||||
|
loading: LoadingReducer,
|
||||||
|
error: ErrorReducer,
|
||||||
|
login: LoginReducer
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createRootReducer;
|
export default createRootReducer;
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import Storage from './storage.service';
|
||||||
|
import { COMMON_REQUEST, COMMON_SUCCESS, COMMON_CANCEL } from '../utils/constants';
|
||||||
|
|
||||||
|
class HttpServiceSingleton {
|
||||||
|
constructor() {
|
||||||
|
axios.defaults.headers.post["Content-Type"] = "application/json";
|
||||||
|
axios.defaults.headers.put["Accept"] = "application/json";
|
||||||
|
this.count = 0; // This will store API requests
|
||||||
|
this.complete = 0; // This will store API Success/errors
|
||||||
|
}
|
||||||
|
|
||||||
|
set reduxStore(store) {
|
||||||
|
this.store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(payload, noAuth) {
|
||||||
|
const cancelToken = axios.CancelToken;
|
||||||
|
this.cancelSource = cancelToken.source();
|
||||||
|
let config = {};
|
||||||
|
if (!noAuth) {
|
||||||
|
let token = Storage.get("token");
|
||||||
|
config.headers = {
|
||||||
|
'Authirization': `Bearer ${token}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
config.method = payload.method ? payload.method : 'get';
|
||||||
|
config.url = payload.url ? payload.url : '';
|
||||||
|
config.cancelToken = this.cancelSource.token;
|
||||||
|
if (payload.data) config.data = JSON.stringify(payload.data);
|
||||||
|
|
||||||
|
return axios(config).catch((thrown) => {
|
||||||
|
if (axios.isCancel(thrown)) {
|
||||||
|
this.count = 0;
|
||||||
|
this.complete = 0;
|
||||||
|
this.store.dispatch({
|
||||||
|
type: COMMON_CANCEL
|
||||||
|
});
|
||||||
|
console.log('Request canceled', thrown.message);
|
||||||
|
} else {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
httpInterceptor() {
|
||||||
|
axios.interceptors.request.use((config) => {
|
||||||
|
this.count++;
|
||||||
|
this.checkApiComplete(this.store);
|
||||||
|
return config;
|
||||||
|
}, (error) => {
|
||||||
|
this.complete++;
|
||||||
|
this.checkApiComplete(this.store);
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
axios.interceptors.response.use((response) => {
|
||||||
|
this.complete++;
|
||||||
|
this.checkApiComplete(this.store);
|
||||||
|
return response;
|
||||||
|
}, (error) => {
|
||||||
|
this.complete++;
|
||||||
|
this.checkApiComplete(this.store);
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRequest() {
|
||||||
|
this.cancelSource.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fallback to cancel all API loaders in case of multiple API call occuts
|
||||||
|
checkApiComplete(store) {
|
||||||
|
if (this.count == this.complete) { // Cancel API loader when all requests are completed
|
||||||
|
store.dispatch({
|
||||||
|
type: COMMON_SUCCESS
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
store.dispatch({
|
||||||
|
type: COMMON_REQUEST
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HttpService = new HttpServiceSingleton();
|
||||||
|
// Object.freeze(HttpService); // Singleton Http Service
|
||||||
|
|
||||||
|
export default HttpService;
|
||||||
10
src/services/selectors.js
Normal file
10
src/services/selectors.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export const createLoadingSelector = actions => state => {
|
||||||
|
return _(actions).some(action => _.get(state, `loading.${action}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createNotificationSelector = actions => state => {
|
||||||
|
return _(actions)
|
||||||
|
.some(action => _.get(state, `error.${action}`));
|
||||||
|
};
|
||||||
@@ -9,7 +9,8 @@ class Storage{
|
|||||||
if(!key||!value){
|
if(!key||!value){
|
||||||
throw("Storag.set expects a 'key' and a 'value' - 'value' & 'key' can't be null");
|
throw("Storag.set expects a 'key' and a 'value' - 'value' & 'key' can't be null");
|
||||||
}
|
}
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
value = (typeof value=="string") ? value : JSON.stringify(value);
|
||||||
|
localStorage.setItem(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/shared/footer.css
Normal file
13
src/shared/footer.css
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 95%;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer>.container {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
35
src/shared/footer.hoc.js
Normal file
35
src/shared/footer.hoc.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { compose } from "redux";
|
||||||
|
import './footer.css';
|
||||||
|
|
||||||
|
const Footer = (HocComponent) => {
|
||||||
|
return class FooterComponent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<HocComponent {...this.props} />
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="container">Footer</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const WithFooter = compose(
|
||||||
|
connect(mapStateToProps, null),
|
||||||
|
Footer
|
||||||
|
)
|
||||||
|
export default WithFooter;
|
||||||
39
src/shared/header_footer.hoc.js
Normal file
39
src/shared/header_footer.hoc.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { compose } from "redux";
|
||||||
|
|
||||||
|
const HeaderFooter = (HocComponent) => {
|
||||||
|
return class HeaderFooterComponent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<nav className="nav">
|
||||||
|
<Link className="nav-link" to={"/dashboard"} >Dashboard</Link>
|
||||||
|
<Link className="nav-link" to={"/superadmin"} >Superadmin</Link>
|
||||||
|
<Link className="nav-link" to={"/admin"} >Admin</Link>
|
||||||
|
<Link className="nav-link" to={"/login"} >Login</Link>
|
||||||
|
</nav>
|
||||||
|
<HocComponent {...this.props} />
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="container">Footer</div>
|
||||||
|
</footer>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const WithHeaderFooter = compose(
|
||||||
|
connect(mapStateToProps, null),
|
||||||
|
HeaderFooter
|
||||||
|
)
|
||||||
|
export default WithHeaderFooter;
|
||||||
48
src/shared/loader.css
Normal file
48
src/shared/loader.css
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
.loader {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
top: 0px;
|
||||||
|
left: 0;
|
||||||
|
height: 4px;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:before {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: -200px;
|
||||||
|
width: 200px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #ff8373;
|
||||||
|
animation: loading 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
from {
|
||||||
|
left: -200px;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
80% {
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
95% {
|
||||||
|
left: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/shared/loader.hoc.js
Normal file
16
src/shared/loader.hoc.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import "./loader.css";
|
||||||
|
const WithLoader = HocComponent => {
|
||||||
|
return function ({ ...props }) {
|
||||||
|
return (
|
||||||
|
<div className="loader-container">
|
||||||
|
{props.isLoading && (
|
||||||
|
<div className="loader"></div>
|
||||||
|
)}
|
||||||
|
<HocComponent {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithLoader;
|
||||||
6
src/shared/sidebar.css
Normal file
6
src/shared/sidebar.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.sidebar{
|
||||||
|
background: #cccc;
|
||||||
|
width: 92px;
|
||||||
|
height: calc(100vh);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
36
src/shared/sidebar.hoc.js
Normal file
36
src/shared/sidebar.hoc.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { compose } from "redux";
|
||||||
|
import './sidebar.css';
|
||||||
|
|
||||||
|
const Sidebar = (HocComponent) => {
|
||||||
|
return class SidebarComponent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="sidebar">
|
||||||
|
Sidebar
|
||||||
|
</div>
|
||||||
|
<div className="content">
|
||||||
|
<HocComponent {...this.props} />
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const WithSidebar = compose(
|
||||||
|
connect(mapStateToProps, null),
|
||||||
|
Sidebar
|
||||||
|
)
|
||||||
|
export default WithSidebar;
|
||||||
5
src/utils/constants.js
Normal file
5
src/utils/constants.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const LOGIN_REQUEST = "LOGIN_REQUEST";
|
||||||
|
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
|
||||||
|
export const COMMON_REQUEST = "COMMON_REQUEST";
|
||||||
|
export const COMMON_SUCCESS = "COMMON_SUCCESS";
|
||||||
|
export const COMMON_CANCEL = "COMMON_CANCEL";
|
||||||
3
src/utils/random.key.js
Normal file
3
src/utils/random.key.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const RandomKey = {
|
||||||
|
generate : ()=> parseInt(Math.random()*Math.pow(10,12),10)
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user