wip Pup 1.0.0

This commit is contained in:
cleverbeagle 2017-05-23 20:05:46 -05:00
commit cdc15f019d
48 changed files with 1100 additions and 0 deletions

3
.babelrc Normal file
View file

@ -0,0 +1,3 @@
{
"presets": ["es2015", "react"]
}

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules
settings-staging.json
settings-production.json

View file

@ -0,0 +1,16 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages

1
.meteor/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
local

7
.meteor/.id Normal file
View file

@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
16be20efyo0qb53r01o

32
.meteor/packages Normal file
View file

@ -0,0 +1,32 @@
# Meteor packages used by this project, one per line.
# Check this file (and the other files in this directory) into your repository.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-base@1.0.4 # Packages every Meteor app needs to have
mobile-experience@1.0.4 # Packages for a great mobile UX
mongo@1.1.17 # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
reactive-var@1.0.11 # Reactive variable for tracker
tracker@1.1.3 # Meteor's client-side reactive programming library
standard-minifier-css@1.3.4 # CSS minifier run for production mode
standard-minifier-js@2.0.0 # JS minifier run for production mode
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.7.3 # Enable ECMAScript2015+ syntax in app code
shell-server@0.2.3 # Server-side component of the `meteor shell` command
react-meteor-data
alanning:roles
fourseven:scss
twbs:bootstrap
accounts-base
accounts-password
service-configuration
accounts-facebook
accounts-github
accounts-google
themeteorchef:bert
fortawesome:fontawesome
aldeed:collection2-core

2
.meteor/platforms Normal file
View file

@ -0,0 +1,2 @@
server
browser

1
.meteor/release Normal file
View file

@ -0,0 +1 @@
METEOR@1.4.4.2

102
.meteor/versions Normal file
View file

@ -0,0 +1,102 @@
accounts-base@1.2.17
accounts-facebook@1.1.1
accounts-github@1.2.1
accounts-google@1.1.2
accounts-oauth@1.1.15
accounts-password@1.3.6
alanning:roles@1.2.16
aldeed:collection2-core@1.2.0
aldeed:simple-schema@1.5.3
allow-deny@1.0.5
autoupdate@1.3.12
babel-compiler@6.18.2
babel-runtime@1.0.1
base64@1.0.10
binary-heap@1.0.10
blaze@2.3.2
blaze-html-templates@1.1.2
blaze-tools@1.0.10
boilerplate-generator@1.0.11
caching-compiler@1.1.9
caching-html-compiler@1.1.2
callback-hook@1.0.10
check@1.2.5
ddp@1.2.5
ddp-client@1.3.4
ddp-common@1.2.8
ddp-rate-limiter@1.0.7
ddp-server@1.3.14
deps@1.0.12
diff-sequence@1.0.7
ecmascript@0.7.3
ecmascript-runtime@0.3.15
ejson@1.0.13
email@1.2.1
es5-shim@4.6.15
facebook-oauth@1.3.0
fastclick@1.0.13
fortawesome:fontawesome@4.4.0_1
fourseven:scss@4.5.0
geojson-utils@1.0.10
github-oauth@1.2.0
google-oauth@1.2.4
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.2.12
id-map@1.0.9
jquery@1.11.10
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.0.12
logging@1.1.17
mdg:validation-error@0.2.0
meteor@1.6.1
meteor-base@1.0.4
minifier-css@1.2.16
minifier-js@2.0.0
minimongo@1.0.23
mobile-experience@1.0.4
mobile-status-bar@1.0.14
modules@0.8.2
modules-runtime@0.7.10
mongo@1.1.17
mongo-id@1.0.6
npm-bcrypt@0.9.2
npm-mongo@2.2.24
oauth@1.1.13
oauth2@1.1.11
observe-sequence@1.0.16
ordered-dict@1.0.9
promise@0.8.8
raix:eventemitter@0.1.3
random@1.0.10
rate-limit@1.0.8
react-meteor-data@0.2.11
reactive-dict@1.1.8
reactive-var@1.0.11
reload@1.1.11
retry@1.0.9
routepolicy@1.0.12
service-configuration@1.0.11
session@1.1.7
sha@1.0.9
shell-server@0.2.3
spacebars@1.0.15
spacebars-compiler@1.1.2
srp@1.0.10
standard-minifier-css@1.3.4
standard-minifier-js@2.0.0
templating@1.3.2
templating-compiler@1.3.2
templating-runtime@1.3.2
templating-tools@1.1.2
themeteorchef:bert@2.1.2
tmeasday:check-npm-versions@0.2.0
tracker@1.1.3
twbs:bootstrap@3.3.6
ui@1.0.13
underscore@1.0.10
url@1.1.0
webapp@1.3.15
webapp-hashing@1.0.9

