Initial commit

This commit is contained in:
Arjun Patel
2019-06-09 22:42:29 -07:00
commit c561cdc0a2
29 changed files with 8726 additions and 0 deletions
+196
View File
@@ -0,0 +1,196 @@
import { authConstants, alertConstants } from '../constants';
import { dataService } from '../services';
import { history } from '../helpers';
function donorLogin(email, password) {
return dispatch => {
dispatch(request({ email }));
dataService(
dispatch,
'post',
'/api/auth/donor/login/',
{ email, password },
true
)
.then(data => {
if (data.token) {
// store jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem(
'auth',
JSON.stringify({ userType: 'donor', token: data.token })
);
}
// can use for permissions
data.user.type = 'donor';
dispatch(success({ message: data.message, user: data.user }));
history.push('/main');
})
.catch(error => {
dispatch(failure(error));
});
};
function request(user) {
return { type: authConstants.DONOR_LOGIN_REQUEST, payload: user };
}
function success(data) {
return { type: authConstants.DONOR_LOGIN_SUCCESS, payload: data };
}
function failure(error) {
return { type: authConstants.DONOR_LOGIN_FAILURE, payload: error };
}
}
function orgLogin(email, password) {
return dispatch => {
dispatch(request({ email }));
dataService(
dispatch,
'post',
'/api/auth/org/login/',
{ email, password },
true
)
.then(data => {
if (data.token) {
// store jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem(
'auth',
JSON.stringify({ token: data.token, userType: 'org' })
);
}
// can use for permissions
data.user.type = 'organization';
dispatch(success({ message: data.message, user: data.user }));
history.push('/org/main');
})
.catch(error => {
dispatch(failure(error));
});
};
function request(user) {
return { type: authConstants.ORGANIZATION_LOGIN_REQUEST, payload: user };
}
function success(data) {
return { type: authConstants.ORGANIZATION_LOGIN_SUCCESS, payload: data };
}
function failure(error) {
return { type: authConstants.ORGANIZATION_LOGIN_FAILURE, payload: error };
}
}
// synchronous/blocks
function logout() {
localStorage.removeItem('auth');
history.push('/');
return { type: authConstants.LOGOUT };
}
function resetPasswordEmail(email, userType) {
return dispatch => {
dispatch(request({ email }));
dataService(
dispatch,
'get',
'/api/auth/resetpassword',
{ email, usertype: userType },
false
)
.then(data => {
dispatch(success(data));
history.push('/');
dispatch({
type: alertConstants.SHOW_SNACK_BAR,
alertMessage: 'Sent Password Reset Email!'
});
})
.catch(error => {
dispatch(failure(error));
dispatch({
type: alertConstants.SHOW_SNACK_BAR,
alertMessage: error.data.error
});
});
};
function request(user) {
return {
type: authConstants.USER_RESET_PASSWORD_EMAIL_REQUEST,
payload: user
};
}
function success(data) {
return {
type: authConstants.USER_RESET_PASSWORD_EMAIL_SUCCESS,
payload: data
};
}
function failure(error) {
return {
type: authConstants.USER_RESET_PASSWORD_EMAIL_FAILURE,
payload: error
};
}
}
function resetPassword(password, code) {
return dispatch => {
dispatch(request());
dataService(
dispatch,
'post',
'/api/auth/resetpassword',
{ password, code },
false
)
.then(data => {
dispatch(success(data));
history.push('/');
dispatch({
type: alertConstants.SHOW_SNACK_BAR,
alertMessage: 'Successfully reset password!'
});
})
.catch(error => {
dispatch(failure(error));
dispatch({
type: alertConstants.SHOW_SNACK_BAR,
alertMessage: error.data.error
});
});
};
function request(user) {
return { type: authConstants.USER_RESET_PASSWORD_REQUEST, payload: user };
}
function success(data) {
return { type: authConstants.USER_RESET_PASSWORD_SUCCESS, payload: data };
}
function failure(error) {
return { type: authConstants.USER_RESET_PASSWORD_FAILURE, payload: error };
}
}
export const authActions = {
donorLogin,
orgLogin,
logout,
resetPasswordEmail,
resetPassword
};
+1
View File
@@ -0,0 +1 @@
export * from './auth.actions';
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

