Initial commit

This commit is contained in:
Aaron Lindsay 2016-12-22 21:22:47 -05:00
commit df4970c4c3
19 changed files with 1116 additions and 0 deletions

View File

@ -0,0 +1,34 @@
var ErrorConstants = require('../constants/ErrorConstants');
var models = require('../models.js');
var Error = models.Error;
function serverError(error) {
return {
type: ErrorConstants.ERROR_SERVER,
error: error
};
}
function ajaxError(error) {
var e = new Error();
e.ErrorId = 5;
e.ErrorString = "Request Failed: " + status + error;
return {
type: ErrorConstants.ERROR_AJAX,
error: e
};
}
function clearError() {
return {
type: ErrorConstants.CLEAR_ERROR,
};
}
module.exports = {
serverError: serverError,
ajaxError: ajaxError,
clearError: clearError
};

204
js/actions/UserActions.js Normal file
View File

@ -0,0 +1,204 @@
var UserConstants = require('../constants/UserConstants');
var ErrorActions = require('./ErrorActions');
var models = require('../models.js');
var User = models.User;
var Session = models.Session;
var Error = models.Error;
function loginUser() {
return {
type: UserConstants.LOGIN_USER
}
}
function userLoggedIn(session) {
return {
type: UserConstants.USER_LOGGEDIN,
session: session
}
}
function logoutUser() {
return {
type: UserConstants.LOGOUT_USER
}
}
function userLoggedOut() {
return {
type: UserConstants.USER_LOGGEDOUT
}
}
function fetchUser(userId) {
return {
type: UserConstants.FETCH_USER,
userId: userId
}
}
function userFetched(user) {
return {
type: UserConstants.USER_FETCHED,
user: user
}
}
function updateUser(user) {
return {
type: UserConstants.UPDATE_USER,
user: user
}
}
function userUpdated(user) {
return {
type: UserConstants.USER_UPDATED,
user: user
}
}
function fetch(userId) {
return function (dispatch) {
dispatch(fetchUser());
$.ajax({
type: "GET",
dataType: "json",
url: "user/"+userId+"/",
success: function(data, status, jqXHR) {
var e = new Error();
e.fromJSON(data);
if (e.isError()) {
ErrorActions.serverError(e);
} else {
var u = new User();
u.fromJSON(data);
dispatch(userFetched(u));
}
},
error: function(jqXHR, status, error) {
ErrorActions.ajaxError(e);
}
});
};
}
function initializeSession(dispatch, session) {
dispatch(userLoggedIn(session));
dispatch(fetch(session.UserId));
}
function login(user) {
return function (dispatch) {
dispatch(loginUser());
$.ajax({
type: "POST",
dataType: "json",
url: "session/",
data: {user: user.toJSON()},
success: function(data, status, jqXHR) {
var e = new Error();
e.fromJSON(data);
if (e.isError()) {
ErrorActions.serverError(e);
} else {
var s = new Session();
s.fromJSON(data);
initializeSession(dispatch, s);
}
},
error: function(jqXHR, status, error) {
ErrorActions.ajaxError(e);
}
});
};
}
function tryResumingSession() {
return function (dispatch) {
$.ajax({
type: "GET",
dataType: "json",
url: "session/",
success: function(data, status, jqXHR) {
var e = new Error();
e.fromJSON(data);
if (e.isError()) {
if (e.ErrorId != 1 /* Not Signed In*/)
ErrorActions.serverError(e);
} else {
var s = new Session();
s.fromJSON(data);
dispatch(loginUser());
initializeSession(dispatch, s);
}
},
error: function(jqXHR, status, error) {
ErrorActions.ajaxError(e);
}
});
};
}
function logout() {
return function (dispatch) {
dispatch(logoutUser());
$.ajax({
type: "DELETE",
dataType: "json",
url: "session/",
success: function(data, status, jqXHR) {
var e = new Error();
e.fromJSON(data);
if (e.isError()) {
ErrorActions.serverError(e);
} else {
dispatch(userLoggedOut());
}
},
error: function(jqXHR, status, error) {
ErrorActions.ajaxError(e);
}
});
};
}
function update(user) {
return function (dispatch) {
dispatch(updateUser());
$.ajax({
type: "PUT",
dataType: "json",
url: "user/"+user.UserId+"/",
data: {user: user.toJSON()},
success: function(data, status, jqXHR) {
var e = new Error();
e.fromJSON(data);
if (e.isError()) {
ErrorActions.serverError(e);
} else {
var u = new User();
u.fromJSON(data);
dispatch(userUpdated(u));
}
},
error: function(jqXHR, status, error) {
ErrorActions.ajaxError(e);
}
});
};
}
module.exports = {
fetch: fetch,
login: login,
logout: logout,
update: update,
tryResumingSession: tryResumingSession
};