13
client/main.html Normal file
View file

@ -0,0 +1,13 @@
<head>
<meta charset="utf-8">
<title>Pup</title>
<meta name="description" content="A description for the application.">
<meta name="viewport" content="initial-scale=1, minimal-ui, maximum-scale=1, minimum-scale=1" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link rel="shortcut icon" type="image/png" href="/favicon.png?v1" sizes="16x16 32x32 64x64">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-precomposed.png">
</head>
<body>
<div id="react-root"></div>
</body>

1
client/main.js Normal file
View file

@ -0,0 +1 @@
import '../imports/startup/client';

View file

@ -0,0 +1,51 @@
/* eslint-disable consistent-return */
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
const Documents = new Mongo.Collection('Documents');
Documents.allow({
insert: () => false,
update: () => false,
remove: () => false,
});
Documents.deny({
insert: () => true,
update: () => true,
remove: () => true,
});
Documents.schema = new SimpleSchema({
owner: {
type: String,
label: 'The ID of the user this document belongs to.',
},
createdAt: {
type: String,
label: 'The date this document was created.',
autoValue() {
if (this.isInsert) return (new Date()).toISOString();
},
},
updatedAt: {
type: String,
label: 'The date this document was last updated.',
autoValue() {
if (this.isInsert || this.isUpdate) return (new Date()).toISOString();
},
},
title: {
type: String,
label: 'The title of the document.',
},
body: {
type: String,
label: 'The body of the document.',
},
});
Documents.attachSchema(Documents.schema);
export default Documents;

View file

@ -0,0 +1,4 @@
import $ from 'jquery';
import 'jquery-validation';
export default (form, options) => $(form).validate(options);

View file

@ -0,0 +1,8 @@
import React from 'react';
import { render } from 'react-dom';
import { Meteor } from 'meteor/meteor';
import App from '../../ui/layouts/App/App';
import '../../ui/stylesheets/app.scss';
Meteor.startup(() => render(<App />, document.getElementById('react-root')));

View file

View file

@ -0,0 +1,22 @@
import React, { PropTypes } from 'react';
import { Route, Redirect } from 'react-router-dom';
const Authenticated = ({ loggingIn, authenticated, component, ...rest }) => (
<Route
{...rest}
render={(props) => {
if (loggingIn) return <div />;
return authenticated ?
(React.createElement(component, { ...props, loggingIn, authenticated })) :
(<Redirect to="/login" />);
}}
/>
);
Authenticated.propTypes = {
loggingIn: PropTypes.bool.isRequired,
authenticated: PropTypes.bool.isRequired,
component: PropTypes.func.isRequired,
};
export default Authenticated;

View file

@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import { LinkContainer } from 'react-router-bootstrap';
import { Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
const AuthenticatedNavigation = ({ name }) => (
<div>
<Nav>
<LinkContainer to="/documents">
<NavItem eventKey={1} href="/documents">Documents</NavItem>
</LinkContainer>
</Nav>
<Nav pullRight>
<NavDropdown eventKey={2} title={name} id="user-nav-dropdown">
<LinkContainer to="/profile">
<NavItem eventKey={2.1} href="/profile">Profile</NavItem>
</LinkContainer>
<MenuItem divider />
<MenuItem eventKey={2.2} onClick={() => Meteor.logout()}>Logout</MenuItem>
</NavDropdown>
</Nav>
</div>
);
AuthenticatedNavigation.propTypes = {
name: PropTypes.string.isRequired,
};
export default AuthenticatedNavigation;

View file

@ -0,0 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import './InputHint.scss';
const InputHint = ({ children }) => (
<div className="InputHint">
{children}
</div>
);
InputHint.propTypes = {
children: PropTypes.node.isRequired,
};
export default InputHint;

View file

@ -0,0 +1,9 @@
@import '../../stylesheets/colors';
.InputHint {
display: block;
margin-top: 8px;
font-style: italic;
color: $gray-light;
font-size: 13px;
}

View file

@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Navbar } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import PublicNavigation from '../PublicNavigation/PublicNavigation';
import AuthenticatedNavigation from '../AuthenticatedNavigation/AuthenticatedNavigation';
import './Navigation.scss';
const Navigation = ({ authenticated, name }) => (
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<Link to="/">Pup</Link>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
{!authenticated ? <PublicNavigation /> : <AuthenticatedNavigation name={name} />}
</Navbar.Collapse>
</Navbar>
);
Navigation.defaultProps = {
name: PropTypes.string,
};
Navigation.propTypes = {
authenticated: PropTypes.bool.isRequired,
name: PropTypes.string,
};
export default Navigation;

