├── .babelrc ├── .gitignore ├── README.md ├── config.js ├── demo-dashboard.gif ├── dist ├── dfc0cda63aba4a842c70693be2325975.gif ├── index.html └── main.js ├── index.pug ├── package-lock.json ├── package.json ├── pagination.gif ├── public └── index.html ├── render-posts.gif ├── server.js ├── src ├── App.js ├── client-config.js ├── components │ ├── Blogs.js │ ├── Home.js │ ├── Login.js │ ├── NavLink.js │ ├── Navbar.js │ ├── Page.js │ ├── Posts.js │ ├── SinglePost.js │ ├── content │ │ └── Content.js │ ├── context │ │ ├── AppContext.js │ │ └── AppProvider.js │ ├── dashboard │ │ ├── Dashboard.js │ │ ├── pages │ │ │ └── Pages.js │ │ ├── posts │ │ │ ├── CreatePost.js │ │ │ └── Posts.js │ │ └── sidebar │ │ │ ├── SidebarMenu.js │ │ │ ├── ToggleSidebarBtn.js │ │ │ └── menus │ │ │ ├── PageMenu.js │ │ │ └── PostMenu.js │ ├── functions.js │ └── layouts │ │ ├── DashboardLayout.js │ │ ├── FeaturedImage.js │ │ ├── Loader.js │ │ ├── Pagination.js │ │ ├── Post.js │ │ └── PostLoader.js ├── index.js ├── loader.gif ├── style.css └── utils │ └── functions.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "@babel/preset-env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": [ 7 | "last 2 Chrome versions", 8 | "last 2 Firefox versions", 9 | "last 2 Safari versions", 10 | "last 2 iOS versions", 11 | "last 1 Android version", 12 | "last 1 ChromeAndroid version", 13 | "ie 11" 14 | ] 15 | } 16 | } ], 17 | "@babel/preset-react" 18 | ], 19 | "plugins": [ "@babel/plugin-proposal-class-properties" ] 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.log 3 | npm-debug.log 4 | yarn-error.log 5 | .DS_Store 6 | node_modules 7 | .cache 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React with WordPress 2 | 3 | :fire: Example of react application with WordPress REST API 4 | 5 | ******* PLEASE STAR MY REPO TO SUPPORT MY WORK 🙏 ****** 6 | 7 | Please follow me 🙏 on [twitter](https://twitter.com/imranhsayed) 8 | 9 | ## Dashboard Demo 10 | 11 | ![](demo-dashboard.gif) 12 | 13 | ## Pagination :video_camera: 14 | ![](pagination.gif) 15 | 16 | ## Post Listings Demo :video_camera: 17 | 18 | ![](render-posts.gif) 19 | 20 | ## [Live Demo](https://react-with-wordpress.netlify.com/) 21 | 22 | ## Features 23 | 24 | 1. CRUD operation with WordPress REST API 25 | 2. Authentication with JWT ( Login Logout ) 26 | 3. Accessing public and private routes 27 | 4. Handing WordPress REST API custom end points. 28 | 5. Creating Dashboard with React for CRUD operation. 29 | 6. Pagination 30 | 31 | ## Installation 32 | 33 | 1. Clone this repo in `git clone https://github.com/imranhsayed/react-with-wordpress` 34 | 35 | 2. `git checkout branchname` 36 | 37 | 3. Run `npm install` 38 | 39 | ## Add [REST API ENDPOINTS WordPress Plugin](https://github.com/imranhsayed/rest-api-endpoints) 40 | 41 | * Clone the [REST API ENDPOINTS](https://github.com/imranhsayed/rest-api-endpoints) plugin in your WordPress plugin directory. 42 | 43 | `git clone git@github.com:imranhsayed/rest-api-endpoints.git` 44 | 45 | ## Configure 46 | 47 | Add your wordPress siteUrl in `src/client-config.js` 48 | 49 | ```ruby 50 | const clientConfig = { 51 | siteUrl: 'http://localhost:8888/wordpress' 52 | }; 53 | 54 | export default clientConfig; 55 | ``` 56 | 57 | ## Branches 58 | 59 | ### 1. [login-with-jwt-wordpress-plugin](https://github.com/imranhsayed/react-with-wordpress/tree/login-with-jwt-wordpress-plugin) 60 | 61 | > A React App where you can login using the endpoint provided by `JWT Authentication for WP-API` WordPress Plugin. 62 | So you need to have this plugin installed on WordPress. The plugin's endpoint returns the user object and a jwt-token on success, 63 | which we can then store in localstorage and login the user on front React Application 64 | 65 | ### Steps 66 | * You need to install and activate [JWT Authentication for WP REST API](https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/) plugin on you WordPress site 67 | * Then you need to configure it by adding these: 68 | 69 | i. Add the last three lines in your WordPress `.htaccess` file as shown: 70 | ```ruby 71 | # BEGIN WordPress 72 | 73 | RewriteEngine On 74 | RewriteBase /wordpress/ 75 | RewriteRule ^index\.php$ - [L] 76 | RewriteCond %{REQUEST_FILENAME} !-f 77 | RewriteCond %{REQUEST_FILENAME} !-d 78 | RewriteRule . /wordpress/index.php [L] 79 | 80 | 81 | RewriteCond %{HTTP:Authorization} ^(.*) 82 | RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1] 83 | SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 84 | 85 | 86 | ``` 87 | ii. Add the following in your `wp-config.php` Wordpress file. You can choose your own secret key. 88 | 89 | ```ruby 90 | define('JWT_AUTH_SECRET_KEY', '&BZd]N-ghz|hbH`=%~a5z(`mR=n%7#8-Iz@KoqtDhQ6(8h$og%-IbI#>N*T`s9Dg'); 91 | define('JWT_AUTH_CORS_ENABLE', true); 92 | ``` 93 | 94 | iii. Now you can make a request to `/wp-json/jwt-auth/v1/token` REST API provided by the plugin. You need to pass 95 | username and password and it returns a user object and token . You can save the token in localstorage and send it in the headers 96 | of your protected route requests ( e.g. [Create Post](https://developer.wordpress.org/rest-api/reference/posts/#create-a-post) `/wp-json/wp/v2/posts` ) 97 | 98 | iiv. So whenever you send a request to WordPress REST API for your protected routes, you send the token received in the headers of 99 | your request 100 | ``` 101 | { 102 | 'Accept': 'application/json', 103 | 'Content-Type': 'application/json', 104 | 'Authorization': `Bearer putTokenReceivedHere` 105 | } 106 | 107 | ``` 108 | 109 | This repo also demonstrates how to create posts in React Application by sending request to protected endpoints ( passing the token in the header ) 110 | 111 | ### 2. [jwt-verify-with-node](https://github.com/imranhsayed/react-with-wordpress/tree/jwt-verify-with-node) 112 | 113 | > A React(front end) + Node(back end) application. It uses `jwt.sign()` ( from `jwtwebtoken` npm package ) to generate a token using the username and password 114 | sent from front end( React ) and returns it as a response, which we then store in localstorage to login the user. 115 | This token received by frond end, will be sent with all further request for protected routes, which will then be verified in node route 116 | using `jwt.verify()` 117 | Besides generating the token, the end point in node also accesses the WordPress rest api to confirm the credentials and returns the user object 118 | or errors if any. 119 | 120 | > It also has functionality to create post where we make a request from front end along with token( React ) to a node end point. 121 | The node endpoint verifies the token and then makes a request to WordPress REST API endpoint to create the post and then returns the 122 | new post id, or error if any. 123 | 124 | ## Commands 125 | 126 | 1. Branch [master](https://github.com/imranhsayed/react-with-wordpress) and [build-app-for-heroku](https://github.com/imranhsayed/react-with-wordpress/tree/build-app-for-heroku) 127 | - `start` Runs node server for development ( in watch mode ). The server.js sends all front end route request to index.html and then all front end route requests is handled by reach router 128 | 129 | 2. Branch [jwt-verify-with-node](https://github.com/imranhsayed/react-with-wordpress/tree/jwt-verify-with-node) and 130 | [login-with-jwt-wordpress-plugin](https://github.com/imranhsayed/react-with-wordpress/tree/login-with-jwt-wordpress-plugin) 131 | 132 | - `dev` Runs webpack dev server for development ( in watch mode ) 133 | 134 | Common 135 | - `prod` Runs webpack in production mode 136 | 137 | ## Free Courses 138 | 139 | [Codeytek](https://codeytek.com/) 140 | 141 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tokenSecret: 'SECRET', 3 | wordPressUrl: 'https://orionhive.com', 4 | wordPressRestNameSpace: '/wp-json/wp/v2/rae' 5 | }; 6 | -------------------------------------------------------------------------------- /demo-dashboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/react-with-wordpress/36282df72aef5d668832502fe67945fe831bf887/demo-dashboard.gif -------------------------------------------------------------------------------- /dist/dfc0cda63aba4a842c70693be2325975.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/react-with-wordpress/36282df72aef5d668832502fe67945fe831bf887/dist/dfc0cda63aba4a842c70693be2325975.gif -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | React App 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /index.pug: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | meta(charset="UTF-8") 4 | meta(name="viewport", content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0") 5 | link(rel="stylesheet", href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css") 6 | link(rel="stylesheet", href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css") 7 | link(rel="stylesheet", href="static/bundle.css") 8 | | 9 | body 10 | div(id="root") 11 | script(src="static/bundle.js") 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-workshop", 3 | "version": "1.0.0", 4 | "description": ":fire: This is a workshop for learning how to build React Applications.", 5 | "main": "index.js", 6 | "scripts": { 7 | "webpack-dev-server": "webpack-dev-server", 8 | "dev": "webpack-dev-server --mode=development", 9 | "prod": "webpack --mode=production" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/imranhsayed/react-workshop.git" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/imranhsayed/react-workshop/issues" 20 | }, 21 | "homepage": "https://github.com/imranhsayed/react-workshop#readme", 22 | "dependencies": { 23 | "@reach/router": "^1.2.1", 24 | "axios": "^0.21.1", 25 | "cors": "^2.8.5", 26 | "dompurify": "^2.1.1", 27 | "jsonwebtoken": "^9.0.0", 28 | "moment": "^2.29.4", 29 | "pug": "^3.0.2", 30 | "qs": "^6.7.3", 31 | "querystringify": "^2.0.0", 32 | "react": "^16.13.1", 33 | "react-dom": "^16.13.1", 34 | "react-moment": "^0.9.2", 35 | "react-navigation": "^3.9.1", 36 | "react-render-html": "^0.6.0" 37 | }, 38 | "devDependencies": { 39 | "@babel/core": "^7.4.3", 40 | "@babel/plugin-proposal-class-properties": "^7.4.0", 41 | "@babel/preset-env": "^7.4.3", 42 | "@babel/preset-react": "^7.0.0", 43 | "babel-loader": "^8.0.5", 44 | "css-loader": "^2.1.1", 45 | "file-loader": "^3.0.1", 46 | "html-webpack-plugin": "^5.5.0", 47 | "image-webpack-loader": "^4.6.0", 48 | "path": "^0.12.7", 49 | "style-loader": "^0.23.1", 50 | "url-loader": "^1.1.2", 51 | "webpack": "^4.29.6", 52 | "webpack-cli": "^3.3.0", 53 | "webpack-dev-server": "^3.2.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pagination.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/react-with-wordpress/36282df72aef5d668832502fe67945fe831bf887/pagination.gif -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | React App 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /render-posts.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/react-with-wordpress/36282df72aef5d668832502fe67945fe831bf887/render-posts.gif -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require( 'express' ); 2 | const cors = require( 'cors' ); 3 | const jwt = require('jsonwebtoken'); 4 | const bodyParser = require('body-parser'); 5 | const config = require( './config' ); 6 | const axios = require( 'axios' ); 7 | const webpack = require('webpack'); 8 | const webpackDevMiddleware = require('webpack-dev-middleware'); 9 | const webpackConfig = require('./webpack.config'); 10 | const path = require('path') 11 | 12 | const wordPressRestUrl = `${config.wordPressUrl}/${config.wordPressRestNameSpace}`; 13 | 14 | console.log(wordPressRestUrl, 'wordpressurl') 15 | 16 | const app = express(); 17 | app.use( cors() ); 18 | 19 | // Setting Paths 20 | app.use('/static', express.static(path.join(__dirname, '/'))); 21 | 22 | // Setting views of the app 23 | app.set('views', path.join(__dirname, './')); 24 | app.set('view engine', 'pug'); 25 | 26 | // Parse application/x-www-form-urlencoded 27 | app.use( bodyParser.urlencoded( { extended: false } ) ); 28 | 29 | // Parse application/json 30 | app.use(bodyParser.json()); 31 | 32 | // Adding webpack build 33 | // Middleware of webpack 34 | if (process.env.NODE_ENV === 'development') { 35 | console.log('in webpack hot middleware'); 36 | const compiler = webpack(webpackConfig); 37 | 38 | app.use(webpackDevMiddleware(compiler, { 39 | noInfo: true, 40 | publicPath: webpackConfig.output.publicPath, 41 | })); 42 | } 43 | 44 | 45 | /** 46 | * Sign in user 47 | * 48 | * @route http://localhost:5000/sign-in 49 | */ 50 | app.post( '/sign-in', ( req, res ) => { 51 | 52 | jwt.sign( req.body ,config.tokenSecret , { expiresIn: 3600 }, ( err, token ) => { 53 | if ( ! token ) { 54 | res.json({ success: false, error: 'Token could not be generated' }); 55 | } else { 56 | 57 | // Make a login request. 58 | axios.post( `${wordPressRestUrl}/user/login`, req.body ) 59 | .then( response => { 60 | res.json( { 61 | success: true, 62 | status: 200, 63 | token, 64 | userData: response.data.user.data 65 | } ); 66 | } ) 67 | .catch( err => { 68 | const responseReceived = err.response.data; 69 | res.status(404).json({ success: false, status: 400, errorMessage: responseReceived.message }); 70 | } ); 71 | } 72 | 73 | } ); 74 | } ); 75 | 76 | /** 77 | * Create a new post 78 | * 79 | * @route http://localhost:5000/create-post 80 | */ 81 | app.post( '/create-post', ( req, res ) => { 82 | 83 | jwt.verify(req.body.token, config.tokenSecret, function ( err, decoded ) { 84 | if ( undefined !== decoded ) { 85 | 86 | const postData = { 87 | user_id: req.body.userID, 88 | title: req.body.title, 89 | content: req.body.content, 90 | }; 91 | 92 | // Make a create post request. 93 | axios.post( `${wordPressRestUrl}/post/create`, postData ) 94 | .then( response => { 95 | 96 | res.json( { 97 | success: true, 98 | status: 200, 99 | userData: response.data 100 | } ); 101 | } ) 102 | .catch( err => { 103 | const responseReceived = err.response.data; 104 | res.status(404).json({ success: false, status: 400, errorMessage: responseReceived.message }); 105 | } ); 106 | } else { 107 | res.status( 400 ).json( { success: false, status: 400, errorMessage: 'Authorization failed'} ); 108 | } 109 | }); 110 | } ); 111 | 112 | app.get('/', (req, res) => { 113 | res.render('index') 114 | }); 115 | 116 | app.get('/login', (req, res) => { 117 | res.render('index') 118 | }); 119 | 120 | app.get('/dashboard/:userName', (req, res) => { 121 | res.render('index') 122 | }); 123 | 124 | app.get('/post/:id', (req, res) => { 125 | res.render('index') 126 | }); 127 | 128 | 129 | app.listen( process.env.PORT || 5000, () => console.log( 'Listening on port 5000' ) ); 130 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './style.css'; 3 | import { Router } from "@reach/router"; 4 | import Login from "./components/Login"; 5 | import Dashboard from "./components/dashboard/Dashboard"; 6 | import Home from "./components/Home"; 7 | import SinglePost from "./components/SinglePost"; 8 | import CreatePost from "./components/dashboard/posts/CreatePost"; 9 | import AppProvider from "./components/context/AppProvider"; 10 | import Posts from "./components/dashboard/posts/Posts"; 11 | import Pages from "./components/dashboard/pages/Pages"; 12 | import Blogs from "./components/Blogs"; 13 | import Page from "./components/Page"; 14 | 15 | class App extends React.Component { 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | } 34 | } 35 | 36 | export default App; 37 | -------------------------------------------------------------------------------- /src/client-config.js: -------------------------------------------------------------------------------- 1 | const clientConfig = { 2 | siteUrl: 'https://codeytek.com', 3 | }; 4 | 5 | export default clientConfig; 6 | -------------------------------------------------------------------------------- /src/components/Blogs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navbar from "./Navbar"; 3 | import { Posts } from "./Posts"; 4 | 5 | const Blogs = () => { 6 | 7 | return ( 8 | 9 | 10 | 11 | 12 | ) 13 | }; 14 | 15 | export default Blogs; 16 | -------------------------------------------------------------------------------- /src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navbar from "./Navbar"; 3 | import axios from 'axios'; 4 | import Loader from "../loader.gif"; 5 | import renderHTML from 'react-render-html'; 6 | import Moment from 'react-moment'; 7 | import { Link } from '@reach/router'; 8 | import clientConfig from '../client-config'; 9 | 10 | class Home extends React.Component { 11 | 12 | constructor( props ) { 13 | super( props ); 14 | 15 | this.state = { 16 | loading : false, 17 | posts: [], 18 | error: '' 19 | }; 20 | } 21 | 22 | createMarkup = ( data ) => ({ 23 | __html: data 24 | }); 25 | 26 | componentDidMount() { 27 | const wordPressSiteURL = clientConfig.siteUrl; 28 | 29 | this.setState( { loading: true }, () => { 30 | axios.get( `${wordPressSiteURL}/wp-json/wp/v2/posts/` ) 31 | .then( res => { 32 | if ( 200 === res.status ) { 33 | if ( res.data.length ) { 34 | this.setState( { loading: false, posts: res.data } ); 35 | } else { 36 | this.setState( { loading: false, error: 'No Posts Found' } ); 37 | } 38 | } 39 | 40 | } ) 41 | .catch( err => this.setState( { loading: false, error: err } ) ); 42 | } ) 43 | } 44 | 45 | render() { 46 | 47 | const { loading, posts, error } = this.state; 48 | 49 | return( 50 | 51 | 52 | { error &&
} 53 | { posts.length ? ( 54 |
55 | { posts.map( post => ( 56 |
57 |
58 | 59 | {renderHTML( post.title.rendered )} 60 | 61 |
62 |
63 |
{ renderHTML( post.excerpt.rendered ) }
64 |
65 |
66 | {post.date} 67 | 68 | Read More... 69 | 70 |
71 |
72 | ) ) } 73 |
74 | ) : '' } 75 | { loading && Loader } 76 | 77 | ); 78 | } 79 | } 80 | 81 | export default Home; 82 | -------------------------------------------------------------------------------- /src/components/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from 'react'; 2 | import Navbar from "./Navbar"; 3 | import { Redirect } from "@reach/router"; 4 | import Loader from "../loader.gif"; 5 | import axios from 'axios'; 6 | import clientConfig from '../client-config'; 7 | import AppContext from "./context/AppContext"; 8 | 9 | const Login = () => { 10 | 11 | const [ store, setStore ] = useContext( AppContext ); 12 | 13 | const [ loginFields, setLoginFields ] = useState({ 14 | username: '', 15 | password: '', 16 | userNiceName: '', 17 | userEmail: '', 18 | loading: false, 19 | error: '' 20 | }); 21 | 22 | const createMarkup = ( data ) => ({ 23 | __html: data 24 | }); 25 | 26 | const onFormSubmit = ( event ) => { 27 | event.preventDefault(); 28 | 29 | const siteUrl = clientConfig.siteUrl; 30 | 31 | const loginData = { 32 | username: loginFields.username, 33 | password: loginFields.password, 34 | }; 35 | 36 | setLoginFields( { ...loginFields, loading: true } ); 37 | 38 | axios.post( `${siteUrl}/wp-json/jwt-auth/v1/token`, loginData ) 39 | .then( res => { 40 | 41 | if ( undefined === res.data.token ) { 42 | setLoginFields( { 43 | ...loginFields, 44 | error: res.data.message, 45 | loading: false } 46 | ); 47 | return; 48 | } 49 | 50 | const { token, user_nicename, user_email } = res.data; 51 | 52 | localStorage.setItem( 'token', token ); 53 | localStorage.setItem( 'userName', user_nicename ); 54 | 55 | setStore({ 56 | ...store, 57 | userName: user_nicename, 58 | token: token 59 | }); 60 | 61 | setLoginFields( { 62 | ...loginFields, 63 | loading: false, 64 | token: token, 65 | userNiceName: user_nicename, 66 | userEmail: user_email, 67 | } ) 68 | } ) 69 | .catch( err => { 70 | setLoginFields( { ...loginFields, error: err.response.data.message, loading: false } ); 71 | } ) 72 | }; 73 | 74 | const handleOnChange = ( event ) => { 75 | setLoginFields( { ...loginFields, [event.target.name]: event.target.value } ); 76 | }; 77 | 78 | 79 | const { username, password, userNiceName, error, loading } = loginFields; 80 | 81 | if ( store.token ) { 82 | return ( ) 83 | } else { 84 | return ( 85 | 86 | 87 |
88 |

Login

89 | { error &&
} 90 |
91 | 101 |
102 | 112 |
113 | 114 | { loading && Loader } 115 |
116 |
117 | 118 | ) 119 | } 120 | }; 121 | 122 | export default Login; 123 | -------------------------------------------------------------------------------- /src/components/NavLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from "@reach/router"; 3 | 4 | /** 5 | * Reach Routers gives us access to a function called getProps. 6 | * Whatever is returned by getProps(), in this case style, 7 | * will be applied to the Link attribute as props. 8 | * So here {...props} will be replaced by style: {} 9 | * 10 | * @param props 11 | * @return {*} 12 | * @constructor 13 | */ 14 | const NavLink = props => ( 15 | ( { style: { color: isCurrent ? '#fff' : '#fffc' } } )} 18 | className="nav-link" 19 | /> 20 | ); 21 | 22 | export default NavLink; 23 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import NavLink from './NavLink'; 3 | import { isLoggedIn } from "./functions"; 4 | import ToggleSidebarBtn from "./dashboard/sidebar/ToggleSidebarBtn"; 5 | import AppContext from "./context/AppContext"; 6 | 7 | const Navbar = () => { 8 | 9 | const [ store, setStore ] = useContext( AppContext ); 10 | 11 | const handleLogout = () => { 12 | localStorage.removeItem( 'token' ); 13 | localStorage.removeItem( 'useName' ); 14 | 15 | setStore( { 16 | ...store, 17 | token: '', 18 | useName: '' 19 | } ); 20 | window.location.href = '/'; 21 | }; 22 | 23 | return ( 24 | 55 | ); 56 | }; 57 | 58 | export default Navbar; 59 | -------------------------------------------------------------------------------- /src/components/Page.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navbar from "./Navbar"; 3 | import Posts from "./Posts"; 4 | 5 | const Page = ( props ) => { 6 | 7 | const { id } = props; 8 | 9 | return ( 10 | 11 | 12 | 13 | 14 | ) 15 | }; 16 | 17 | export default Page; 18 | -------------------------------------------------------------------------------- /src/components/Posts.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import clientConfig from "../client-config"; 3 | import axios from "axios"; 4 | import Loader from "./layouts/Loader"; 5 | import { Post } from "./layouts/Post"; 6 | import { Pagination } from "./layouts/Pagination"; 7 | import PostLoader from "./layouts/PostLoader"; 8 | 9 | export const Posts = ( props ) => { 10 | 11 | const pageId = parseInt( props.pageId ); 12 | 13 | const [currentPage, setCurrentPage] = useState( pageId ); 14 | 15 | const [totalPages, setTotalPages] = useState( 1 ); 16 | const [loading, setLoading] = useState( false ); 17 | const [errMessage, setError] = useState( '' ); 18 | const [posts, setPosts] = useState( null ); 19 | 20 | useEffect( () => { 21 | 22 | const wordPressSiteURL = clientConfig.siteUrl; 23 | 24 | setLoading( true ); 25 | 26 | axios.get( `${ wordPressSiteURL }/wp-json/rae/v1/posts?page_no=${ currentPage }` ) 27 | .then( res => { 28 | 29 | setLoading( false ); 30 | 31 | if ( 200 === res.data.status ) { 32 | setPosts( res.data.posts_data ); 33 | setTotalPages( res.data.page_count ) 34 | } else { 35 | setError( 'No posts found' ); 36 | } 37 | } ) 38 | .catch( err => { 39 | setError( err.response.data.message ); 40 | } ); 41 | 42 | }, [currentPage] ); 43 | 44 | const getPosts = ( posts ) => { 45 | return posts.map( post => ); 46 | }; 47 | 48 | return ( 49 | 50 | { loading ? : '' } 51 |
52 | { ( !loading && null !== posts && posts.length ) ? ( 53 | 54 | { getPosts( posts ) } 55 | 60 | 61 | ) :
{ errMessage }
} 62 |
63 |
64 | ) 65 | 66 | }; 67 | 68 | export default Posts; 69 | -------------------------------------------------------------------------------- /src/components/SinglePost.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navbar from "./Navbar"; 3 | import renderHTML from "react-render-html"; 4 | import Moment from "react-moment"; 5 | import Loader from "../loader.gif"; 6 | import axios from "axios"; 7 | import clientConfig from "../client-config"; 8 | 9 | class SinglePost extends React.Component { 10 | 11 | constructor( props ) { 12 | super( props ); 13 | 14 | this.state = { 15 | loading : false, 16 | post: {}, 17 | error: '' 18 | }; 19 | } 20 | 21 | createMarkup = ( data ) => ({ 22 | __html: data 23 | }); 24 | 25 | componentDidMount() { 26 | const wordPressSiteURL = clientConfig.siteUrl; 27 | 28 | this.setState( { loading: true }, () => { 29 | axios.get( `${wordPressSiteURL}/wp-json/wp/v2/posts/${this.props.id}` ) 30 | .then( res => { 31 | 32 | if ( Object.keys( res.data ).length ) { 33 | this.setState( { loading: false, post: res.data } ); 34 | } else { 35 | this.setState( { loading: false, error: 'No Posts Found' } ); 36 | } 37 | } ) 38 | .catch( err => this.setState( { loading: false, error: err.response.data.message } ) ); 39 | } ) 40 | } 41 | 42 | render() { 43 | 44 | const { loading, post, error } = this.state; 45 | 46 | return( 47 | 48 | 49 | { error &&
} 50 | { Object.keys( post ).length ? ( 51 |
52 |
53 |
54 | {renderHTML( post.title.rendered )} 55 |
56 |
57 |
{ renderHTML( post.content.rendered ) }
58 |
59 |
{post.date}
60 |
61 |
62 | ) : '' } 63 | { loading && Loader } 64 | 65 | ) 66 | } 67 | } 68 | 69 | export default SinglePost; 70 | -------------------------------------------------------------------------------- /src/components/content/Content.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import Navbar from "../Navbar"; 3 | import AppContext from "../context/AppContext"; 4 | 5 | const Content = ( props ) => { 6 | 7 | const [ store, setStore ] = useContext( AppContext ); 8 | 9 | return ( 10 |
11 | {/* Top Navbar */} 12 | 13 | {/* Main Content */} 14 |
15 | { props.children } 16 |
17 |
18 | ) 19 | }; 20 | 21 | export default Content; 22 | -------------------------------------------------------------------------------- /src/components/context/AppContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react'; 2 | 3 | const AppContext = createContext([ 4 | {}, 5 | () => {} 6 | ]); 7 | 8 | export default AppContext; 9 | -------------------------------------------------------------------------------- /src/components/context/AppProvider.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import AppContext from "./AppContext"; 3 | 4 | const AppProvider = ( props ) => { 5 | 6 | const [ store, setStore ] = useState( { 7 | userName: '', 8 | token: '', 9 | activeMenu: {}, 10 | sidebarActive: true 11 | } ); 12 | 13 | useEffect( () => { 14 | const token = localStorage.getItem( 'token' ); 15 | const userName = localStorage.getItem( 'userName' ); 16 | 17 | setStore( { ...store, token, userName } ); 18 | 19 | }, [] ); 20 | 21 | return ( 22 | 23 | { props.children } 24 | 25 | ) 26 | 27 | }; 28 | 29 | export default AppProvider; 30 | -------------------------------------------------------------------------------- /src/components/dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DashboardLayout from "./../layouts/DashboardLayout"; 3 | import { getUserName } from "../functions"; 4 | 5 | const Dashboard = ( props ) => { 6 | 7 | const userName = ( getUserName() ) ? getUserName() : ''; 8 | 9 | return( 10 | 11 | { userName ?

