Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broken Proxy* #41

Open
wants to merge 51 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
43ed191
mailer
StephenGrider Jul 17, 2017
598ba5e
email boilerplate
StephenGrider Jul 17, 2017
fcfde28
send emails
StephenGrider Jul 17, 2017
7faa7b6
test emails
StephenGrider Jul 17, 2017
454eeba
improve template
StephenGrider Jul 17, 2017
c8e3cfd
added error checking
StephenGrider Jul 18, 2017
821bb80
feedback for feedback
StephenGrider Jul 18, 2017
14de7d3
Update diagrams.xml
StephenGrider Jul 18, 2017
a078497
Update diagrams.xml
StephenGrider Jul 18, 2017
de5aaee
dashboard
StephenGrider Jul 18, 2017
c0b0080
material icons
StephenGrider Jul 18, 2017
e4f3655
nav with link
StephenGrider Jul 18, 2017
ef649f5
finish redux form
StephenGrider Jul 20, 2017
f1400ab
Update diagrams.xml
StephenGrider Jul 20, 2017
a90f0fe
Update diagrams.xml
StephenGrider Jul 20, 2017
b348d35
prop names
StephenGrider Jul 20, 2017
512a9e4
Update diagrams.xml
StephenGrider Jul 20, 2017
6ed860e
redirect
StephenGrider Jul 20, 2017
dcebea4
Update diagrams.xml
StephenGrider Jul 20, 2017
9b778b5
Create 07
StephenGrider Jul 20, 2017
95b1e75
Delete 07
StephenGrider Jul 20, 2017
28f2313
Create .gitkeep
StephenGrider Jul 20, 2017
1393cdf
Added diagrams.xml
StephenGrider Jul 20, 2017
11628df
added localtunnel
StephenGrider Jul 20, 2017
9e6ebae
test webhook
StephenGrider Jul 20, 2017
fa6e54d
encode survey data
StephenGrider Jul 20, 2017
1f1d49a
parse route
StephenGrider Jul 20, 2017
697c11e
uniqueness
StephenGrider Jul 20, 2017
dcf011b
lodash chain
StephenGrider Jul 20, 2017
f4c66f5
bad query
StephenGrider Jul 21, 2017
5bac251
add query
StephenGrider Jul 21, 2017
0b8c497
execute query
StephenGrider Jul 21, 2017
36caaaa
added stuff
StephenGrider Jul 21, 2017
ee90b61
fetch list of surveys
StephenGrider Jul 21, 2017
5479139
whitelist fields
StephenGrider Jul 21, 2017
b573500
wiring surveys
StephenGrider Jul 21, 2017
9e49251
wire react to redux
StephenGrider Jul 21, 2017
8d1a0ea
survey-list
StephenGrider Jul 21, 2017
d8525e1
reverse list
StephenGrider Jul 21, 2017
b1c830b
Merge branch 'master' of github.com:StephenGrider/FullstackReactCode
StephenGrider Jul 21, 2017
6f66b1d
Delete diagrams.xml
StephenGrider Jul 21, 2017
0a6543d
Added diagrams.xml
StephenGrider Jul 21, 2017
2e04d65
Merge branch 'master' of github.com:StephenGrider/FullstackReactCode
StephenGrider Jul 21, 2017
07c8983
Update diagrams.xml
StephenGrider Jul 23, 2017
6842459
Delete .DS_Store
StephenGrider Jul 24, 2017
f0bb84b
Delete .DS_Store
StephenGrider Jul 24, 2017
5e84779
Delete .DS_Store
StephenGrider Jul 24, 2017
9157079
Create README.md
StephenGrider Jul 26, 2017
da3cbbc
Delete 001 - process.png
StephenGrider Jul 26, 2017
05bb635
Broken Proxy*
joshuaaguilar20 Nov 24, 2018
23ab0cb
Delete package.json
joshuaaguilar20 Nov 26, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# FullstackReactCode

Companion repo to a course on Udemy.com. See here: https://www.udemy.com/node-with-react-fullstack-web-development
Binary file removed diagrams/.DS_Store
Binary file not shown.
Binary file removed diagrams/01/001 - process.png
Binary file not shown.
2 changes: 1 addition & 1 deletion diagrams/01/diagrams.xml