View file

@ -0,0 +1,6 @@
.navbar {
border-radius: 0;
border-left: none;
border-right: none;
border-top: none;
}

View file

@ -0,0 +1,63 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Meteor } from 'meteor/meteor';
import { Bert } from 'meteor/themeteorchef:bert';
import './OAuthLoginButton.scss';
const handleLogin = (service, options, callback) => {
const defaultOptions = {
facebook: {
requestPermissions: ['email'],
loginStyle: 'redirect',
},
google: {
requestPermissions: ['email'],
requestOfflineToken: true,
loginStyle: 'redirect',
},
github: {
requestPermissions: ['user:email'],
loginStyle: 'redirect',
},
}[service];
const defaultCallback = (error) => {
if (error) Bert.alert(error.reason, 'danger');
};
return {
facebook: Meteor.loginWithFacebook,
google: Meteor.loginWithGoogle,
github: Meteor.loginWithGithub,
}[service](options || defaultOptions, callback || defaultCallback);
};
const serviceLabel = {
facebook: <span><i className="fa fa-facebook" /> Log In with Facebook</span>,
google: <span><i className="fa fa-google" /> Log In with Google</span>,
github: <span><i className="fa fa-github" /> Log In with GitHub</span>,
};
const OAuthLoginButton = ({ service, options, callback }) => (
<button
className={`OAuthLoginButton OAuthLoginButton-${service}`}
type="button"
onClick={() => handleLogin(service, options, callback)}
>
{serviceLabel[service]}
</button>
);
OAuthLoginButton.defaultProps = {
options: PropTypes.object,
callback: PropTypes.func,
};
OAuthLoginButton.propTypes = {
service: PropTypes.string.isRequired,
options: PropTypes.object, // eslint-disable-line react/forbid-prop-types
callback: PropTypes.func,
};
export default OAuthLoginButton;

View file

@ -0,0 +1,52 @@
@import '../../stylesheets/colors';
.OAuthLoginButton {
display: block;
width: 100%;
padding: 10px 15px;
border: none;
background: $gray-lighter;
border-radius: 3px;
i {
margin-right: 3px;
font-size: 18px;
position: relative;
top: 1px;
}
&.OAuthLoginButton-facebook {
background: $facebook;
color: #fff;
&:hover { background: darken($facebook, 5%); }
}
&.OAuthLoginButton-google {
background: $google;
color: #fff;
&:hover { background: darken($google, 5%); }
}
&.OAuthLoginButton-github {
background: $github;
color: #fff;
&:hover { background: darken($github, 5%); }
}
&:active {
position: relative;
top: 1px;
}
&:active,
&:focus {
outline: 0;
}
}
.OAuthLoginButton + .OAuthLoginButton {
margin-top: 10px;
}

View file

@ -0,0 +1,22 @@
import React, { PropTypes } from 'react';
import { Route, Redirect } from 'react-router-dom';
const Public = ({ loggingIn, authenticated, component, ...rest }) => (
<Route
{...rest}
render={(props) => {
if (loggingIn) return <div />;
return !authenticated ?
(React.createElement(component, { ...props, loggingIn, authenticated })) :
(<Redirect to="/documents" />);
}}
/>
);
Public.propTypes = {
loggingIn: PropTypes.bool.isRequired,
authenticated: PropTypes.bool.isRequired,
component: PropTypes.func.isRequired,
};
export default Public;

View file

@ -0,0 +1,16 @@
import React from 'react';
import { LinkContainer } from 'react-router-bootstrap';
import { Nav, NavItem } from 'react-bootstrap';
const PublicNavigation = () => (
<Nav pullRight>
<LinkContainer to="/signup">
<NavItem eventKey={1} href="/signup">Sign Up</NavItem>
</LinkContainer>
<LinkContainer to="/login">
<NavItem eventKey={2} href="/login">Log In</NavItem>
</LinkContainer>
</Nav>
);
export default PublicNavigation;