View File

@ -0,0 +1,158 @@
var React = require('react');
var ReactDOM = require('react-dom');
var ReactBootstrap = require('react-bootstrap');
var Modal = ReactBootstrap.Modal;
var Button = ReactBootstrap.Button;
var ButtonGroup = ReactBootstrap.ButtonGroup;
var Form = ReactBootstrap.Form;
var FormGroup = ReactBootstrap.FormGroup;
var FormControl = ReactBootstrap.FormControl;
var ControlLabel = ReactBootstrap.ControlLabel;
var Col = ReactBootstrap.Col;
var User = require('../models').User;
module.exports = React.createClass({
displayName: "AccountSettingsModal",
_getInitialState: function(props) {
return {error: "",
name: props.user.Name,
username: props.user.Username,
email: props.user.Email,
password: models.BogusPassword,
confirm_password: models.BogusPassword,
passwordChanged: false,
initial_password: models.BogusPassword};
},
getInitialState: function() {
return this._getInitialState(this.props);
},
componentWillReceiveProps: function(nextProps) {
if (nextProps.show && !this.props.show) {
this.setState(this._getInitialState(nextProps));
}
},
passwordValidationState: function() {
if (this.state.passwordChanged) {
if (this.state.password.length >= 10)
return "success";
else if (this.state.password.length >= 6)
return "warning";
else
return "error";
}
},
confirmPasswordValidationState: function() {
if (this.state.confirm_password.length > 0) {
if (this.state.confirm_password == this.state.password)
return "success";
else
return "error";
}
},
handleCancel: function() {
if (this.props.onCancel != null)
this.props.onCancel();
},
handleChange: function() {
if (ReactDOM.findDOMNode(this.refs.password).value != this.state.initial_password)
this.setState({passwordChanged: true});
this.setState({
name: ReactDOM.findDOMNode(this.refs.name).value,
username: ReactDOM.findDOMNode(this.refs.username).value,
email: ReactDOM.findDOMNode(this.refs.email).value,
password: ReactDOM.findDOMNode(this.refs.password).value,
confirm_password: ReactDOM.findDOMNode(this.refs.confirm_password).value
});
},
handleSubmit: function(e) {
var u = new User();
e.preventDefault();
u.UserId = this.props.user.UserId;
u.Name = this.state.name;
u.Username = this.state.username;
u.Email = this.state.email;
if (this.state.passwordChanged) {
u.Password = this.state.password;
if (u.Password != this.state.confirm_password) {
this.setState({error: "Error: password do not match"});
return;
}
} else {
u.Password = models.BogusPassword;
}
this.props.onUpdateUser(u);
this.props.onSubmit();
},
render: function() {
return (
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="large">
<Modal.Header closeButton>
<Modal.Title>Edit Account Settings</Modal.Title>
</Modal.Header>
<Modal.Body>
<span color="red">{this.state.error}</span>
<Form horizontal onSubmit={this.handleSubmit}>
<FormGroup>
<Col componentClass={ControlLabel} xs={2}>Name</Col>
<Col xs={10}>
<FormControl type="text"
value={this.state.name}
onChange={this.handleChange}
ref="name"/>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} xs={2}>Username</Col>
<Col xs={10}>
<FormControl type="text"
value={this.state.username}
onChange={this.handleChange}
ref="username"/>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} xs={2}>Email</Col>
<Col xs={10}>
<FormControl type="email"
value={this.state.email}
onChange={this.handleChange}
ref="email"/>
</Col>
</FormGroup>
<FormGroup validationState={this.passwordValidationState()}>
<Col componentClass={ControlLabel} xs={2}>Password</Col>
<Col xs={10}>
<FormControl type="password"
value={this.state.password}
onChange={this.handleChange}
ref="password"/>
<FormControl.Feedback/>
</Col>
</FormGroup>
<FormGroup validationState={this.confirmPasswordValidationState()}>
<Col componentClass={ControlLabel} xs={2}>Confirm Password</Col>
<Col xs={10}>
<FormControl type="password"
value={this.state.confirm_password}
onChange={this.handleChange}
ref="confirm_password"/>
<FormControl.Feedback/>
</Col>
</FormGroup>
</Form>
</Modal.Body>
<Modal.Footer>
<ButtonGroup>
<Button onClick={this.handleCancel} bsStyle="warning">Cancel</Button>
<Button onClick={this.handleSubmit} bsStyle="success">Save Settings</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>
);
}
});