+39
View File
@@ -0,0 +1,39 @@
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={props => {
return JSON.parse(localStorage.getItem('auth')).userType ==
rest.userType ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: rest.rejectPath, state: { from: props.location } }}
/>
);
}}
/>
);
};
import { connect } from 'react-redux';
const mapStateToProps = state => {
return {
authentication: state.authentication
};
};
const mapDispatchToProps = dispatch => {
return {
login: (email, password) => dispatch(authActions.login(email, password))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PrivateRoute);
+28
View File
@@ -0,0 +1,28 @@
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import styles from './root.less';
import PrivateRoute from './PrivateRoute';
import backgroundImage from '../assets/images/mainbg.jpg';
const Root = props => {
return (
<React.Fragment>
{/* Toast for notifications */}
<div className={styles.mainCont}>
<input
className={styles.searchBar}
placeholder="Do Everything You Want"
/>
<img className={styles.backgroundImage} src={backgroundImage} />
</div>
<Switch />
</React.Fragment>
);
};
export default Root;
+38
View File
@@ -0,0 +1,38 @@
.mainCont {
position: relative;
overflow: hidden;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
.searchBar {
display: block;
margin: 0;
padding: var(--inputPaddingV) var(--inputPaddingH);
color: inherit;
font-family: inherit;
font-size: var(--inputFontSize);
font-weight: inherit;
line-height: var(--inputLineHeight);
border: none;
border-radius: 0.4rem;
transition: box-shadow var(--transitionDuration);
padding: 1em;
box-shadow: 0 0 8px #666;
width: 400px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.backgroundImage {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.7;
z-index: -1;
}
}
+19
View File
@@ -0,0 +1,19 @@
export const authConstants = {
DONOR_LOGIN_REQUEST: 'DONOR_LOGIN_REQUEST',
DONOR_LOGIN_SUCCESS: 'DONOR_LOGIN_SUCCESS',
DONOR_LOGIN_FAILURE: 'DONOR_LOGIN_FAILURE',
ORGANIZATION_LOGIN_REQUEST: 'ORGANIZATION_LOGIN_REQUEST',
ORGANIZATION_LOGIN_SUCCESS: 'ORGANIZATION_LOGIN_SUCCESS',
ORGANIZATION_LOGIN_FAILURE: 'ORGANIZATION_LOGIN_FAILURE',
LOGOUT: 'USER_LOGOUT',
USER_RESET_PASSWORD_EMAIL_REQUEST: 'USER_RESET_PASSWORD_EMAIL_REQUEST',
USER_RESET_PASSWORD_EMAIL_SUCCESS: 'USER_RESET_PASSWORD_EMAIL_SUCCESS',
USER_RESET_PASSWORD_EMAIL_FAILURE: 'USER_RESET_PASSWORD_EMAIL_FAILURE',
USER_RESET_PASSWORD_REQUEST: 'USER_RESET_PASSWORD_REQUEST',
USER_RESET_PASSWORD_SUCCESS: 'USER_RESET_PASSWORD_SUCCESS',
USER_RESET_PASSWORD_FAILURE: 'USER_RESET_PASSWORD_FAILURE'
};
+10
View File
@@ -0,0 +1,10 @@
export const authHeader = () => {
// return authorization header with jwt token
let auth = JSON.parse(localStorage.getItem('auth'));
if (auth) {
return { Authorization: 'Bearer ' + auth.token };
} else {
return { Authorization: 'unauthorize me' };
}
};
+50
View File
@@ -0,0 +1,50 @@
const toJsDate = date => {
// Split timestamp into [ Y, M, D, h, m, s ]
var dateParts = date.split('-');
var utcDate = new Date(
dateParts[0],
dateParts[1] - 1,
dateParts[2].substr(0, 2)
);
var newDate = new Date(
utcDate.getTime() + utcDate.getTimezoneOffset() * 60 * 1000
);
var offset = utcDate.getTimezoneOffset() / 60;
var hours = utcDate.getHours();
newDate.setHours(hours - offset);
return newDate;
};
const dateDiff = (dt1, dt2) => {
var diff = (dt2.getTime() - dt1.getTime()) / 1000;
diff /= 60 * 60 * 24 * 7 * 4;
return Math.abs(Math.round(diff));
};
const getMonthString = month => {
const monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
return monthNames[month];
};
export const dateFunctions = {
toJsDate,
dateDiff,
getMonthString
};
+3
View File
@@ -0,0 +1,3 @@
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
+5
View File
@@ -0,0 +1,5 @@
export * from './history.config';
export * from './store.config';
export * from './auth-header';
export * from './dates';
export * from './text.js';
+23
View File
@@ -0,0 +1,23 @@
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import allReducers from '../reducers';
import { composeWithDevTools } from 'redux-devtools-extension';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const persistConfig = {
key: 'root',
storage
};
const persistedReducer = persistReducer(persistConfig, allReducers);
export const storePersistorConfig = () => {
let store = createStore(
persistedReducer,
composeWithDevTools(applyMiddleware(thunk))
);
let persistor = persistStore(store);
return { store, persistor };
};
+6
View File
@@ -0,0 +1,6 @@
function numberWithCommas(x) {
if (x) return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return 0;
}
export const textFunctions = { numberWithCommas };
+42
View File
@@ -0,0 +1,42 @@
import React from 'react';
import ReactDOM from 'react-dom';
const envVars = require('config');
import Root from './components/Root';
import { storePersistorConfig, history } from './helpers';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { Router } from 'react-router-dom';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
const theme = createMuiTheme({
palette: {
primary: { main: '#009688' }, // classic teal
submit: { main: '#4caf50' },
secondary: { main: '#9E9E9E' }, // greyish text color
error: { main: '#F44336' } // red
},
typography: { useNextVariants: true }
});
var config = storePersistorConfig();
// TODO: adding back in redux-persist? <PersistGate loading={null} persistor={config.persistor}>
// after Provider, before StripeProvider
ReactDOM.render(
<MuiThemeProvider theme={theme}>
<Provider store={config.store}>
<PersistGate loading={null} persistor={config.persistor}>
<Router history={history}>
<Root />
</Router>
</PersistGate>
</Provider>
</MuiThemeProvider>,
document.getElementById('app')
);
module.hot.accept();
+16
View File
@@ -0,0 +1,16 @@
let have_token = JSON.parse(localStorage.getItem('auth')) ? true : false;
const INITIAL_STATE = {
have_token,
user: null,
loading: false,
error: null,
signed_up: null
};
export function authentication(state = INITIAL_STATE, action) {
switch (action.type) {
default:
return state;
}
}
+13
View File
@@ -0,0 +1,13 @@
import { combineReducers } from 'redux';
import { authentication } from './auth.reducer';
/*
* We combine all reducers into a single object before updated data is dispatched (sent) to store
* Your entire applications state (store) is just whatever gets returned from all your reducers
* */
const allReducers = combineReducers({
authentication
});
export default allReducers;
+51
View File
@@ -0,0 +1,51 @@
import axios from 'axios';
const config = require('config');
import { authHeader, history } from '../helpers';
import { authConstants } from '../constants';
// Main data service for api calls
export const dataService = (
dispatch,
method,
endpoint,
data_params = {},
data_or_params = true,
need_auth = false,
headers = {}
) => {
const requestOptions = {
method,
url: config.server_url + endpoint,
headers
};
data_or_params
? (requestOptions.data = data_params)
: (requestOptions.params = data_params);
need_auth ? (requestOptions.headers = authHeader()) : {};
return axios(requestOptions)
.then(handleResponse)
.catch(handleError(dispatch));
};
const handleResponse = response => {
return response.data;
};
const handleError = dispatch => error => {
// logout user if jwt token expired by clearing storage
if (error.response.status == 403) {
localStorage.removeItem('auth');
//setting have_token to false, and rest = INITIAL_STATE
dispatch({
type: authConstants.LOGOUT,
payload: {
message: 'Auth unsuccessful for call...logging out'
}
});
history.push('/login');
}
throw error.response;
};