Welcome { userName }!!

: '' } 12 |
13 | ) 14 | }; 15 | 16 | export default Dashboard; 17 | -------------------------------------------------------------------------------- /src/components/dashboard/pages/Pages.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DashboardLayout from "../../layouts/DashboardLayout"; 3 | 4 | const Pages = () => { 5 | return ( 6 | 7 | Pages 8 | 9 | ) 10 | }; 11 | 12 | export default Pages; 13 | -------------------------------------------------------------------------------- /src/components/dashboard/posts/CreatePost.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DashboardLayout from "../../layouts/DashboardLayout"; 3 | import Loader from '../../../loader.gif'; 4 | import clientConfig from "../../../client-config"; 5 | import axios from 'axios'; 6 | 7 | class CreatePost extends React.Component { 8 | 9 | constructor( props ) { 10 | super( props ); 11 | 12 | this.state = { 13 | title: '', 14 | content: '', 15 | postCreated: false, 16 | loading: false, 17 | message: '', 18 | } 19 | 20 | } 21 | 22 | createMarkup = ( data ) => ({ 23 | __html: data 24 | }); 25 | 26 | handleFormSubmit = ( event ) => { 27 | event.preventDefault(); 28 | 29 | this.setState( { loading: true } ); 30 | 31 | const formData = { 32 | title: this.state.title, 33 | content: this.state.content, 34 | status: 'publish' 35 | }; 36 | 37 | const wordPressSiteUrl = clientConfig.siteUrl; 38 | const authToken = localStorage.getItem( 'token' ); 39 | 40 | // Post request to create a post 41 | axios.post( `${ wordPressSiteUrl }/wp-json/wp/v2/posts`, formData, { 42 | headers: { 43 | 'Content-Type': 'application/json', 44 | 'Authorization': `Bearer ${ authToken }` 45 | } 46 | } ) 47 | .then( res => { 48 | 49 | this.setState( { 50 | loading: false, 51 | postCreated: !! res.data.id, 52 | message: res.data.id ? 'New post created' : '' 53 | } ) 54 | } ) 55 | .catch( err => {{ 56 | 57 | this.setState( { loading: false, message: err.response.data.message } ) 58 | }} ) 59 | }; 60 | 61 | handleInputChange = ( event ) => { 62 | 63 | this.setState( { [ event.target.name ]: event.target.value } ); 64 | 65 | }; 66 | 67 | render() { 68 | 69 | const { loading, message, postCreated } = this.state; 70 | 71 | return( 72 | 73 |
74 | Create Post 75 | 76 | { message ?
: ''} 77 | 78 | {/*Title*/} 79 |
80 | 81 | 82 |
83 | 84 | {/* Content*/} 85 |
86 | 87 |