95
js/components/LunchApp.js Normal file
View File

@ -0,0 +1,95 @@
var React = require('react');
var ReactBootstrap = require('react-bootstrap');
var Jumbotron = ReactBootstrap.Jumbotron;
var Tabs = ReactBootstrap.Tabs;
var Tab = ReactBootstrap.Tab;
var Modal = ReactBootstrap.Modal;
var TopBarContainer = require('../containers/TopBarContainer');
var NewUserForm = require('./NewUserForm');
var AccountSettingsModalContainer = require('../containers/AccountSettingsModalContainer');
module.exports = React.createClass({
displayName: "LunchApp",
getInitialState: function() {
return {
hash: "home",
showAccountSettingsModal: false
};
},
componentDidMount: function() {
this.props.tryResumingSession();
this.handleHashChange();
if ("onhashchange" in window) {
window.onhashchange = this.handleHashChange;
}
},
handleHashChange: function() {
var hash = location.hash.replace(/^#/, '');
if (hash.length == 0)
hash = "home";
if (hash != this.state.hash)
this.setHash(hash);
},
setHash: function(hash) {
location.hash = hash;
if (this.state.hash != hash)
this.setState({hash: hash});
},
handleAccountSettings: function() {
this.setState({showAccountSettingsModal: true});
},
handleSettingsSubmitted: function(user) {
this.setState({
showAccountSettingsModal: false
});
},
handleSettingsCanceled: function() {
this.setState({showAccountSettingsModal: false});
},
handleCreateNewUser: function() {
this.setHash("new_user");
},
handleGoHome: function() {
this.setHash("home");
},
render: function() {
var mainContent;
if (this.state.hash == "new_user") {
mainContent = <NewUserForm onNewUser={this.handleGoHome} onCancel={this.handleGoHome}/>
} else {
if (this.props.user.isUser())
mainContent = (
<Tabs defaultActiveKey={1} id='mainNavigationTabs'>
<Tab title="Accounts" eventKey={1} >accounts
</Tab>
<Tab title="Securities" eventKey={2} >securities
</Tab>
<Tab title="Scheduled Transactions" eventKey={3} >Scheduled transactions go here...</Tab>
<Tab title="Budgets" eventKey={4} >Budgets go here...</Tab>
<Tab title="Reports" eventKey={5} >Reports go here...</Tab>
</Tabs>);
else
mainContent = (
<Jumbotron>
<center>
<h1>Lunch App</h1>
</center>
</Jumbotron>);
}
return (
<div className="fullheight ui">
<TopBarContainer
onCreateNewUser={this.handleCreateNewUser}
onAccountSettings={this.handleAccountSettings} />
{mainContent}
<AccountSettingsModalContainer
show={this.state.showAccountSettingsModal}
onSubmit={this.handleSettingsSubmitted}
onCancel={this.handleSettingsCanceled}/>
</div>
);
}
});

View File

@ -0,0 +1,165 @@
var React = require('react');
var ReactDOM = require('react-dom');
var ReactBootstrap = require('react-bootstrap');
var Panel = ReactBootstrap.Panel;
var Form = ReactBootstrap.Form;
var FormGroup = ReactBootstrap.FormGroup;
var FormControl = ReactBootstrap.FormControl;
var ControlLabel = ReactBootstrap.ControlLabel;
var Col = ReactBootstrap.Col;
var Button = ReactBootstrap.Button;
var ButtonGroup = ReactBootstrap.ButtonGroup;
var models = require('../models');
var User = models.User;
var Error = models.Error;
module.exports = React.createClass({
getInitialState: function() {
return {error: "",
name: "",
username: "",
email: "",
password: "",
confirm_password: "",
passwordChanged: false,
initial_password: ""};
},
passwordValidationState: function() {
if (this.state.passwordChanged) {
if (this.state.password.length >= 10)
return "success";
else if (this.state.password.length >= 6)
return "warning";
else
return "error";
}
},
confirmPasswordValidationState: function() {
if (this.state.confirm_password.length > 0) {
if (this.state.confirm_password == this.state.password)
return "success";
else
return "error";
}
},
handleCancel: function() {
if (this.props.onCancel != null)
this.props.onCancel();
},
handleChange: function() {
if (ReactDOM.findDOMNode(this.refs.password).value != this.state.initial_password)
this.setState({passwordChanged: true});
this.setState({
name: ReactDOM.findDOMNode(this.refs.name).value,
username: ReactDOM.findDOMNode(this.refs.username).value,
email: ReactDOM.findDOMNode(this.refs.email).value,
password: ReactDOM.findDOMNode(this.refs.password).value,
confirm_password: ReactDOM.findDOMNode(this.refs.confirm_password).value
});
},
handleSubmit: function(e) {
var u = new User();
var error = "";
e.preventDefault();
u.Name = this.state.name;
u.Username = this.state.username;
u.Email = this.state.email;
u.Password = this.state.password;
if (u.Password != this.state.confirm_password) {
this.setState({error: "Error: password do not match"});
return;
}
this.handleCreateNewUser(u);
},
handleCreateNewUser: function(user) {
$.ajax({
type: "POST",
dataType: "json",
url: "user/",
data: {user: user.toJSON()},
success: function(data, status, jqXHR) {
var e = new Error();
e.fromJSON(data);
if (e.isError()) {
this.setState({error: e});
} else {
this.props.onNewUser();
}
}.bind(this),
error: function(jqXHR, status, error) {
var e = new Error();
e.ErrorId = 5;
e.ErrorString = "Request Failed: " + status + error;
this.setState({error: e});
}.bind(this),
});
},
render: function() {
var title = <h3>Create New User</h3>;
return (
<Panel header={title} bsStyle="info">
<span color="red">{this.state.error}</span>
<Form horizontal onSubmit={this.handleSubmit}>
<FormGroup>
<Col componentClass={ControlLabel} xs={2}>Name</Col>
<Col xs={10}>
<FormControl type="text"
value={this.state.name}
onChange={this.handleChange}
ref="name"/>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} xs={2}>Username</Col>
<Col xs={10}>
<FormControl type="text"
value={this.state.username}
onChange={this.handleChange}
ref="username"/>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} xs={2}>Email</Col>
<Col xs={10}>
<FormControl type="email"
value={this.state.email}
onChange={this.handleChange}
ref="email"/>
</Col>
</FormGroup>
<FormGroup validationState={this.passwordValidationState()}>
<Col componentClass={ControlLabel} xs={2}>Password</Col>
<Col xs={10}>
<FormControl type="password"
value={this.state.password}
onChange={this.handleChange}
ref="password"/>
<FormControl.Feedback/>
</Col>
</FormGroup>
<FormGroup validationState={this.confirmPasswordValidationState()}>
<Col componentClass={ControlLabel} xs={2}>Confirm Password</Col>
<Col xs={10}>
<FormControl type="password"
value={this.state.confirm_password}
onChange={this.handleChange}
ref="confirm_password"/>
<FormControl.Feedback/>
</Col>
</FormGroup>
<ButtonGroup className="pull-right">
<Button onClick={this.handleCancel}
bsStyle="warning">Cancel</Button>
<Button type="submit"
bsStyle="success">Create New User</Button>
</ButtonGroup>
</Form>
</Panel>
);
}
});

