)
78 | }
79 |
80 | return (
81 |
82 |
83 |
84 | Sign In
85 |
86 |
87 |
88 |
{
89 | values.error && (
90 | error
91 | {values.error}
92 | )
93 | }
94 |
95 |
96 |
97 |
98 |
99 | )
100 | }
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MERN Skeleton 2.0
2 | - *Looking for the first edition code? [Check here](https://github.com/shamahoque/mern-skeleton/tree/master)*
3 |
4 | A skeleton application with basic user CRUD and auth features - developed using React, Node, Express and MongoDB.
5 |
6 | 
7 |
8 | ### [Live Demo](http://skeleton2.mernbook.com/ "MERN Skeleton")
9 |
10 | #### What you need to run this code
11 | 1. Node (13.12.0)
12 | 2. NPM (6.14.4) or Yarn (1.22.4)
13 | 3. MongoDB (4.2.0)
14 |
15 | #### How to run this code
16 | 1. Make sure MongoDB is running on your system
17 | 2. Clone this repository
18 | 3. Open command line in the cloned folder,
19 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
20 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
21 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
22 | ----
23 | ### More applications built by extending this skeleton
24 |
25 | * [MERN Social](https://github.com/shamahoque/mern-social/tree/second-edition)
26 | * [MERN Classroom](https://github.com/shamahoque/mern-classroom)
27 | * [MERN Marketplace](https://github.com/shamahoque/mern-marketplace/tree/second-edition)
28 | * [MERN Expense Tracker](https://github.com/shamahoque/mern-expense-tracker)
29 | * [MERN Mediastream](https://github.com/shamahoque/mern-mediastream/tree/second-edition)
30 | * [MERN VR Game](https://github.com/shamahoque/mern-vrgame/tree/second-edition)
31 |
32 | Learn more at [mernbook.com](http://www.mernbook.com/)
33 |
34 | ----
35 | ## Get the book
36 | #### [Full-Stack React Projects - Second Edition](https://www.packtpub.com/web-development/full-stack-react-projects-second-edition)
37 | *Learn MERN stack development by building modern web apps using MongoDB, Express, React, and Node.js*
38 |
39 |
40 |
41 | React combined with industry-tested, server-side technologies, such as Node, Express, and MongoDB, enables you to develop and deploy robust real-world full-stack web apps. This updated second edition focuses on the latest versions and conventions of the technologies in this stack, along with their new features such as Hooks in React and async/await in JavaScript. The book also explores advanced topics such as implementing real-time bidding, a web-based classroom app, and data visualization in an expense tracking app.
42 |
43 | Full-Stack React Projects will take you through the process of preparing the development environment for MERN stack-based web development, creating a basic skeleton app, and extending it to build six different web apps. You'll build apps for social media, classrooms, media streaming, online marketplaces with real-time bidding, and web-based games with virtual reality features. Throughout the book, you'll learn how MERN stack web development works, extend its capabilities for complex features, and gain actionable insights into creating MERN-based apps, along with exploring industry best practices to meet the ever-increasing demands of the real world.
44 |
45 | Things you'll learn in this book:
46 |
47 | - Extend a MERN-based application to build a variety of applications
48 | - Add real-time communication capabilities with Socket.IO
49 | - Implement data visualization features for React applications using Victory
50 | - Develop media streaming applications using MongoDB GridFS
51 | - Improve SEO for your MERN apps by implementing server-side rendering with data
52 | - Implement user authentication and authorization using JSON web tokens
53 | - Set up and use React 360 to develop user interfaces with VR capabilities
54 | - Make your MERN stack applications reliable and scalable with industry best practices
55 |
56 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1839215410) today!
57 |
58 | ---
59 |
--------------------------------------------------------------------------------
/client/user/Signup.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react'
2 | import Card from '@material-ui/core/Card'
3 | import CardActions from '@material-ui/core/CardActions'
4 | import CardContent from '@material-ui/core/CardContent'
5 | import Button from '@material-ui/core/Button'
6 | import TextField from '@material-ui/core/TextField'
7 | import Typography from '@material-ui/core/Typography'
8 | import Icon from '@material-ui/core/Icon'
9 | import { makeStyles } from '@material-ui/core/styles'
10 | import {create} from './api-user.js'
11 | import Dialog from '@material-ui/core/Dialog'
12 | import DialogActions from '@material-ui/core/DialogActions'
13 | import DialogContent from '@material-ui/core/DialogContent'
14 | import DialogContentText from '@material-ui/core/DialogContentText'
15 | import DialogTitle from '@material-ui/core/DialogTitle'
16 | import {Link} from 'react-router-dom'
17 |
18 | const useStyles = makeStyles(theme => ({
19 | card: {
20 | maxWidth: 600,
21 | margin: 'auto',
22 | textAlign: 'center',
23 | marginTop: theme.spacing(5),
24 | paddingBottom: theme.spacing(2)
25 | },
26 | error: {
27 | verticalAlign: 'middle'
28 | },
29 | title: {
30 | marginTop: theme.spacing(2),
31 | color: theme.palette.openTitle
32 | },
33 | textField: {
34 | marginLeft: theme.spacing(1),
35 | marginRight: theme.spacing(1),
36 | width: 300
37 | },
38 | submit: {
39 | margin: 'auto',
40 | marginBottom: theme.spacing(2)
41 | }
42 | }))
43 |
44 | export default function Signup() {
45 | const classes = useStyles()
46 | const [values, setValues] = useState({
47 | name: '',
48 | password: '',
49 | email: '',
50 | open: false,
51 | error: ''
52 | })
53 |
54 | const handleChange = name => event => {
55 | setValues({ ...values, [name]: event.target.value })
56 | }
57 |
58 | const clickSubmit = () => {
59 | const user = {
60 | name: values.name || undefined,
61 | email: values.email || undefined,
62 | password: values.password || undefined
63 | }
64 | create(user).then((data) => {
65 | if (data.error) {
66 | setValues({ ...values, error: data.error})
67 | } else {
68 | setValues({ ...values, error: '', open: true})
69 | }
70 | })
71 | }
72 |
73 | return (
74 |
75 |
76 |
77 | Sign Up
78 |
79 |
80 |
81 |
82 |
{
83 | values.error && (
84 | error
85 | {values.error})
86 | }
87 |
88 |
89 |
90 |
91 |
92 |
107 |
108 | )
109 | }
--------------------------------------------------------------------------------
/client/user/EditProfile.js:
--------------------------------------------------------------------------------
1 | import React, {useState, useEffect} from 'react'
2 | import Card from '@material-ui/core/Card'
3 | import CardActions from '@material-ui/core/CardActions'
4 | import CardContent from '@material-ui/core/CardContent'
5 | import Button from '@material-ui/core/Button'
6 | import TextField from '@material-ui/core/TextField'
7 | import Typography from '@material-ui/core/Typography'
8 | import Icon from '@material-ui/core/Icon'
9 | import { makeStyles } from '@material-ui/core/styles'
10 | import auth from './../auth/auth-helper'
11 | import {read, update} from './api-user.js'
12 | import {Redirect} from 'react-router-dom'
13 |
14 | const useStyles = makeStyles(theme => ({
15 | card: {
16 | maxWidth: 600,
17 | margin: 'auto',
18 | textAlign: 'center',
19 | marginTop: theme.spacing(5),
20 | paddingBottom: theme.spacing(2)
21 | },
22 | title: {
23 | margin: theme.spacing(2),
24 | color: theme.palette.protectedTitle
25 | },
26 | error: {
27 | verticalAlign: 'middle'
28 | },
29 | textField: {
30 | marginLeft: theme.spacing(1),
31 | marginRight: theme.spacing(1),
32 | width: 300
33 | },
34 | submit: {
35 | margin: 'auto',
36 | marginBottom: theme.spacing(2)
37 | }
38 | }))
39 |
40 | export default function EditProfile({ match }) {
41 | const classes = useStyles()
42 | const [values, setValues] = useState({
43 | name: '',
44 | password: '',
45 | email: '',
46 | open: false,
47 | error: '',
48 | redirectToProfile: false
49 | })
50 | const jwt = auth.isAuthenticated()
51 |
52 | useEffect(() => {
53 | const abortController = new AbortController()
54 | const signal = abortController.signal
55 |
56 | read({
57 | userId: match.params.userId
58 | }, {t: jwt.token}, signal).then((data) => {
59 | if (data && data.error) {
60 | setValues({...values, error: data.error})
61 | } else {
62 | setValues({...values, name: data.name, email: data.email})
63 | }
64 | })
65 | return function cleanup(){
66 | abortController.abort()
67 | }
68 |
69 | }, [match.params.userId])
70 |
71 | const clickSubmit = () => {
72 | const user = {
73 | name: values.name || undefined,
74 | email: values.email || undefined,
75 | password: values.password || undefined
76 | }
77 | update({
78 | userId: match.params.userId
79 | }, {
80 | t: jwt.token
81 | }, user).then((data) => {
82 | if (data && data.error) {
83 | setValues({...values, error: data.error})
84 | } else {
85 | setValues({...values, userId: data._id, redirectToProfile: true})
86 | }
87 | })
88 | }
89 | const handleChange = name => event => {
90 | setValues({...values, [name]: event.target.value})
91 | }
92 |
93 | if (values.redirectToProfile) {
94 | return ()
95 | }
96 | return (
97 |
98 |
99 |
100 | Edit Profile
101 |
102 |
103 |
104 |
105 |
{
106 | values.error && (
107 | error
108 | {values.error}
109 | )
110 | }
111 |
112 |
113 |
114 |
115 |
116 | )
117 | }
118 |
119 |
--------------------------------------------------------------------------------