Large diffs are not rendered by default.

Binary file removed diagrams/04/.DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion diagrams/05/diagrams.xml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion diagrams/06/diagrams.xml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions diagrams/07/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions diagrams/07/diagrams.xml

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions server/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 0 additions & 31 deletions server/client/package.json

This file was deleted.

1 change: 1 addition & 0 deletions server/client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
15 changes: 14 additions & 1 deletion server/client/src/actions/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from 'axios';
import { FETCH_USER } from './types';
import { FETCH_USER, FETCH_SURVEYS } from './types';

export const fetchUser = () => async dispatch => {
const res = await axios.get('/api/current_user');
Expand All @@ -12,3 +12,16 @@ export const handleToken = token => async dispatch => {

dispatch({ type: FETCH_USER, payload: res.data });
};

export const submitSurvey = (values, history) => async dispatch => {
const res = await axios.post('/api/surveys', values);

history.push('/surveys');
dispatch({ type: FETCH_USER, payload: res.data });
};

export const fetchSurveys = () => async dispatch => {
const res = await axios.get('/api/surveys');

dispatch({ type: FETCH_SURVEYS, payload: res.data });
};
1 change: 1 addition & 0 deletions server/client/src/actions/types.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const FETCH_USER = 'fetch_user';
export const FETCH_SURVEYS = 'fetch_surveys';
4 changes: 2 additions & 2 deletions server/client/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import * as actions from '../actions';

import Header from './Header';
import Landing from './Landing';
const Dashboard = () => <h2>Dashboard</h2>;
const SurveyNew = () => <h2>SurveyNew</h2>;
import Dashboard from './Dashboard';
import SurveyNew from './surveys/SurveyNew';

class App extends Component {
componentDidMount() {
Expand Down
18 changes: 18 additions & 0 deletions server/client/src/components/Dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { Link } from 'react-router-dom';
import SurveyList from './surveys/SurveyList';

const Dashboard = () => {
return (
<div>
<SurveyList />
<div className="fixed-action-btn">
<Link to="/surveys/new" className="btn-floating btn-large red">
<i className="material-icons">add</i>
</Link>
</div>
</div>
);
};

export default Dashboard;
15 changes: 15 additions & 0 deletions server/client/src/components/surveys/SurveyField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SurveyField contains logic to render a single
// label and text input
import React from 'react';

export default ({ input, label, meta: { error, touched } }) => {
return (
<div>
<label>{label}</label>
<input {...input} style={{ marginBottom: '5px' }} />
<div className="red-text" style={{ marginBottom: '20px' }}>
{touched && error}
</div>
</div>
);
};
61 changes: 61 additions & 0 deletions server/client/src/components/surveys/SurveyForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SurveyForm shows a form for a user to add input
import _ from 'lodash';
import React, { Component } from 'react';
import { reduxForm, Field } from 'redux-form';
import { Link } from 'react-router-dom';
import SurveyField from './SurveyField';
import validateEmails from '../../utils/validateEmails';
import formFields from './formFields';

class SurveyForm extends Component {
renderFields() {
return _.map(formFields, ({ label, name }) => {
return (
<Field
key={name}
component={SurveyField}
type="text"
label={label}
name={name}
/>
);
});
}

render() {
return (
<div>
<form onSubmit={this.props.handleSubmit(this.props.onSurveySubmit)}>
{this.renderFields()}
<Link to="/surveys" className="red btn-flat white-text">
Cancel
</Link>
<button type="submit" className="teal btn-flat right white-text">
Next
<i className="material-icons right">done</i>
</button>
</form>
</div>
);
}
}

function validate(values) {
const errors = {};

errors.recipients = validateEmails(values.recipients || '');

_.each(formFields, ({ name }) => {
if (!values[name]) {
errors[name] = 'You must provide a value';
}
});

return errors;
}

export default reduxForm({
validate,
form: 'surveyForm',
destroyOnUnmount: false
})(SurveyForm);
46 changes: 46 additions & 0 deletions server/client/src/components/surveys/SurveyFormReview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SurveyFormReview shows users their form inputs for review
import _ from 'lodash';
import React from 'react';
import { connect } from 'react-redux';
import formFields from './formFields';
import { withRouter } from 'react-router-dom';
import * as actions from '../../actions';

const SurveyFormReview = ({ onCancel, formValues, submitSurvey, history }) => {
const reviewFields = _.map(formFields, ({ name, label }) => {
return (
<div key={name}>
<label>{label}</label>
<div>
{formValues[name]}
</div>
</div>
);
});

return (
<div>
<h5>Please confirm your entries</h5>
{reviewFields}
<button
className="yellow darken-3 white-text btn-flat"
onClick={onCancel}
>
Back
</button>
<button
onClick={() => submitSurvey(formValues, history)}
className="green btn-flat right white-text"
>
Send Survey
<i className="material-icons right">email</i>
</button>
</div>
);
};

function mapStateToProps(state) {
return { formValues: state.form.surveyForm.values };
}

export default connect(mapStateToProps, actions)(withRouter(SurveyFormReview));
45 changes: 45 additions & 0 deletions server/client/src/components/surveys/SurveyList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchSurveys } from '../../actions';

class SurveyList extends Component {
componentDidMount() {
this.props.fetchSurveys();
}

renderSurveys() {
return this.props.surveys.reverse().map(survey => {
return (
<div className="card darken-1" key={survey._id}>
<div className="card-content">
<span className="card-title">{survey.title}</span>
<p>
{survey.body}
</p>
<p className="right">
Sent On: {new Date(survey.dateSent).toLocaleDateString()}
</p>
</div>
<div className="card-action">
<a>Yes: {survey.yes}</a>
<a>No: {survey.no}</a>
</div>
</div>
);
});
}

render() {
return (
<div>
{this.renderSurveys()}
</div>
);
}
}

function mapStateToProps({ surveys }) {
return { surveys };
}

export default connect(mapStateToProps, { fetchSurveys })(SurveyList);
37 changes: 37 additions & 0 deletions server/client/src/components/surveys/SurveyNew.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SurveyNew shows SurveyForm and SurveyFormReview
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import SurveyForm from './SurveyForm';
import SurveyFormReview from './SurveyFormReview';

class SurveyNew extends Component {
state = { showFormReview: false };

renderContent() {
if (this.state.showFormReview) {
return (
<SurveyFormReview
onCancel={() => this.setState({ showFormReview: false })}
/>
);
}

return (
<SurveyForm
onSurveySubmit={() => this.setState({ showFormReview: true })}
/>
);
}

render() {
return (
<div>
{this.renderContent()}
</div>
);
}
}

export default reduxForm({
form: 'surveyForm'
})(SurveyNew);
6 changes: 6 additions & 0 deletions server/client/src/components/surveys/formFields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default [
{ label: 'Campaign Title', name: 'title' },
{ label: 'Subject Line', name: 'subject' },
{ label: 'Email Body', name: 'body' },
{ label: 'Recipient List', name: 'recipients' }
];
4 changes: 4 additions & 0 deletions server/client/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import reduxThunk from 'redux-thunk';
import App from './components/App';
import reducers from './reducers';

// Development only axios helpers!
import axios from 'axios';
window.axios = axios;

const store = createStore(reducers, {}, applyMiddleware(reduxThunk));

ReactDOM.render(
Expand Down
6 changes: 5 additions & 1 deletion server/client/src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { combineReducers } from 'redux';
import { reducer as reduxForm } from 'redux-form';
import authReducer from './authReducer';
import surveysReducer from './surveysReducer';

export default combineReducers({
auth: authReducer
auth: authReducer,
form: reduxForm,
surveys: surveysReducer
});
10 changes: 10 additions & 0 deletions server/client/src/reducers/surveysReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FETCH_SURVEYS } from '../actions/types';

export default function(state = [], action) {
switch (action.type) {
case FETCH_SURVEYS:
return action.payload;
default:
return state;
}
}
14 changes: 14 additions & 0 deletions server/client/src/utils/validateEmails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const re = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

export default emails => {
const invalidEmails = emails
.split(',')
.map(email => email.trim())
.filter(email => re.test(email) === false);

if (invalidEmails.length) {
return `These emails are invalid: ${invalidEmails}`;
}

return;
};
Loading