View file

@ -0,0 +1,61 @@
import React from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { Grid } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import { Roles } from 'meteor/alanning:roles';
import Navigation from '../../components/Navigation/Navigation';
import Authenticated from '../../components/Authenticated/Authenticated';
import Public from '../../components/Public/Public';
import Index from '../../pages/Index/Index';
import Documents from '../../pages/Documents/Documents';
import NewDocument from '../../pages/NewDocument/NewDocument';
import ViewDocument from '../../pages/ViewDocument/ViewDocument';
import EditDocument from '../../pages/EditDocument/EditDocument';
import Signup from '../../pages/Signup/Signup';
import Login from '../../pages/Login/Login';
import RecoverPassword from '../../pages/RecoverPassword/RecoverPassword';
import ResetPassword from '../../pages/ResetPassword/ResetPassword';
import NotFound from '../../pages/NotFound/NotFound';
const App = props => (
<Router>
{!props.loading ? <div className="App">
<Navigation {...props} />
<Grid>
<Switch>
<Route exact name="index" path="/" component={Index} />
<Authenticated exact path="/documents" component={Documents} {...props} />
<Authenticated exact path="/documents/new" component={NewDocument} {...props} />
<Authenticated exact path="/documents/:_id" component={ViewDocument} {...props} />
<Authenticated exact path="/documents/:_id/edit" component={EditDocument} {...props} />
<Public path="/signup" component={Signup} {...props} />
<Public path="/login" component={Login} {...props} />
<Route name="recover-password" path="/recover-password" component={RecoverPassword} />
<Route name="reset-password" path="/reset-password/:token" component={ResetPassword} />
<Route component={NotFound} />
</Switch>
</Grid>
</div> : ''}
</Router>
);
App.propTypes = {
loading: PropTypes.bool.isRequired,
};
export default createContainer(() => {
const loggingIn = Meteor.loggingIn();
const user = Meteor.user();
const userId = Meteor.userId();
const loading = !Roles.subscription.ready();
return {
loading,
loggingIn,
authenticated: !loggingIn && !!userId,
name: user && user.profile ? `${user.profile.name.first} ${user.profile.name.last}` : '',
roles: !loading && Roles.getRolesForUser(userId),
};
}, App);

View file

View file

View file

@ -0,0 +1,18 @@
import React from 'react';
import { Jumbotron } from 'react-bootstrap';
import './Index.scss';
const Index = () => (
<div className="Index">
<Jumbotron>
<img
src="https://s3-us-west-2.amazonaws.com/cleverbeagle-assets/graphics/email-icon.png"
alt="Clever Beagle"
/>
<p>Need help building your app? Check out our mentorship service.</p>
</Jumbotron>
</div>
);
export default Index;

View file

View file

