aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIndrajith K L2019-12-12 19:31:50 +0530
committerIndrajith K L2019-12-12 19:31:50 +0530
commit8883eacd2a5e2f3f5637a6b71123dfcb2a64c3d5 (patch)
treefe0fb8f601f2272f9bf2a7d2b365c7812acb6e63
parentf41d980fd83ab7da5804efd8aa7e914e820797d6 (diff)
downloadreact-redux-saga-starter-8883eacd2a5e2f3f5637a6b71123dfcb2a64c3d5.tar.gz
react-redux-saga-starter-8883eacd2a5e2f3f5637a6b71123dfcb2a64c3d5.tar.bz2
react-redux-saga-starter-8883eacd2a5e2f3f5637a6b71123dfcb2a64c3d5.zip
:fire: :zap: 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
-rw-r--r--src/actions/login.action.js16
-rw-r--r--src/core/app.routes.js6
-rw-r--r--src/core/custom.router.js4
-rw-r--r--src/core/permission.router.js17
-rw-r--r--src/core/routes.js43
-rw-r--r--src/index.css15
-rw-r--r--src/index.js4
-rw-r--r--src/master/master.component.js36
-rw-r--r--src/middlewares/login.middleware.js29
-rw-r--r--src/middlewares/root.middleware.js3
-rw-r--r--src/modules/admin/admin.container.js33
-rw-r--r--src/modules/dashboard/dashboard.container.js30
-rw-r--r--src/modules/login/login.container.js50
-rw-r--r--src/modules/login/login.service.js9
-rw-r--r--src/modules/superadmin/superadmin.container.js57
-rw-r--r--src/reducers/error.reducer.js18
-rw-r--r--src/reducers/loading.reducer.js12
-rw-r--r--src/reducers/login.reducer.js21
-rw-r--r--src/reducers/root.reducer.js6
-rw-r--r--src/services/http.service.js90
-rw-r--r--src/services/selectors.js10
-rw-r--r--src/services/storage.service.js3
-rw-r--r--src/shared/footer.css13
-rw-r--r--src/shared/footer.hoc.js35
-rw-r--r--src/shared/header_footer.hoc.js39
-rw-r--r--src/shared/loader.css48
-rw-r--r--src/shared/loader.hoc.js16
-rw-r--r--src/shared/sidebar.css6
-rw-r--r--src/shared/sidebar.hoc.js36
-rw-r--r--src/utils/constants.js5
-rw-r--r--src/utils/random.key.js3
-rw-r--r--src/utils/utls.js (renamed from src/shared/header.hoc.js)0
32 files changed, 662 insertions, 51 deletions
diff --git a/src/actions/login.action.js b/src/actions/login.action.js
new file mode 100644
index 0000000..3468e4e
--- /dev/null
+++ b/src/actions/login.action.js
@@ -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
+ };
+} \ No newline at end of file
diff --git a/src/core/app.routes.js b/src/core/app.routes.js
index 5d06e63..b581d2e 100644
--- a/src/core/app.routes.js
+++ b/src/core/app.routes.js
@@ -1,5 +1,6 @@
import AdminContainer from "../modules/admin/admin.container";
import SuperAdminContainer from "../modules/superadmin/superadmin.container";
+import DashBoardContainer from "../modules/dashboard/dashboard.container";
export const AppRoutes = [
{
@@ -11,5 +12,10 @@ export const AppRoutes = [
path: '/superadmin',
component: SuperAdminContainer,
permission: ['admin', 'superadmin', 'user']
+ },
+ {
+ path: '/dashboard',
+ component: DashBoardContainer,
+ permission: ['user']
}
];
diff --git a/src/core/custom.router.js b/src/core/custom.router.js
index c6b2859..9fcd4f6 100644
--- a/src/core/custom.router.js
+++ b/src/core/custom.router.js
@@ -1,6 +1,8 @@
import React from "react";
import { Route, Redirect } from "react-router-dom";
import Storage from "../services/storage.service";
+import Permissions from "./permission.router";
+import { RandomKey } from "../utils/random.key";
export const CustomRouter = ({ xComponent: Component, ...xProps }) => {
return (
@@ -19,7 +21,7 @@ export const CustomRouter = ({ xComponent: Component, ...xProps }) => {
return <Redirect to="/dashboard" />;
}
- return <Component {...returnProps} />;
+ return <Permissions {...returnProps} key={RandomKey.generate()} Component={Component}/>;
}}
/>
);
diff --git a/src/core/permission.router.js b/src/core/permission.router.js
index 4047b75..d698917 100644
--- a/src/core/permission.router.js
+++ b/src/core/permission.router.js
@@ -1,4 +1,19 @@
import React from "react";
import { Route, Redirect } from "react-router-dom";
-// export const Permi \ No newline at end of file
+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; \ No newline at end of file
diff --git a/src/core/routes.js b/src/core/routes.js
index 764e793..9c2d222 100644
--- a/src/core/routes.js
+++ b/src/core/routes.js
@@ -1,33 +1,36 @@
import React, { Suspense } from "react";
-import { Provider } from "react-redux";
+import { Provider, connect } from "react-redux";
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 LoginContainer from "../modules/login/login.container";
import DashBoardContainer from "../modules/dashboard/dashboard.container";
import { AppRoutes } from "./app.routes";
+import MasterComponent from "../master/master.component";
const Routes = ({ store, history }) => {
return (
<Provider store={store}>
- <ConnectedRouter history={history}>
- <Suspense
- fallback={<div style={{ display: "none" }}> Loading ...</div>}
- >
- <Switch>
- <CustomRouter path="/login" xComponent={LoginContainer} />
- <CustomRouter path="/dashboard" xComponent={DashBoardContainer} />
- {AppRoutes.map(_routes =>
- <CustomRouter
- key={_routes.path}
- path={_routes.path}
- xComponent={_routes.component}
- permissions={_routes.permission}
- />)}
- <Redirect from="*" to="/login" push />
- </Switch>
- </Suspense>
- </ConnectedRouter>
+ <MasterComponent>
+ <ConnectedRouter history={history}>
+ <Suspense
+ fallback={<div style={{ display: "none" }}> Loading ...</div>}
+ >
+ <Switch>
+ <CustomRouter path="/login" xComponent={LoginContainer} />
+ <CustomRouter path="/dashboard" xComponent={DashBoardContainer} />
+ {AppRoutes.map(_routes =>
+ <CustomRouter
+ key={_routes.path}
+ path={_routes.path}
+ xComponent={_routes.component}
+ permissions={_routes.permission}
+ />)}
+ <Redirect from="*" to="/login" push />
+ </Switch>
+ </Suspense>
+ </ConnectedRouter>
+ </MasterComponent>
</Provider>
);
};
diff --git a/src/index.css b/src/index.css
index 1532074..0d4c466 100644
--- a/src/index.css
+++ b/src/index.css
@@ -6,3 +6,18 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+
+
+html, body {
+ margin:0;
+ padding:0;
+ height:100%;
+}
+
+.container-fluid{
+ height: 100%;
+}
+
+.content{
+ margin-left: 92px;
+} \ No newline at end of file
diff --git a/src/index.js b/src/index.js
index a3b342f..787b0c1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -11,6 +11,7 @@ import {
Route
} from "react-router-dom";
import Routes from './core/routes';
+import HttpService from './services/http.service';
require("es6-promise").polyfill();
@@ -18,6 +19,9 @@ require("es6-promise").polyfill();
const store = configureStore();
store.runSaga(rootMiddleware);
+HttpService.reduxStore = store;
+HttpService.httpInterceptor();
+// console.log(HttpService.httpInterceptor)
const XRouter = () => {
return (
diff --git a/src/master/master.component.js b/src/master/master.component.js
new file mode 100644
index 0000000..703badf
--- /dev/null
+++ b/src/master/master.component.js
@@ -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)
+);
+
diff --git a/src/middlewares/login.middleware.js b/src/middlewares/login.middleware.js
new file mode 100644
index 0000000..c4dbc5b
--- /dev/null
+++ b/src/middlewares/login.middleware.js
@@ -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)]; \ No newline at end of file
diff --git a/src/middlewares/root.middleware.js b/src/middlewares/root.middleware.js
index 2e90582..065a572 100644
--- a/src/middlewares/root.middleware.js
+++ b/src/middlewares/root.middleware.js
@@ -1,7 +1,8 @@
import { all } from "redux-saga/effects";
+import { LoginSaga } from "./login.middleware";
export default function* rootMiddleware() {
yield all([
- //...LoginSaga,
+ ...LoginSaga,
]);
} \ No newline at end of file
diff --git a/src/modules/admin/admin.container.js b/src/modules/admin/admin.container.js
index bd6e1dc..91b0d1c 100644
--- a/src/modules/admin/admin.container.js
+++ b/src/modules/admin/admin.container.js
@@ -1,16 +1,33 @@
-import React, {Component} from 'react';
-
-class AdminContainer extends Component{
- constructor(props){
+import React, { Component } from 'react';
+import { connect } from "react-redux";
+import WithHeaderFooter from '../../shared/header_footer.hoc';
+import WithSidebar from '../../shared/sidebar.hoc';
+class AdminContainer extends Component {
+ constructor(props) {
console.log(props);
super(props);
}
- render(){
- return(
- <div>Admin Container</div>
+ render() {
+ return (
+ <div className="container-fluid">
+ <div className="row">
+ <div className="col-md-12">
+ Admin Container
+ </div>
+ </div>
+ </div>
);
}
}
-export default AdminContainer; \ No newline at end of file
+const mapStateToProps = state => {
+ return {
+
+ };
+};
+
+
+export default connect(mapStateToProps)(
+ WithSidebar(WithHeaderFooter(AdminContainer))
+); \ No newline at end of file
diff --git a/src/modules/dashboard/dashboard.container.js b/src/modules/dashboard/dashboard.container.js
index 595aa9f..f792c20 100644
--- a/src/modules/dashboard/dashboard.container.js
+++ b/src/modules/dashboard/dashboard.container.js
@@ -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(
- <div>Dashboard</div>
+ render() {
+ return (
+ <div className="container-fluid">
+ <div className="row">
+ <div className="col-md-12">
+ Dashboard
+ </div>
+ </div>
+ </div>
);
}
}
-export default DashBoardContainer; \ No newline at end of file
+const mapStateToProps = state => {
+ return {
+
+ };
+};
+
+export default connect(mapStateToProps)(
+ WithSidebar(WithHeaderFooter(DashBoardContainer))
+); \ No newline at end of file
diff --git a/src/modules/login/login.container.js b/src/modules/login/login.container.js
index 705284e..2bf05b1 100644
--- a/src/modules/login/login.container.js
+++ b/src/modules/login/login.container.js
@@ -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; \ No newline at end of file
+const mapStateToProps = state => {
+ return {
+
+ };
+};
+
+export default connect(mapStateToProps)(
+ WithFooter(LoginContainer)
+); \ No newline at end of file
diff --git a/src/modules/login/login.service.js b/src/modules/login/login.service.js
new file mode 100644
index 0000000..15fc3cc
--- /dev/null
+++ b/src/modules/login/login.service.js
@@ -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
+ });
+} \ No newline at end of file
diff --git a/src/modules/superadmin/superadmin.container.js b/src/modules/superadmin/superadmin.container.js
index a82608d..ef83a52 100644
--- a/src/modules/superadmin/superadmin.container.js
+++ b/src/modules/superadmin/superadmin.container.js
@@ -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; \ No newline at end of file
+const mapStateToProps = state => {
+ return {
+
+ };
+};
+
+export default connect(mapStateToProps)(
+ WithSidebar(WithHeaderFooter(SuperAdminContainer))
+); \ No newline at end of file
diff --git a/src/reducers/error.reducer.js b/src/reducers/error.reducer.js
new file mode 100644
index 0000000..83d1128
--- /dev/null
+++ b/src/reducers/error.reducer.js
@@ -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
+ };
+ }
+ \ No newline at end of file
diff --git a/src/reducers/loading.reducer.js b/src/reducers/loading.reducer.js
new file mode 100644
index 0000000..5ff7651
--- /dev/null
+++ b/src/reducers/loading.reducer.js
@@ -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')
+ };
+
+} \ No newline at end of file
diff --git a/src/reducers/login.reducer.js b/src/reducers/login.reducer.js
new file mode 100644
index 0000000..3229e35
--- /dev/null
+++ b/src/reducers/login.reducer.js
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/src/reducers/root.reducer.js b/src/reducers/root.reducer.js
index de1a3b8..c5796ea 100644
--- a/src/reducers/root.reducer.js
+++ b/src/reducers/root.reducer.js
@@ -1,9 +1,15 @@
import { combineReducers } from "redux";
import { connectRouter } from "connected-react-router";
+import LoadingReducer from "./loading.reducer";
+import ErrorReducer from "./error.reducer";
+import { LoginReducer } from "./login.reducer";
const createRootReducer = history =>
combineReducers({
router: connectRouter(history),
+ loading: LoadingReducer,
+ error: ErrorReducer,
+ login: LoginReducer
});
export default createRootReducer;
diff --git a/src/services/http.service.js b/src/services/http.service.js
index e69de29..c3914f6 100644
--- a/src/services/http.service.js
+++ b/src/services/http.service.js
@@ -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; \ No newline at end of file
diff --git a/src/services/selectors.js b/src/services/selectors.js
new file mode 100644
index 0000000..f9942d4
--- /dev/null
+++ b/src/services/selectors.js
@@ -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}`));
+};
diff --git a/src/services/storage.service.js b/src/services/storage.service.js
index c41d552..d29a1e5 100644
--- a/src/services/storage.service.js
+++ b/src/services/storage.service.js
@@ -9,7 +9,8 @@ class Storage{
if(!key||!value){
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);
}
}
diff --git a/src/shared/footer.css b/src/shared/footer.css
new file mode 100644
index 0000000..048b0c6
--- /dev/null
+++ b/src/shared/footer.css
@@ -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;
+} \ No newline at end of file
diff --git a/src/shared/footer.hoc.js b/src/shared/footer.hoc.js
new file mode 100644
index 0000000..535a52d
--- /dev/null
+++ b/src/shared/footer.hoc.js
@@ -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; \ No newline at end of file
diff --git a/src/shared/header_footer.hoc.js b/src/shared/header_footer.hoc.js
new file mode 100644
index 0000000..0c093a6
--- /dev/null
+++ b/src/shared/header_footer.hoc.js
@@ -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; \ No newline at end of file
diff --git a/src/shared/loader.css b/src/shared/loader.css
new file mode 100644
index 0000000..2ee47a2
--- /dev/null
+++ b/src/shared/loader.css
@@ -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%;
+ }
+ } \ No newline at end of file
diff --git a/src/shared/loader.hoc.js b/src/shared/loader.hoc.js
new file mode 100644
index 0000000..c60089e
--- /dev/null
+++ b/src/shared/loader.hoc.js
@@ -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; \ No newline at end of file
diff --git a/src/shared/sidebar.css b/src/shared/sidebar.css
new file mode 100644
index 0000000..cfb3e02
--- /dev/null
+++ b/src/shared/sidebar.css
@@ -0,0 +1,6 @@
+.sidebar{
+ background: #cccc;
+ width: 92px;
+ height: calc(100vh);
+ position: absolute;
+} \ No newline at end of file
diff --git a/src/shared/sidebar.hoc.js b/src/shared/sidebar.hoc.js
new file mode 100644
index 0000000..30a18a3
--- /dev/null
+++ b/src/shared/sidebar.hoc.js
@@ -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; \ No newline at end of file
diff --git a/src/utils/constants.js b/src/utils/constants.js
new file mode 100644
index 0000000..a7ae6de
--- /dev/null
+++ b/src/utils/constants.js
@@ -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"; \ No newline at end of file
diff --git a/src/utils/random.key.js b/src/utils/random.key.js
new file mode 100644
index 0000000..50e5293
--- /dev/null
+++ b/src/utils/random.key.js
@@ -0,0 +1,3 @@
+export const RandomKey = {
+ generate : ()=> parseInt(Math.random()*Math.pow(10,12),10)
+}; \ No newline at end of file
diff --git a/src/shared/header.hoc.js b/src/utils/utls.js
index e69de29..e69de29 100644
--- a/src/shared/header.hoc.js
+++ b/src/utils/utls.js