123
js/components/TopBar.js Normal file
View File

@ -0,0 +1,123 @@
var React = require('react');
var ReactBootstrap = require('react-bootstrap');
var Alert = ReactBootstrap.Alert;
var FormGroup = ReactBootstrap.FormGroup;
var FormControl = ReactBootstrap.FormControl;
var Button = ReactBootstrap.Button;
var DropdownButton = ReactBootstrap.DropdownButton;
var MenuItem = ReactBootstrap.MenuItem;
var Row = ReactBootstrap.Row;
var Col = ReactBootstrap.Col;
var ReactDOM = require('react-dom');
var User = require('../models').User;
const LoginBar = React.createClass({
getInitialState: function() {
return {username: '', password: ''};
},
onUsernameChange: function(e) {
this.setState({username: e.target.value});
},
onPasswordChange: function(e) {
this.setState({password: e.target.value});
},
handleSubmit: function(e) {
var user = new User();
e.preventDefault();
user.Username = ReactDOM.findDOMNode(this.refs.username).value;
user.Password = ReactDOM.findDOMNode(this.refs.password).value;
this.props.onLogin(user);
},
handleNewUserSubmit: function(e) {
e.preventDefault();
this.props.onCreateNewUser();
},
render: function() {
return (
<form onSubmit={this.handleSubmit}>
<FormGroup>
<Row>
<Col xs={4}></Col>
<Col xs={2}>
<Button bsStyle="link"
onClick={this.handleNewUserSubmit}>Create New User</Button>
</Col>
<Col xs={2}>
<FormControl type="text"
placeholder="Username..."
ref="username"/>
</Col>
<Col xs={2}>
<FormControl type="password"
placeholder="Password..."
ref="password"/>
</Col>
<Col xs={2}>
<Button type="submit" bsStyle="primary" block>
Login</Button>
</Col>
</Row>
</FormGroup>
</form>
);
}
});
const LogoutBar = React.createClass({
handleOnSelect: function(key) {
if (key == 1) {
if (this.props.onAccountSettings != null)
this.props.onAccountSettings();
} else if (key == 2) {
this.props.onLogout();
}
},
render: function() {
var signedInString = "Signed in as "+this.props.user.Name;
return (
<FormGroup>
<Row>
<Col xs={2}><label className="control-label pull-left">Lunch App</label></Col>
<Col xs={6}></Col>
<Col xs={4}>
<div className="pull-right">
<DropdownButton id="logout-settings-dropdown" title={signedInString} onSelect={this.handleOnSelect} bsStyle="info">
<MenuItem eventKey={1}>Account Settings</MenuItem>
<MenuItem eventKey={2}>Logout</MenuItem>
</DropdownButton>
</div>
</Col>
</Row>
</FormGroup>
);
}
});
module.exports = React.createClass({
displayName: "TopBar",
render: function() {
var barContents;
var errorAlert;
if (!this.props.user.isUser())
barContents = <LoginBar onLogin={this.props.onLogin} onCreateNewUser={this.props.onCreateNewUser} />;
else
barContents = <LogoutBar user={this.props.user} onLogout={this.props.onLogout} onAccountSettings={this.props.onAccountSettings}/>;
if (this.props.error.isError())
errorAlert =
<Alert bsStyle="danger" onDismiss={this.props.onClearError}>
<h4>Error!</h4>
<p>Error {this.props.error.ErrorId}: {this.props.error.ErrorString}</p>
<Button onClick={this.props.onClearError}>Clear</Button>
</Alert>;
return (
<div>
{barContents}
{errorAlert}
</div>
);
}
});