@ -0,0 +1,106 @@
import React from 'react';
import { Row, Col, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Meteor } from 'meteor/meteor';
import { Bert } from 'meteor/themeteorchef:bert';
import OAuthLoginButton from '../../components/OAuthLoginButton/OAuthLoginButton';
import validate from '../../../modules/validate';
import './Login.scss';
class Login extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
const component = this;
validate(component.form, {
rules: {
emailAddress: {
required: true,
email: true,
},
password: {
required: true,
},
},
messages: {
emailAddress: {
required: 'Need an email address here.',
email: 'Is this email address correct?',
},
password: {
required: 'Need a password here.',
},
},
submitHandler() { component.handleSubmit(); },
});
}
handleSubmit() {
const { history } = this.props;
Meteor.loginWithPassword(this.emailAddress.value, this.password.value, (error) => {
if (error) {
Bert.alert(error.reason, 'danger');
} else {
Bert.alert('Welcome back!', 'success');
history.push('/documents');
}
});
}
render() {
return (<div className="Login">
<Row>
<Col xs={12} sm={6} md={5} lg={4}>
<h4 className="page-header">Log In</h4>
<Row>
<Col xs={12}>
<FormGroup className="OAuthLoginButtons">
<OAuthLoginButton service="facebook" />
<OAuthLoginButton service="google" />
<OAuthLoginButton service="github" />
</FormGroup>
</Col>
</Row>
<form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}>
<p className="LoginWithEmail">Log In with an Email Address</p>
<FormGroup>
<ControlLabel>Email Address</ControlLabel>
<input
type="email"
name="emailAddress"
ref={emailAddress => (this.emailAddress = emailAddress)}
className="form-control"
/>
</FormGroup>
<FormGroup>
<ControlLabel>Password</ControlLabel>
<input
type="password"
name="password"
ref={password => (this.password = password)}
className="form-control"
/>
</FormGroup>
<Button type="submit" bsStyle="success">Log In</Button>
<p className="DontHaveAnAccount">
{'Don\'t have an account?'} <Link to="/signup">Sign Up</Link>.
</p>
</form>
</Col>
</Row>
</div>);
}
}
Login.propTypes = {
history: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
export default Login;

View file

@ -0,0 +1,47 @@
@import '../../stylesheets/mixins';
@import '../../stylesheets/colors';
.Login {
.page-header {
margin-top: 0;
}
form {
position: relative;
border-top: 1px solid $gray-lighter;
margin-top: 15px;
padding-top: 25px;
.LoginWithEmail {
display: inline-block;
background: #fff;
padding: 0 10px;
position: absolute;
top: -12px;
left: 50%;
margin-left: -106px;
}
.DontHaveAnAccount {
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid $gray-lighter;
}
}
}
@include breakpoint(tablet) {
.Login {
.page-header {
margin-top: 10px;
}
}
}
@include breakpoint(desktop) {
.Login {
.page-header {
margin-top: 20px;
}
}
}

View file

View file

View file

@ -0,0 +1,155 @@
import React from 'react';
import { Row, Col, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert';
import OAuthLoginButton from '../../components/OAuthLoginButton/OAuthLoginButton';
import InputHint from '../../components/InputHint/InputHint';
import validate from '../../../modules/validate';
import './Signup.scss';
class Signup extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
const component = this;
validate(component.form, {
rules: {
firstName: {
required: true,
},
lastName: {
required: true,
},
emailAddress: {
required: true,
email: true,
},
password: {
required: true,
minlength: 6,
},
},
messages: {
firstName: {
required: 'What\'s your first name?',
},
lastName: {
required: 'What\'s your last name?',
},
emailAddress: {
required: 'Need an email address here.',
email: 'Is this email address correct?',
},
password: {
required: 'Need a password here.',
minlength: 'Please use at least six characters.',
},
},
submitHandler() { component.handleSubmit(); },
});
}
handleSubmit() {
const { history } = this.props;
Accounts.createUser({
email: this.emailAddress.value,
password: this.password.value,
profile: {
name: {
first: this.firstName.value,
last: this.lastName.value,
},
},
}, (error) => {
if (error) {
Bert.alert(error.reason, 'danger');
} else {
Bert.alert('Welcome!', 'success');
history.push('/documents');
}
});
}
render() {
return (<div className="Signup">
<Row>
<Col xs={12} sm={6} md={5} lg={4}>
<h4 className="page-header">Sign Up</h4>
<Row>
<Col xs={12}>
<FormGroup className="OAuthLoginButtons">
<OAuthLoginButton service="facebook" />
<OAuthLoginButton service="google" />
<OAuthLoginButton service="github" />
</FormGroup>
</Col>
</Row>
<form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}>
<p className="SignupWithEmail">Sign Up with an Email Address</p>
<Row>
<Col xs={6}>
<FormGroup>
<ControlLabel>First Name</ControlLabel>
<input
type="text"
name="firstName"
ref={firstName => (this.firstName = firstName)}
className="form-control"
/>
</FormGroup>
</Col>
<Col xs={6}>
<FormGroup>
<ControlLabel>Last Name</ControlLabel>
<input
type="text"
name="lastName"
ref={lastName => (this.lastName = lastName)}
className="form-control"
/>
</FormGroup>
</Col>
</Row>
<FormGroup>
<ControlLabel>Email Address</ControlLabel>
<input
type="email"
name="emailAddress"
ref={emailAddress => (this.emailAddress = emailAddress)}
className="form-control"
/>
</FormGroup>
<FormGroup>
<ControlLabel>Password</ControlLabel>
<input
type="password"
name="password"
ref={password => (this.password = password)}
className="form-control"
/>
<InputHint>Use at least six characters.</InputHint>
</FormGroup>
<Button type="submit" bsStyle="success">Sign Up</Button>
<p className="HaveAnAccount">
Already have an account? <Link to="/login">Log In</Link>.
</p>
</form>
</Col>
</Row>
</div>);
}
}
Signup.propTypes = {
history: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
export default Signup;

View file

@ -0,0 +1,47 @@
@import '../../stylesheets/mixins';
@import '../../stylesheets/colors';
.Signup {
.page-header {
margin-top: 0;
}
form {
position: relative;
border-top: 1px solid $gray-lighter;
margin-top: 15px;
padding-top: 25px;
.SignupWithEmail {
display: inline-block;
background: #fff;
padding: 0 10px;
position: absolute;
top: -12px;
left: 50%;
margin-left: -106px;
}
.HaveAnAccount {
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid $gray-lighter;
}
}
}
@include breakpoint(tablet) {
.Signup {
.page-header {
margin-top: 10px;
}
}
}
@include breakpoint(desktop) {
.Signup {
.page-header {
margin-top: 20px;
}
}
}

View file

@ -0,0 +1,15 @@
$primary: #337ab7;
$success: #5cb85c;
$info: #5bc0de;
$warning: #f0ad4e;
$danger: #d9534f;
$gray-darker: #222;
$gray-dark: #333;
$gray: #555;
$gray-light: #777;
$gray-lighter: #eee;
$facebook: #3b5998;
$google: #ea4335;
$github: $gray-dark;

View file

@ -0,0 +1,12 @@
@import './mixins';
@import './colors';
form {
label.error {
display: block;
margin-top: 8px;
font-size: 13px;
font-weight: normal;
color: $danger;
}
}

View file

@ -0,0 +1,23 @@
@mixin breakpoint($point) {
@if $point == desktop-large {
@media (min-width: 1200px) { @content; }
} @else if $point == desktop {
@media (min-width: 1024px) { @content; }
} @else if $point == tablet {
@media (min-width: 768px) { @content; }
} @else if $point == handheld-large {
@media (min-width: 480px) { @content; }
} @else {
@media (min-width: $point) { @content; }
}
}
@mixin ie {
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
@content;
}
@supports (-ms-accelerator:true) {
@content;
}
}

