├── .gitattributes ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── actions │ ├── auth_actions.js │ ├── index.js │ └── types.js ├── components │ ├── AccountMenu.js │ ├── Auth │ │ ├── RequireAuth.js │ │ ├── SignIn.js │ │ ├── SignInForm.js │ │ ├── SignUp.js │ │ └── SignUpForm.js │ ├── GraphSection.js │ ├── HCard.js │ ├── Header.js │ ├── LatestSection.js │ ├── MenuItem.js │ ├── NavBar.js │ └── Overview.js ├── config │ ├── firebase.js │ └── index.js ├── containers │ ├── Analytics.js │ ├── App.js │ ├── Customers.js │ ├── Dashboard.js │ ├── Home.js │ ├── Main.js │ ├── Menu.js │ ├── Orders.js │ ├── Products.js │ ├── Profile.js │ ├── Root.js │ ├── Root.test.js │ ├── Settings.js │ ├── Shop.js │ └── __snapshots__ │ │ └── Root.test.js.snap ├── images │ └── screenshot.jpg ├── index.js ├── reducers │ ├── auth_reducer.js │ └── index.js ├── store │ └── index.js ├── styles │ ├── _base.scss │ ├── _bootstrap.css │ ├── _colors.scss │ ├── _fonts.scss │ ├── components │ │ ├── _authform.scss │ │ ├── _buttons.scss │ │ ├── _card.scss │ │ ├── _container.scss │ │ ├── _header.scss │ │ ├── _main.scss │ │ ├── _menu.scss │ │ ├── _panel.scss │ │ └── _welcome.scss │ ├── index.css │ └── index.scss └── utils │ ├── colors.js │ └── registerServiceWorker.js └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.js linguist-vendored=false 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | .vscode 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Admin Dashboard 2 | Basic Admin UI With Firebase 3 | 4 | ![Screenshot](src/images/screenshot.jpg) 5 | 6 | ## Require step: 7 | - [Create a Firebase project for your app](https://firebase.google.com/docs/web/setup) 8 | - update your Firebase credentials in `src/config/firebase.js` 9 | 10 | ## Available Scripts 11 | 12 | - In the project directory, you can run: `npm install` or `yarn install` 13 | - Run `npm start` or `yarn start` 14 | - Open [http://localhost:3000](http://localhost:3000) to view it in the browser 15 | - To watch for changes in SCSS files, run `npm run watch-css` or `yarn run watch-css` 16 | 17 | ## Dependencies 18 | 19 | - [Create React App](https://github.com/facebookincubator/create-react-app) 20 | - [React Redux](https://github.com/reactjs/redux) 21 | - [React Router](https://github.com/ReactTraining/react-router) 22 | - [Redux Form](http://redux-form.com/) 23 | - [Firebase](https://firebase.google.com/) 24 | - [Semantic UI React](https://react.semantic-ui.com) 25 | - [Recharts](http://recharts.org/#/en-US/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-admin-dashboard", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "firebase": "^4.0.0", 7 | "moment": "^2.19.3", 8 | "prop-types": "^15.5.10", 9 | "react": "^15.5.4", 10 | "react-count-to": "^0.8.0", 11 | "react-dom": "^15.5.4", 12 | "react-fontawesome": "^1.6.1", 13 | "react-redux": "^5.0.5", 14 | "react-router": "^4.1.1", 15 | "react-router-dom": "^4.1.1", 16 | "react-router-redux": "^4.0.8", 17 | "recharts": "^1.3.3", 18 | "redux": "^3.6.0", 19 | "redux-form": "^6.7.0", 20 | "redux-logger": "^3.0.6", 21 | "redux-thunk": "^2.2.0", 22 | "semantic-ui-react": "^0.83.0" 23 | }, 24 | "devDependencies": { 25 | "faker": "^4.1.0", 26 | "node-sass-chokidar": "0.0.1", 27 | "react-scripts": "1.0.6", 28 | "react-test-renderer": "^15.5.4" 29 | }, 30 | "scripts": { 31 | "build-css": "node-sass-chokidar src/ -o src/", 32 | "watch-css": "yarn run build-css && node-sass-chokidar src/ -o src/ --watch --recursive", 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "test": "react-scripts test --env=jsdom", 36 | "eject": "react-scripts eject" 37 | }, 38 | "jest": {} 39 | } 40 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lannify/react-admin-dashboard/7721438cc9d8c36078e5e0f9160aa96d4759e36d/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | React Admin Dashboard 13 | 14 | 15 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/actions/auth_actions.js: -------------------------------------------------------------------------------- 1 | import firebase from 'firebase'; 2 | 3 | import { 4 | LOGIN_SUCCESS, 5 | LOGIN_FAIL, 6 | LOGOUT, 7 | CREATE_USER_SUCCESS, 8 | CREATE_USER_FAIL 9 | } from './types'; 10 | 11 | export const createUser = (values) => async (dispatch) => { 12 | const { name, email, password } = values; 13 | 14 | try { 15 | let authUser = await firebase.auth().createUserWithEmailAndPassword(email, password); 16 | 17 | await firebase.database().ref(`users/${authUser.uid}`).set({ 18 | email, 19 | name 20 | }); 21 | 22 | dispatch({ type: CREATE_USER_SUCCESS, payload: authUser }); 23 | 24 | } catch (err) { 25 | console.log(err) 26 | let errorMessage = ''; 27 | 28 | switch (err.code) { 29 | case 'auth/email-already-in-use': 30 | errorMessage = 'Email is already in use.' 31 | break; 32 | default: 33 | errorMessage = 'Cannot create new account. Please try again.'; 34 | } 35 | 36 | dispatch({ type: CREATE_USER_FAIL, payload: errorMessage }); 37 | } 38 | }; 39 | 40 | export const signIn = (values) => async (dispatch) => { 41 | const { email, password } = values; 42 | 43 | try { 44 | let user = await firebase.auth().signInWithEmailAndPassword(email, password); 45 | 46 | dispatch({ type: LOGIN_SUCCESS, payload: user }); 47 | 48 | } catch (err) { 49 | console.log(err); 50 | 51 | dispatch({ type: LOGIN_FAIL, payload: err}); 52 | } 53 | }; 54 | 55 | export const signOut = () => dispatch => { 56 | firebase.auth().signOut(); 57 | dispatch({ type: LOGOUT }); 58 | } -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | export * from './auth_actions'; -------------------------------------------------------------------------------- /src/actions/types.js: -------------------------------------------------------------------------------- 1 | export const LOGIN_SUCCESS = 'login_success'; 2 | export const LOGIN_FAIL = 'login_fail'; 3 | export const LOGOUT = 'logout'; 4 | 5 | export const CREATE_USER_SUCCESS = 'create_user_success'; 6 | export const CREATE_USER_FAIL = 'create_user_fail'; 7 | -------------------------------------------------------------------------------- /src/components/AccountMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { signOut } from '../actions'; 5 | 6 | class AccountMenu extends React.Component { 7 | 8 | signOut = () => this.props.signOut(); 9 | 10 | render() { 11 | return ( 12 |
13 | 14 |
15 | ); 16 | } 17 | } 18 | 19 | const mapsStateToProps = state => ({ 20 | auth: state.auth 21 | }); 22 | 23 | export default connect(mapsStateToProps, {signOut})(AccountMenu); -------------------------------------------------------------------------------- /src/components/Auth/RequireAuth.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lannify/react-admin-dashboard/7721438cc9d8c36078e5e0f9160aa96d4759e36d/src/components/Auth/RequireAuth.js -------------------------------------------------------------------------------- /src/components/Auth/SignIn.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { 4 | Card, 5 | Divider 6 | } from 'semantic-ui-react'; 7 | 8 | import { signIn } from '../../actions'; 9 | 10 | import NavBar from '../NavBar'; 11 | import SignInForm from './SignInForm'; 12 | 13 | class SignIn extends React.Component { 14 | 15 | state = { loading: false, error: '' }; 16 | 17 | onSubmit = (values) => { 18 | this.setState({ loading: true }); 19 | this.props.signIn(values); 20 | } 21 | 22 | componentWillReceiveProps(nextProps) { 23 | const { loggedIn } = nextProps.auth; 24 | 25 | if (!loggedIn) { 26 | this.setState({ error: nextProps.auth.error }); 27 | } 28 | } 29 | 30 | renderError = () => { 31 | if (this.state.error !== '') { 32 | return

{this.state.error}

; 33 | } 34 | } 35 | 36 | render() { 37 | return ( 38 |
39 |
40 | 41 |
42 | 43 |

Sign In

44 | 45 | {this.renderError()} 46 | 47 |
48 |
49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | const mapStateToProps = state => ({ 56 | form: state.form, 57 | auth: state.auth 58 | }) 59 | 60 | export default connect(mapStateToProps, { signIn } )(SignIn); -------------------------------------------------------------------------------- /src/components/Auth/SignInForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Form, 4 | Button 5 | } from 'semantic-ui-react'; 6 | import { Field, reduxForm } from 'redux-form'; 7 | 8 | class SignInForm extends React.Component { 9 | render() { 10 | const { handleSubmit, submitting } = this.props; 11 | 12 | return ( 13 |
14 |
15 |
16 | 23 |
24 |
25 | 32 |
33 | 34 | 38 |
39 |
40 | ); 41 | } 42 | } 43 | 44 | const validate = values => { 45 | const errors = {}; 46 | 47 | if (!values.email) { 48 | errors.email = 'Required'; 49 | } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) { 50 | errors.email = 'Invalid Email Address'; 51 | } 52 | 53 | if (!values.password) { 54 | errors.password = 'Required'; 55 | } else if (values.password.length < 6) { 56 | errors.password = 'Minimum 6 characters'; 57 | } 58 | 59 | return errors; 60 | } 61 | 62 | const renderField = ({ input, label, type, meta: { touched, error } }) => ( 63 |
64 | 65 | 66 | {touched && (error &&

{error}

)} 67 |
68 | ) 69 | 70 | SignInForm = reduxForm({ 71 | form: 'SignInForm', 72 | validate 73 | })(SignInForm); 74 | 75 | export default SignInForm; -------------------------------------------------------------------------------- /src/components/Auth/SignUp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { 4 | Card, 5 | Divider 6 | } from 'semantic-ui-react'; 7 | 8 | import { createUser } from '../../actions'; 9 | 10 | import NavBar from '../NavBar'; 11 | import SignUpForm from './SignUpForm'; 12 | 13 | class SignUp extends React.Component { 14 | 15 | state = { loading: false, error: '' }; 16 | 17 | onSubmit = (values) => { 18 | this.setState({ loading: true }); 19 | this.props.createUser(values); 20 | } 21 | 22 | componentWillReceiveProps(nextProps) { 23 | const { loggedIn } = nextProps.auth; 24 | 25 | if (!loggedIn) { 26 | this.setState({ error: nextProps.auth.error }); 27 | } 28 | } 29 | 30 | renderError = () => { 31 | if (this.state.error !== '') { 32 | return

{this.state.error}

; 33 | } 34 | } 35 | 36 | render() { 37 | return ( 38 |
39 |
40 | 41 |
42 | 43 |

Sign Up

44 | 45 | {this.renderError()} 46 | 47 |
48 |
49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | const mapStateToProps = state => ({ 56 | form: state.form, 57 | auth: state.auth 58 | }) 59 | 60 | export default connect(mapStateToProps, { createUser } )(SignUp); -------------------------------------------------------------------------------- /src/components/Auth/SignUpForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Form, 4 | Button 5 | } from 'semantic-ui-react'; 6 | import { Field, reduxForm } from 'redux-form'; 7 | 8 | class SignUpForm extends React.Component { 9 | render() { 10 | const { handleSubmit, submitting } = this.props; 11 | 12 | return ( 13 |
14 |
15 |
16 | 23 |
24 |
25 | 32 |
33 |
34 | 41 |
42 | 43 | 47 |
48 |
49 | ); 50 | } 51 | } 52 | 53 | const validate = values => { 54 | const errors = {}; 55 | 56 | if (!values.name) { 57 | errors.name = 'Required'; 58 | } else if (values.name.length < 2) { 59 | errors.name = 'Minimum 2 characters'; 60 | } 61 | 62 | if (!values.email) { 63 | errors.email = 'Required'; 64 | } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) { 65 | errors.email = 'Invalid Email Address'; 66 | } 67 | 68 | if (!values.password) { 69 | errors.password = 'Required'; 70 | } else if (values.password.length < 6) { 71 | errors.password = 'Minimum 6 characters'; 72 | } 73 | 74 | return errors; 75 | } 76 | 77 | const renderField = ({ input, label, type, meta: { touched, error } }) => ( 78 |
79 | 80 | 81 | {touched && (error &&

{error}

)} 82 |
83 | ) 84 | 85 | SignUpForm = reduxForm({ 86 | form: 'SignUpForm', 87 | validate 88 | })(SignUpForm); 89 | 90 | export default SignUpForm; -------------------------------------------------------------------------------- /src/components/GraphSection.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | ComposedChart, 4 | Area, 5 | Bar, 6 | XAxis, 7 | YAxis, 8 | CartesianGrid, 9 | Tooltip, 10 | Legend, 11 | ResponsiveContainer, 12 | AreaChart 13 | } from 'recharts'; 14 | 15 | const DATA = [ 16 | { month: 'Jan', revenue: 125123, visitors: 171282 }, 17 | { month: 'Feb', revenue: 105467, visitors: 152382 }, 18 | { month: 'Mar', revenue: 86345, visitors: 256222 }, 19 | { month: 'Apr', revenue: 192567, visitors: 302823 }, 20 | { month: 'May', revenue: 135836, visitors: 223563 }, 21 | { month: 'Jun', revenue: 93536, visitors: 234674 }, 22 | { month: 'Jul', revenue: 182576, visitors: 345143 }, 23 | { month: 'Aug', revenue: 76737, visitors: 176332 }, 24 | { month: 'Sep', revenue: 162342, visitors: 223425 }, 25 | { month: 'Oct', revenue: 114764, visitors: 340289 }, 26 | { month: 'Nov', revenue: 204695, visitors: 426264 }, 27 | { month: 'Dec', revenue: 232687, visitors: 456292 } 28 | ]; 29 | 30 | const PRODUCTS = [ 31 | { month: 'Jan', accessories: 234, phones: 178, laptops: 112 }, 32 | { month: 'Feb', accessories: 325, phones: 155, laptops: 161 }, 33 | { month: 'Mar', accessories: 202, phones: 145, laptops: 191 }, 34 | { month: 'Apr', accessories: 228, phones: 168, laptops: 111 }, 35 | { month: 'May', accessories: 347, phones: 171, laptops: 114 }, 36 | { month: 'Jun', accessories: 304, phones: 158, laptops: 111 } 37 | ]; 38 | 39 | const getPercent = (value, total) => { 40 | const ratio = total > 0 ? value / total : 0; 41 | 42 | return toPercent(ratio, 2); 43 | }; 44 | 45 | const toPercent = (decimal, fixed = 0) => { 46 | return `${(decimal * 100).toFixed(fixed)}%`; 47 | }; 48 | 49 | const renderTooltipContent = (o) => { 50 | const { payload, label } = o; 51 | const total = payload.reduce((result, entry) => (result + entry.value), 0); 52 | 53 | return ( 54 |
55 |

{`${label} (Total: ${total})`}

56 | 65 |
66 | ); 67 | }; 68 | 69 | class GraphSection extends React.Component { 70 | 71 | render() { 72 | return ( 73 |
74 |
75 |
76 |

Revenue and Traffic

77 | 78 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
92 |
93 |

Product Sales

94 | 95 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 |
109 |
110 | ); 111 | } 112 | } 113 | 114 | export default GraphSection; -------------------------------------------------------------------------------- /src/components/HCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FontAwesome from 'react-fontawesome'; 3 | import CountTo from 'react-count-to'; 4 | 5 | export default class HCard extends React.Component { 6 | render() { 7 | let iconClass = `iconWrapper ${this.props.backgroundColor}`; 8 | return( 9 |
10 |
11 | 12 |
13 |
14 |

{this.props.prefix}{this.props.suffix}

15 |

{this.props.label}

16 |
17 |
18 | ); 19 | } 20 | } -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AccountMenu from './AccountMenu'; 3 | 4 | class Header extends React.Component { 5 | render() { 6 | return ( 7 |
8 |

{this.props.pageTitle}

9 | 12 |
13 | 14 | ); 15 | } 16 | } 17 | 18 | export default Header; -------------------------------------------------------------------------------- /src/components/LatestSection.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class LatestSection extends React.Component { 4 | 5 | render() { 6 | return ( 7 |
8 |
9 |
10 |

Latest Orders

11 |
12 |
13 |

Latest Visitors

14 |
15 |
16 |
17 | ); 18 | } 19 | } -------------------------------------------------------------------------------- /src/components/MenuItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import FontAwesome from 'react-fontawesome'; 4 | 5 | export default class MenuItem extends React.Component { 6 | state = { open: false }; 7 | 8 | renderItem() { 9 | let classes = this.state.open ? 'menuItem open' : 'menuItem'; 10 | 11 | return( 12 | 15 | ); 16 | } 17 | 18 | render() { 19 | if (this.props.children) { 20 | return( 21 |
  • 22 | 23 |
  • 24 | ); 25 | } else { 26 | return( 27 |
  • 28 | 29 | 30 | {this.props.linkText} 31 | 32 |
  • 33 | ); 34 | } 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /src/components/NavBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink} from 'react-router-dom'; 3 | 4 | class NavBar extends React.Component { 5 | 6 | render() { 7 | return ( 8 | 19 | ); 20 | } 21 | } 22 | 23 | export default NavBar; -------------------------------------------------------------------------------- /src/components/Overview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import HCard from './HCard'; 3 | 4 | class Overview extends React.Component { 5 | 6 | render() { 7 | return ( 8 |
    9 |
    10 |
    11 | 12 |
    13 |
    14 | 15 |
    16 |
    17 | 18 |
    19 |
    20 | 21 |
    22 |
    23 |
    24 | ) 25 | } 26 | } 27 | 28 | export default Overview; -------------------------------------------------------------------------------- /src/config/firebase.js: -------------------------------------------------------------------------------- 1 | // Replace these credentials with your Firebase credentials 2 | 3 | export const firebaseConfig = { 4 | apiKey: "YOUR_FIREBASE_API_KEY", 5 | authDomain: "YOUR_DOMAIN.firebaseapp.com", 6 | databaseURL: "https://YOUR_DOMAIN.firebaseio.com", 7 | projectId: "YOUR_PROJECT_ID", 8 | storageBucket: "YOUR_BUCKET.appspot.com", 9 | messagingSenderId: "YOUR_SENDER_ID" 10 | }; 11 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | export * from './firebase'; -------------------------------------------------------------------------------- /src/containers/Analytics.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Analytics = () => 5 |
    6 |
    7 |
    8 |

    This is Analytics

    9 |
    10 |
    ; 11 | 12 | export default Analytics; -------------------------------------------------------------------------------- /src/containers/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Route, withRouter, Redirect } from 'react-router-dom'; 4 | import firebase from 'firebase'; 5 | import { Loader } from 'semantic-ui-react'; 6 | 7 | import { firebaseConfig } from '../config'; 8 | 9 | import Home from './Home'; 10 | import Dashboard from './Dashboard'; 11 | import SignIn from '../components/Auth/SignIn'; 12 | import SignUp from '../components/Auth/SignUp'; 13 | 14 | 15 | const ProtectedRoute = ({ component: Component, authed, ...rest }) => { 16 | return ( 17 | authed === true 20 | ? 21 | : } 22 | /> 23 | ); 24 | } 25 | 26 | const PublicRoute = ({ component: Component, authed, ...rest }) => { 27 | return ( 28 | authed === false 31 | ? 32 | : } 33 | /> 34 | ); 35 | } 36 | 37 | class App extends React.Component { 38 | 39 | state = { loggedIn: false, loading: true } 40 | 41 | componentDidMount() { 42 | firebase.initializeApp(firebaseConfig); 43 | firebase.auth().onAuthStateChanged((user) => { 44 | if (user !== null) { 45 | this.setState({ loggedIn: true, loading: false }); 46 | } else { 47 | this.setState({ loggedIn: false, loading: false }); 48 | } 49 | }); 50 | } 51 | 52 | render() { 53 | if (this.state.loading) { 54 | return ; 55 | } else { 56 | return ( 57 |
    58 | 59 | 60 | 61 | 62 |
    63 | ) 64 | } 65 | } 66 | } 67 | 68 | const mapStateToProps = (state) => ({ 69 | auth: state.auth 70 | }); 71 | 72 | /** 73 | * withRouter is a HOC to work around the issue of connect() conflict 74 | * with the current version of React Router. 75 | * This might change in future releases 76 | */ 77 | 78 | export default withRouter(connect(mapStateToProps)(App)); -------------------------------------------------------------------------------- /src/containers/Customers.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Customers = () => 5 |
    6 |
    7 |
    8 |

    This is Customers

    9 |
    10 |
    ; 11 | 12 | export default Customers; -------------------------------------------------------------------------------- /src/containers/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | 4 | import Menu from './Menu'; 5 | import Main from './Main'; 6 | import Profile from './Profile'; 7 | import Products from './Products'; 8 | import Shop from './Shop'; 9 | import Customers from './Customers'; 10 | import Orders from './Orders'; 11 | import Analytics from './Analytics'; 12 | import Settings from './Settings'; 13 | 14 | const Dashboard = () => 15 |
    16 |
    17 |
    18 | 19 |
    20 |
    21 |
    22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
    31 |
    32 |
    33 |
    ; 34 | 35 | export default Dashboard; -------------------------------------------------------------------------------- /src/containers/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | import NavBar from '../components/NavBar'; 5 | 6 | export default class Home extends React.Component { 7 | render() { 8 | return ( 9 |
    10 |
    11 | 12 |
    13 |
    14 |

    Welcome to React
    Admin Dashboard

    15 | CREATE AN ACCOUNT 16 |
    17 |
    18 |
    19 |
    20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /src/containers/Main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Header from '../components/Header'; 4 | import Overview from '../components/Overview'; 5 | import GraphSection from '../components/GraphSection'; 6 | import LatestSection from '../components/LatestSection'; 7 | 8 | class Main extends React.Component { 9 | render() { 10 | return ( 11 |
    12 |
    13 | 14 | 15 | 16 |
    17 | ); 18 | } 19 | } 20 | 21 | export default Main; -------------------------------------------------------------------------------- /src/containers/Menu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import MenuItem from '../components/MenuItem'; 4 | 5 | class Menu extends React.Component { 6 | 7 | state = { open: false }; 8 | 9 | render() { 10 | return ( 11 |
    12 |
    13 |

    React Admin

    14 |
    15 |
      16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    25 |
    26 | 27 | ); 28 | } 29 | } 30 | 31 | export default Menu; -------------------------------------------------------------------------------- /src/containers/Orders.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Orders = () => 5 |
    6 |
    7 |
    8 |

    This is Orders

    9 |
    10 |
    ; 11 | 12 | export default Orders; -------------------------------------------------------------------------------- /src/containers/Products.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Products = () => 5 |
    6 |
    7 |
    8 |

    This is Products

    9 |
    10 |
    ; 11 | 12 | export default Products; -------------------------------------------------------------------------------- /src/containers/Profile.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Profile = () => 5 |
    6 |
    7 |
    8 |

    This is Profile

    9 |
    10 |
    ; 11 | 12 | export default Profile; -------------------------------------------------------------------------------- /src/containers/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | BrowserRouter as Router 4 | } from 'react-router-dom'; 5 | 6 | import createHistory from 'history/createBrowserHistory'; 7 | import { Provider } from 'react-redux'; 8 | 9 | import store from '../store'; 10 | 11 | import App from './App'; 12 | 13 | const history = createHistory(); 14 | 15 | class Root extends React.Component { 16 | render() { 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | 28 | export default Root; -------------------------------------------------------------------------------- /src/containers/Root.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import Root from '../containers/Root'; 4 | 5 | describe('Root container', () => { 6 | it('renders correctly', () => { 7 | const tree = renderer.create( 8 | 9 | ).toJSON(); 10 | 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | }); -------------------------------------------------------------------------------- /src/containers/Settings.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Settings = () => 5 |
    6 |
    7 |
    8 |

    This is Settings

    9 |
    10 |
    ; 11 | 12 | export default Settings; -------------------------------------------------------------------------------- /src/containers/Shop.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from '../components/Header'; 3 | 4 | const Shop = () => 5 |
    6 |
    7 |
    8 |

    This is Shop

    9 |
    10 |
    ; 11 | 12 | export default Shop; -------------------------------------------------------------------------------- /src/containers/__snapshots__/Root.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Root container renders correctly 1`] = ` 4 |
    7 | `; 8 | -------------------------------------------------------------------------------- /src/images/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lannify/react-admin-dashboard/7721438cc9d8c36078e5e0f9160aa96d4759e36d/src/images/screenshot.jpg -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import registerServiceWorker from './utils/registerServiceWorker'; 4 | 5 | import Root from './containers/Root'; 6 | import './styles/index.css'; 7 | 8 | ReactDOM.render(, document.getElementById('root')); 9 | registerServiceWorker(); 10 | -------------------------------------------------------------------------------- /src/reducers/auth_reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | LOGIN_SUCCESS, 3 | LOGIN_FAIL, 4 | LOGOUT, 5 | CREATE_USER_SUCCESS, 6 | CREATE_USER_FAIL 7 | } from '../actions/types'; 8 | 9 | const initialState = { 10 | loggedIn: false, 11 | user: null, 12 | error: '' 13 | } 14 | 15 | export default (state = initialState, action) => { 16 | switch(action.type) { 17 | case LOGIN_SUCCESS: 18 | return { loggedIn: true, user: action.payload }; 19 | case LOGIN_FAIL: 20 | return { loggedIn: false, error: action.payload }; 21 | case LOGOUT: 22 | return initialState; 23 | case CREATE_USER_SUCCESS: 24 | return { loggedIn: true, user: action.payload }; 25 | case CREATE_USER_FAIL: 26 | return { loggedIn: false, error: action.payload }; 27 | default: 28 | return state; 29 | } 30 | } -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { reducer as formReducer } from 'redux-form'; 3 | 4 | import auth from './auth_reducer'; 5 | 6 | export default combineReducers({ 7 | auth, 8 | form: formReducer 9 | }); -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | createStore, 3 | applyMiddleware, 4 | // compose 5 | } from 'redux'; 6 | import thunk from 'redux-thunk'; 7 | import reducers from '../reducers'; 8 | import logger from 'redux-logger'; 9 | 10 | const store = createStore(reducers, {}, applyMiddleware(thunk, logger)); 11 | 12 | export default store; 13 | 14 | -------------------------------------------------------------------------------- /src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | a, 2 | a:visited, 3 | a:focus, 4 | a:hover { 5 | text-decoration: none; 6 | } 7 | 8 | ul { 9 | list-style: none; 10 | } 11 | 12 | .pullRight { 13 | float: right; 14 | } -------------------------------------------------------------------------------- /src/styles/_colors.scss: -------------------------------------------------------------------------------- 1 | .greenBG { 2 | background: #8BC34A; 3 | } 4 | 5 | .orangeBG { 6 | background: orange; 7 | } 8 | 9 | .amberBG { 10 | background: #FFC107; 11 | } 12 | 13 | .tealBG { 14 | background: #1DE9B6; 15 | } 16 | 17 | .purpleBG { 18 | background: purple; 19 | } 20 | 21 | .cyanBG { 22 | background: #00BCD4; 23 | } 24 | 25 | .pinkBG { 26 | background: #F06292; 27 | } 28 | 29 | .text-light { 30 | color: #c6c6c6; 31 | } 32 | 33 | .green { 34 | color: #8BC34A; 35 | } 36 | 37 | .orange { 38 | color: orange; 39 | } 40 | 41 | .amber { 42 | color: #FFC107; 43 | } 44 | 45 | .teal { 46 | color: #1DE9B6; 47 | } 48 | 49 | .purple { 50 | color: purple; 51 | } 52 | 53 | .cyan { 54 | color: #00BCD4; 55 | } 56 | 57 | .pink { 58 | color: #F06292; 59 | } 60 | 61 | .white { 62 | color: #fff; 63 | } -------------------------------------------------------------------------------- /src/styles/_fonts.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: .9em; 3 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | color: #5b5c5d; 5 | } 6 | 7 | h1, 8 | h2, 9 | h3, 10 | h4, 11 | h5, 12 | h6 { 13 | font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; 14 | } 15 | 16 | h1 { 17 | font-size: 1.2em; 18 | color: #F06292; 19 | } 20 | 21 | h2 { 22 | font-size: 1.2em; 23 | } 24 | 25 | .error { 26 | color: red; 27 | margin-top: .5rem; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/styles/components/_authform.scss: -------------------------------------------------------------------------------- 1 | .authForm { 2 | max-width: 500px; 3 | margin: 1rem auto; 4 | display: block; 5 | 6 | h1 { 7 | text-align: center; 8 | color: transparent; 9 | background: linear-gradient(130deg,#fc00ff, #0097de); 10 | background-clip: text; 11 | -webkit-background-clip: text; 12 | } 13 | 14 | .card { 15 | width: 100%; 16 | padding: 2rem 0; 17 | } 18 | 19 | .form { 20 | padding: 1rem 2rem; 21 | 22 | input { 23 | width: 100%; 24 | height: 40px; 25 | border-radius: 4px; 26 | border: 1px solid #aeaeae; 27 | padding: 10px 15px; 28 | 29 | } 30 | 31 | button { 32 | margin: 2rem auto; 33 | display: block; 34 | width: 100%; 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /src/styles/components/_buttons.scss: -------------------------------------------------------------------------------- 1 | .btnCommon, 2 | .ui .button { 3 | height: 44px; 4 | display: inline-block; 5 | line-height: 44px; 6 | padding: 0 25px; 7 | box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08); 8 | border-radius: 4px; 9 | font-size: 15px; 10 | letter-spacing: .15em; 11 | text-decoration: none; 12 | transition: all .15s ease; 13 | text-transform: uppercase; 14 | color: #5c63ce; 15 | border:none; 16 | margin-top: 1rem; 17 | } 18 | 19 | .btnWhite { 20 | background: #fff; 21 | } 22 | 23 | .btnOutline { 24 | border: 1px solid #0986c1; 25 | background: none; 26 | color: #0986c1; 27 | } 28 | 29 | .btnPrimary, 30 | .ui .button { 31 | color: #fff; 32 | background: linear-gradient(130deg,#fc00ff, #0097de); 33 | } 34 | 35 | .btnPrimary:hover, 36 | .ui .button:hover { 37 | color: #fff; 38 | background: linear-gradient(130deg, #b84eb9, #0986c1); 39 | } -------------------------------------------------------------------------------- /src/styles/components/_card.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | border: none; 3 | } 4 | 5 | .hCard { 6 | display: flex; 7 | flex-direction: row; 8 | height: 105px; 9 | width: 100%; 10 | margin: 1rem 0; 11 | 12 | .dataWrapper{ 13 | width: 60%; 14 | display: flex; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | } 19 | 20 | .iconWrapper { 21 | width: 40%; 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | color: #fff; 26 | border-top-left-radius: 5px; 27 | border-bottom-left-radius: 5px; 28 | } 29 | 30 | .number { 31 | font-size: 2.2em; 32 | line-height: 1; 33 | margin-bottom: .25rem; 34 | margin-top: .5rem; 35 | } 36 | } 37 | 38 | .vCard { 39 | padding: 1rem; 40 | } -------------------------------------------------------------------------------- /src/styles/components/_container.scss: -------------------------------------------------------------------------------- 1 | .mainContainer { 2 | padding: 2rem; 3 | } -------------------------------------------------------------------------------- /src/styles/components/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | padding: 1rem 2rem; 3 | display: flex; 4 | flex-direction: row; 5 | justify-content: space-between; 6 | box-shadow: 0px 0px 2px 0px #d2d1d1; 7 | 8 | h1 { 9 | color: #F06292; 10 | font-size: 2.2em; 11 | } 12 | } 13 | 14 | .headerNav { 15 | margin-right: 1rem; 16 | } 17 | -------------------------------------------------------------------------------- /src/styles/components/_main.scss: -------------------------------------------------------------------------------- 1 | .main { 2 | padding: 0; 3 | background: #f6f6f6; 4 | 5 | .overview { 6 | padding: 0 2rem; 7 | width: 100%; 8 | box-shadow: 0px 0px 2px 0px #d2d1d1; 9 | } 10 | 11 | .graphSection, 12 | .latestSection { 13 | padding: 2rem; 14 | box-shadow: 0px 0px 2px 0px #d2d1d1; 15 | } 16 | } -------------------------------------------------------------------------------- /src/styles/components/_menu.scss: -------------------------------------------------------------------------------- 1 | .sidebarMenu { 2 | background: #32343c; 3 | padding: 0; 4 | height: 100vh; 5 | overflow: auto; 6 | 7 | a { 8 | color: rgba(255,255,255,0.85); 9 | display: block; 10 | } 11 | 12 | ul { 13 | padding-left: 0; 14 | } 15 | 16 | ul li { 17 | width: 100%; 18 | } 19 | 20 | .brand { 21 | text-align: center; 22 | padding: 1rem 0; 23 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, .25); 24 | 25 | h2 { 26 | color: rgba(255,255,255,0.85); 27 | font-size: 2em; 28 | font-weight: normal; 29 | } 30 | } 31 | } 32 | 33 | .menuItem { 34 | font-size: 1em; 35 | font-weight: normal; 36 | width: 100%; 37 | 38 | 39 | :hover { 40 | background: #1b1c21; 41 | } 42 | 43 | a { 44 | color: rgba(255,255,255,0.85); 45 | padding: .75rem 0; 46 | } 47 | 48 | span { 49 | vertical-align: middle; 50 | } 51 | 52 | .fa { 53 | margin-left: 3rem; 54 | } 55 | 56 | .linkText { 57 | margin-left: 1rem; 58 | } 59 | } 60 | 61 | .accountMenu { 62 | button { 63 | margin-top: 0; 64 | padding: .5rem 1rem; 65 | height: initial; 66 | line-height: 1; 67 | font-size: .9em; 68 | } 69 | } -------------------------------------------------------------------------------- /src/styles/components/_panel.scss: -------------------------------------------------------------------------------- 1 | .panel { 2 | display: flex; 3 | flex-direction: row; 4 | 5 | .panelImage, 6 | .panelContent { 7 | display: inline-block; 8 | padding: .5rem; 9 | } 10 | } -------------------------------------------------------------------------------- /src/styles/components/_welcome.scss: -------------------------------------------------------------------------------- 1 | .welcomeContainer { 2 | height: 100vh; 3 | width: 100%; 4 | // position: absolute; 5 | overflow: hidden; 6 | // transform: skewY(-10deg); 7 | transform-origin: 0; 8 | background: linear-gradient(130deg,#fc00ff, #00dbde); 9 | 10 | 11 | .mainContainer { 12 | // position: relative; 13 | } 14 | 15 | a.navbar-brand, 16 | a.navLink { 17 | color: #fff; 18 | } 19 | 20 | .navbar ul { 21 | display: flex; 22 | flex-direction: row; 23 | justify-content: space-between; 24 | align-items: baseline; 25 | } 26 | 27 | .navLink { 28 | margin: 0 1rem; 29 | } 30 | 31 | .content { 32 | padding: 5rem 5% 1rem 5%; 33 | } 34 | 35 | h1 { 36 | font-size: 3em; 37 | line-height: 1.4; 38 | margin-bottom: 2rem; 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap'; 2 | @import 'base'; 3 | @import 'fonts'; 4 | @import 'colors'; 5 | @import 'components/header'; 6 | @import 'components/menu'; 7 | @import 'components/main'; 8 | @import 'components/panel'; 9 | @import 'components/card'; 10 | @import 'components/welcome'; 11 | @import 'components/buttons'; 12 | @import 'components/authform'; 13 | @import 'components/container'; -------------------------------------------------------------------------------- /src/utils/colors.js: -------------------------------------------------------------------------------- 1 | /** To be Refactored when switch to CSS in JS */ 2 | 3 | export const Colors = { 4 | green: '#8BC34A', 5 | orange: 'orange', 6 | amber: '#FFC107', 7 | teal: '#1DE9B6', 8 | purple: 'purple', 9 | cyan: '#00BCD4', 10 | pink: '#F06292' 11 | } -------------------------------------------------------------------------------- /src/utils/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | export default function register() { 12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 13 | window.addEventListener('load', () => { 14 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 15 | navigator.serviceWorker 16 | .register(swUrl) 17 | .then(registration => { 18 | registration.onupdatefound = () => { 19 | const installingWorker = registration.installing; 20 | installingWorker.onstatechange = () => { 21 | if (installingWorker.state === 'installed') { 22 | if (navigator.serviceWorker.controller) { 23 | // At this point, the old content will have been purged and 24 | // the fresh content will have been added to the cache. 25 | // It's the perfect time to display a "New content is 26 | // available; please refresh." message in your web app. 27 | console.log('New content is available; please refresh.'); 28 | } else { 29 | // At this point, everything has been precached. 30 | // It's the perfect time to display a 31 | // "Content is cached for offline use." message. 32 | console.log('Content is cached for offline use.'); 33 | } 34 | } 35 | }; 36 | }; 37 | }) 38 | .catch(error => { 39 | console.error('Error during service worker registration:', error); 40 | }); 41 | }); 42 | } 43 | } 44 | 45 | export function unregister() { 46 | if ('serviceWorker' in navigator) { 47 | navigator.serviceWorker.ready.then(registration => { 48 | registration.unregister(); 49 | }); 50 | } 51 | } 52 | --------------------------------------------------------------------------------