View File

@ -0,0 +1,9 @@
var keyMirror = require('keymirror');
module.exports = keyMirror({
ERROR_AJAX: null,
ERROR_SERVER: null,
ERROR_CLIENT: null,
ERROR_USER: null,
CLEAR_ERROR: null
});

View File

@ -0,0 +1,12 @@
var keyMirror = require('keymirror');
module.exports = keyMirror({
LOGIN_USER: null,
USER_LOGGEDIN: null,
LOGOUT_USER: null,
USER_LOGGEDOUT: null,
FETCH_USER: null,
USER_FETCHED: null,
UPDATE_USER: null,
USER_UPDATED: null
});

View File

@ -0,0 +1,21 @@
var connect = require('react-redux').connect;
var UserActions = require('../actions/UserActions');
var AccountSettingsModal = require('../components/AccountSettingsModal');
function mapStateToProps(state) {
return {
user: state.user
}
}
function mapDispatchToProps(dispatch) {
return {
onUpdateUser: function(user) {dispatch(UserActions.update(user))}
}
}
module.exports = connect(
mapStateToProps,
mapDispatchToProps
)(AccountSettingsModal)

View File

@ -0,0 +1,22 @@
var connect = require('react-redux').connect;
var UserActions = require('../actions/UserActions');
var LunchApp = require('../components/LunchApp');
function mapStateToProps(state) {
return {
user: state.user
}
}
function mapDispatchToProps(dispatch) {
return {
tryResumingSession: function() {dispatch(UserActions.tryResumingSession())},
}
}
module.exports = connect(
mapStateToProps,
mapDispatchToProps
)(LunchApp)

