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
+4
View File
@@ -0,0 +1,4 @@
{
"presets": ["es2015", "react"],
"plugins": ["babel-plugin-transform-class-properties", "transform-object-rest-spread"]
}
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+25
View File
@@ -0,0 +1,25 @@
.DS_STORE
node_modules
scripts/flow/*/.flowconfig
*~
*.pyc
.grunt
_SpecRunner.html
__benchmarks__
build/
remote-repo/
coverage/
.module-cache
fixtures/dom/public/react-dom.js
fixtures/dom/public/react.js
test/the-files-to-test.generated.js
*.log*
chrome-user-data
*.sublime-project
*.sublime-workspace
.idea
*.iml
.vscode
*.swp
*.swo
dist/bundle.js
+1
View File
@@ -0,0 +1 @@
# deb_client
+17
View File
@@ -0,0 +1,17 @@
{
"server_url": "http://localhost:5000",
"STRIPE_KEY": "pk_test_xPZzi9Y8uYGaHIBprkNe4jDr",
"STRIPE_CONNECT_ID": "ca_EeCxc3WY624dMuO6B5JGlizTZfYiqdrp",
"DEFAULT_LOGO": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultLogo.png",
"DEFAULT_CHARITY_PROFILE_PIC": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultCharityProfilePic.png",
"DEFAULT_CHARITY_COVER_PIC": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultCharityCoverPic.jpg",
"DEFAULT_DONOR_PROFILE_PIC": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultDonorProfilePic.png",
"TERMS_PDF": "https://s3-us-west-1.amazonaws.com/charify-assets/terms.pdf",
"STRIPE_LOGO": "https://s3-us-west-1.amazonaws.com/charify-assets/powered_by_stripe.png"
}
+17
View File
@@ -0,0 +1,17 @@
{
"server_url": "https://ucharify-api.herokuapp.com",
"STRIPE_KEY": "pk_live_DsBKJbrrd6vFxI4HzwL5ogAR",
"STRIPE_CONNECT_ID": "ca_EeCx0ztPuZRyagXlZgdf4ew2E0Gn8aGU",
"DEFAULT_LOGO": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultLogo.png",
"DEFAULT_CHARITY_PROFILE_PIC": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultCharityProfilePic.png",
"DEFAULT_CHARITY_COVER_PIC": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultCharityCoverPic.jpg",
"DEFAULT_DONOR_PROFILE_PIC": "https://s3-us-west-1.amazonaws.com/charify-assets/defaultDonorProfilePic.png",
"TERMS_PDF": "https://s3-us-west-1.amazonaws.com/charify-assets/terms.pdf",
"STRIPE_LOGO": "https://s3-us-west-1.amazonaws.com/charify-assets/powered_by_stripe.png"
}
+26
View File
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" />
<link rel="stylesheet" type="text/css" href="/styles.css" />
<link rel="icon" href="https://s3-us-west-1.amazonaws.com/charify-assets/defaultLogo.png" type="image/png" />
<script src="https://js.stripe.com/v3/"></script>
<title>UCharify</title>
</head>
<body>
<div id="app"></div>
<!-- Need to change
dev: /bundle.js
prod: ./bundle.js XXXX discovered that need the dev /bundle.js for history api fallback in prod. Also, it works in AWS, but not opening up the html file local
-->
<script src="/bundle.js"></script>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
body {
margin: 0;
padding: 0;
}
#app {
display: flex;
justify-content: center;
min-height: 100vh;
}
+12
View File
@@ -0,0 +1,12 @@
-> Root
-> Home
-> LoginForm
-> SignupForm
-> Main
-> Sidebar (always showing)
-> Dashboard
-> My Grants
-> Charities
-> My History (past payments list)
-> Settings
-> (bottom of sidebar) Profile
+7945
View File
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
{
"name": "ucharify",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start:mac": "NODE_ENV=development webpack-dev-server --config ./webpack.config.js --mode development --color --progress",
"start:windows": "set NODE_ENV=development& webpack-dev-server --config ./webpack.config.js --mode development --color --progress",
"build": "set NODE_ENV=production& webpack --mode production --color --progress"
},
"repository": {
"type": "git",
"url": "git+https://github.com/talksik/Ucharify.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/talksik/Ucharify/issues"
},
"homepage": "https://github.com/talksik/Ucharify#readme",
"dependencies": {
"@material-ui/core": "^3.8.1",
"@material-ui/icons": "^3.0.1",
"axios": "^0.19.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-router-dom": "^4.3.1",
"redux-persist": "^5.10.0"
},
"devDependencies": {
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"css-loader": "^2.1.0",
"file-loader": "^3.0.1",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"react-hot-loader": "^4.6.3",
"react-redux": "^6.0.0",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.7",
"redux-thunk": "^2.3.0",
"style-loader": "^0.23.1",
"webpack": "^4.28.3",
"webpack-cli": "^3.2.0",
"webpack-dev-server": "^3.1.14"
}
}
+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;
};
+74
View File
@@ -0,0 +1,74 @@
const webpack = require('webpack');
var path = require('path');
module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
query: {
presets: ['react']
}
}
},
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[local]___[hash:base64:5]'
}
},
{
loader: 'less-loader'
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
]
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [new webpack.HotModuleReplacementPlugin()],
devServer: {
contentBase: './dist',
hot: true,
port: 8081,
historyApiFallback: true
},
externals: {
config: JSON.stringify(
process.env.NODE_ENV === 'development'
? require('./config.dev.json')
: require('./config.prod.json')
)
}
};