View file

@ -0,0 +1,2 @@
@import './colors';
@import './forms';

77
package.json Normal file
View file

@ -0,0 +1,77 @@
{
"name": "pup",
"private": true,
"scripts": {
"start": "meteor --settings settings-development.json",
"test": "jest"
},
"dependencies": {
"babel-runtime": "^6.20.0",
"jquery": "^2.2.4",
"jquery-validation": "^1.16.0",
"meteor-node-stubs": "~0.2.4",
"moment": "^2.18.1",
"prop-types": "^15.5.10",
"react": "^15.5.4",
"react-addons-pure-render-mixin": "^15.5.2",
"react-bootstrap": "^0.31.0",
"react-dom": "^15.5.4",
"react-router-bootstrap": "^0.24.2",
"react-router-dom": "^4.1.1",
"simpl-schema": "^0.3.0"
},
"devDependencies": {
"babel-jest": "^20.0.3",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"enzyme": "^2.8.2",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.1",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^5.0.3",
"eslint-plugin-meteor": "^4.0.1",
"eslint-plugin-react": "^7.0.1",
"jest": "^20.0.3"
},
"eslintConfig": {
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
"plugins": [
"meteor",
"react"
],
"extends": [
"airbnb",
"plugin:meteor/recommended",
"plugin:react/recommended"
],
"env": {
"browser": true
},
"globals": {
"expect": false
},
"rules": {
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"import/extensions": 0,
"no-underscore-dangle": [
"error",
{
"allow": [
"_id",
"_ensureIndex",
"_verifyEmailToken",
"_resetPasswordToken",
"_name"
]
}
],
"class-methods-use-this": 0,
"react/jsx-filename-extension": 0
}
}
}

5
server/main.js Normal file
View file

@ -0,0 +1,5 @@
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// code to run on server at startup
});

19
settings-development.json Normal file
View file

@ -0,0 +1,19 @@
{
"public": {},
"private": {
"OAuth": {
"facebook": {
"clientId": "",
"secret": ""
},
"google": {
"clientId": "",
"secret": ""
},
"github": {
"clientId": "",
"secret": ""
}
}
}
}