View File

@ -0,0 +1,27 @@
var connect = require('react-redux').connect;
var UserActions = require('../actions/UserActions');
var ErrorActions = require('../actions/ErrorActions');
var TopBar = require('../components/TopBar');
function mapStateToProps(state) {
return {
user: state.user,
error: state.error
}
}
function mapDispatchToProps(dispatch) {
return {
onLogin: function(user) {dispatch(UserActions.login(user))},
onLogout: function() {dispatch(UserActions.logout())},
onUpdateUser: function(user) {dispatch(UserActions.update(user))},
onClearError: function() {dispatch(ErrorActions.clearError())}
}
}
module.exports = connect(
mapStateToProps,
mapDispatchToProps
)(TopBar)

40
js/main.js Normal file
View File

@ -0,0 +1,40 @@
var React = require('react');
var ReactDOM = require('react-dom');
var Provider = require('react-redux').Provider;
var Redux = require('redux');
var ReduxThunk = require('redux-thunk').default;
var Globalize = require('globalize');
var globalizeLocalizer = require('react-widgets/lib/localizers/globalize');
var LunchAppContainer = require('./containers/LunchAppContainer');
var LunchReducer = require('./reducers/LunchReducer');
// Setup globalization for react-widgets
//Globalize.load(require("cldr-data").entireSupplemental());
Globalize.load(
require("cldr-data/main/en/ca-gregorian"),
require("cldr-data/main/en/numbers"),
require("cldr-data/supplemental/likelySubtags"),
require("cldr-data/supplemental/timeData"),
require("cldr-data/supplemental/weekData")
);
Globalize.locale('en');
globalizeLocalizer(Globalize);
$(document).ready(function() {
var store = Redux.createStore(
LunchReducer,
Redux.applyMiddleware(
ReduxThunk
)
);
ReactDOM.render(
<Provider store={store}>
<LunchAppContainer />
</Provider>,
document.getElementById("content")
);
});

115
js/models.js Normal file
View File

@ -0,0 +1,115 @@
var Big = require('big.js');
function getJSONObj(json_input) {
if (typeof json_input == "string")
return $.parseJSON(json_input)
else if (typeof json_input == "object")
return json_input;
console.error("Unable to parse json:", json_input);
return null
}
function User() {
this.UserId = -1;
this.Name = "";
this.Username = "";
this.Password = "";
this.Email = "";
}
User.prototype.toJSON = function() {
var json_obj = {};
json_obj.UserId = this.UserId;
json_obj.Name = this.Name;
json_obj.Username = this.Username;
json_obj.Password = this.Password;
json_obj.Email = this.Email;
return JSON.stringify(json_obj);
}
User.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("UserId"))
this.UserId = json_obj.UserId;
if (json_obj.hasOwnProperty("Name"))
this.Name = json_obj.Name;
if (json_obj.hasOwnProperty("Username"))
this.Username = json_obj.Username;
if (json_obj.hasOwnProperty("Password"))
this.Password = json_obj.Password;
if (json_obj.hasOwnProperty("Email"))
this.Email = json_obj.Email;
}
User.prototype.isUser = function() {
var empty_user = new User();
return this.UserId != empty_user.UserId ||
this.Username != empty_user.Username;
}
function Session() {
this.SessionId = -1;
this.UserId = -1;
}
Session.prototype.toJSON = function() {
var json_obj = {};
json_obj.SessionId = this.SessionId;
json_obj.UserId = this.UserId;
return JSON.stringify(json_obj);
}
Session.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("SessionId"))
this.SessionId = json_obj.SessionId;
if (json_obj.hasOwnProperty("UserId"))
this.UserId = json_obj.UserId;
}
Session.prototype.isSession = function() {
var empty_session = new Session();
return this.SessionId != empty_session.SessionId ||
this.UserId != empty_session.UserId;
}
function Error() {
this.ErrorId = -1;
this.ErrorString = "";
}
Error.prototype.toJSON = function() {
var json_obj = {};
json_obj.ErrorId = this.ErrorId;
json_obj.ErrorString = this.ErrorString;
return JSON.stringify(json_obj);
}
Error.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("ErrorId"))
this.ErrorId = json_obj.ErrorId;
if (json_obj.hasOwnProperty("ErrorString"))
this.ErrorString = json_obj.ErrorString;
}
Error.prototype.isError = function() {
var empty_error = new Error();
return this.ErrorId != empty_error.ErrorId ||
this.ErrorString != empty_error.ErrorString;
}
module.exports = models = {
// Classes
User: User,
Session: Session,
Error: Error,
// Constants
BogusPassword: "password"
};

View File

@ -0,0 +1,17 @@
var ErrorConstants = require('../constants/ErrorConstants');
var Error = require('../models').Error;
module.exports = function(state = new Error(), action) {
switch (action.type) {
case ErrorConstants.ERROR_AJAX:
case ErrorConstants.ERROR_SERVER:
case ErrorConstants.ERROR_CLIENT:
case ErrorConstants.ERROR_USER:
return action.error;
case ErrorConstants.CLEAR_ERROR:
return new Error();
default:
return state;
}
};

View File

@ -0,0 +1,11 @@
var Redux = require('redux');
var UserReducer = require('./UserReducer');
var SessionReducer = require('./SessionReducer');
var ErrorReducer = require('./ErrorReducer');
module.exports = Redux.combineReducers({
user: UserReducer,
session: SessionReducer,
error: ErrorReducer
});

View File

@ -0,0 +1,14 @@
var UserConstants = require('../constants/UserConstants');
var Session = require('../models').Session;
module.exports = function(state = new Session(), action) {
switch (action.type) {
case UserConstants.USER_LOGGEDIN:
return action.session;
case UserConstants.USER_LOGGEDOUT:
return new Session();
default:
return state;
}
};

View File

@ -0,0 +1,15 @@
var UserConstants = require('../constants/UserConstants');
var User = require('../models').User;
module.exports = function(state = new User(), action) {
switch (action.type) {
case UserConstants.USER_FETCHED:
case UserConstants.USER_UPDATED:
return action.user;
case UserConstants.USER_LOGGEDOUT:
return new User();
default:
return state;
}
};

18
static/index.html Normal file
View File

@ -0,0 +1,18 @@
<html>
<head>
<title>Lunch</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="static/react-widgets/css/react-widgets.css">
<link rel="stylesheet" href="static/stylesheet.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script type="text/javascript" src="static/bundle.js"></script>
</head>
<body>
<div id="content"></div>
</body>
</html>

16
static/stylesheet.css Normal file
View File

@ -0,0 +1,16 @@
html, body {
height: 100%;
}
div#content {
display: block;
width: 95%;
height: 100%;
min-width: 75em;
max-width: 100em;
margin: auto;
}
/* Keep the main windows sized to the full viewable height */
.fullheight {
height: 100%;
}