Submit projects

This commit is contained in:
Berkeley Martinez
2016-06-08 11:11:13 -07:00
parent dc36396369
commit c8b0a6cf74
5 changed files with 95 additions and 48 deletions

View File

@ -39,7 +39,7 @@ const backEndFieldValidators = {
githubLink: makeRequired(isValidURL) githubLink: makeRequired(isValidURL)
}; };
export function SolutionInput({ solution }) { export function SolutionInput({ solution, placeholder }) {
return ( return (
<FormGroup <FormGroup
controlId='solution' controlId='solution'
@ -47,7 +47,7 @@ export function SolutionInput({ solution }) {
> >
<FormControl <FormControl
name='solution' name='solution'
placeholder='https://codepen.io/your-pen-here' placeholder={ placeholder }
type='url' type='url'
{ ...solution} { ...solution}
/> />
@ -55,7 +55,10 @@ export function SolutionInput({ solution }) {
); );
} }
SolutionInput.propTypes = { solution: PropTypes.object }; SolutionInput.propTypes = {
solution: PropTypes.object,
placeholder: PropTypes.string
};
export function _FrontEndForm({ export function _FrontEndForm({
fields, fields,
@ -72,7 +75,14 @@ export function _FrontEndForm({
name='NewFrontEndProject' name='NewFrontEndProject'
onSubmit={ handleSubmit(submitChallenge)} onSubmit={ handleSubmit(submitChallenge)}
> >
{ isSubmitting ? <SolutionInput { ...fields }/> : null } {
isSubmitting ?
<SolutionInput
placeholder='https://codepen/your-project'
{ ...fields }
/> :
null
}
<Button <Button
block={ true } block={ true }
bsStyle='primary' bsStyle='primary'
@ -113,7 +123,14 @@ export function _BackEndForm({
name='NewBackEndProject' name='NewBackEndProject'
onSubmit={ handleSubmit(submitChallenge)} onSubmit={ handleSubmit(submitChallenge)}
> >
{ isSubmitting ? <SolutionInput solution={ solution }/> : null } {
isSubmitting ?
<SolutionInput
placeholder='https://your-app.com'
solution={ solution }
/> :
null
}
{ isSubmitting ? { isSubmitting ?
<FormGroup <FormGroup
controlId='githubLink' controlId='githubLink'

View File

@ -15,6 +15,8 @@ import {
import { getNextChallenge } from '../utils'; import { getNextChallenge } from '../utils';
import { challengeSelector } from './selectors'; import { challengeSelector } from './selectors';
import { backEndProject } from '../../../utils/challengeTypes';
import { randomCompliment } from '../../../utils/get-words';
import { postJSON$ } from '../../../../utils/ajax-stream'; import { postJSON$ } from '../../../../utils/ajax-stream';
function completedChallenge(state) { function completedChallenge(state) {
@ -84,13 +86,53 @@ function submitModern(type, state) {
})); }));
} }
function submitFrontEnd() { function submitProject(type, state, { solution, githubLink }) {
return Observable.just(null); const {
challenge: { id, challengeType }
} = challengeSelector(state);
const {
app: { isSignedIn, csrfToken }
} = state;
const body = {
id,
challengeType,
solution,
_csrf: csrfToken
};
if (challengeType === backEndProject) {
body.githubLink = githubLink;
}
const saveChallenge$ = postJSON$('/project-completed', body)
.retry(3)
.flatMap(({ alreadyCompleted, points }) => {
return Observable.of(
makeToast({
message:
'Challenge saved.' +
(alreadyCompleted ? '' : ' First time Completed!'),
title: 'Saved',
type: 'info'
}),
updatePoints(points)
);
})
.catch(createErrorObservable);
const challengeCompleted$ = Observable.of(
makeToast({
title: randomCompliment(),
message: isSignedIn ? ' Saving...' : 'Moving on to next challenge.',
type: 'success'
})
// moveToNextChallenge()
);
return Observable.merge(saveChallenge$, challengeCompleted$);
} }
const submitTypes = { const submitTypes = {
tests: submitModern, tests: submitModern,
'project.frontEnd': submitFrontEnd 'project.frontEnd': submitProject,
'project.backEnd': submitProject
}; };
export default function completionSaga(actions$, getState) { export default function completionSaga(actions$, getState) {

View File

@ -1,11 +1,11 @@
export const html = '0'; export const html = 0;
export const js = '1'; export const js = 1;
export const oldVideo = '2'; export const oldVideo = 2;
export const simpleProject = '2'; export const simpleProject = 2;
export const zipline = '3'; export const zipline = 3;
export const frontEndProject = '3'; export const frontEndProject = 3;
export const basejump = '4'; export const basejump = 4;
export const backEndProject = '4'; export const backEndProject = 4;
export const bonfire = '5'; export const bonfire = 5;
export const video = '6'; export const video = 6;
export const step = '7'; export const step = 7;

View File

@ -86,7 +86,7 @@
"type": "array" "type": "array"
}, },
"challengeType": { "challengeType": {
"type": "string" "type": "number"
}, },
"MDNlinks": { "MDNlinks": {
"type": "array" "type": "array"

View File

@ -69,15 +69,23 @@ module.exports = function(app) {
); );
router.post( router.post(
'/completed-challenge/', '/completed-challenge',
send200toNonUser, send200toNonUser,
completedChallenge completedChallenge
); );
// deprecate endpoint
// remove once new endpoint is live
router.post( router.post(
'/completed-zipline-or-basejump', '/completed-zipline-or-basejump',
send200toNonUser, send200toNonUser,
completedZiplineOrBasejump projectCompleted
);
router.post(
'/project-completed',
send200toNonUser,
projectCompleted
); );
app.use(router); app.use(router);
@ -136,9 +144,6 @@ module.exports = function(app) {
function completedChallenge(req, res, next) { function completedChallenge(req, res, next) {
req.checkBody('id', 'id must be an ObjectId').isMongoId(); req.checkBody('id', 'id must be an ObjectId').isMongoId();
req.checkBody('name', 'name must be at least 3 characters')
.isString()
.isLength({ min: 3 });
req.checkBody('challengeType', 'challengeType must be an integer') req.checkBody('challengeType', 'challengeType must be an integer')
.isNumber(); .isNumber();
@ -158,24 +163,12 @@ module.exports = function(app) {
return req.user.getChallengeMap$() return req.user.getChallengeMap$()
.flatMap(() => { .flatMap(() => {
const completedDate = Date.now(); const completedDate = Date.now();
const { const { id, solution, timezone } = req.body;
id,
name,
challengeType,
solution,
timezone
} = req.body;
const { alreadyCompleted, updateData } = buildUserUpdate( const { alreadyCompleted, updateData } = buildUserUpdate(
req.user, req.user,
id, id,
{ { id, solution, completedDate },
id,
challengeType,
solution,
name,
completedDate
},
timezone timezone
); );
@ -197,15 +190,11 @@ module.exports = function(app) {
.subscribe(() => {}, next); .subscribe(() => {}, next);
} }
function completedZiplineOrBasejump(req, res, next) { function projectCompleted(req, res, next) {
const type = accepts(req).type('html', 'json', 'text'); const type = accepts(req).type('html', 'json', 'text');
req.checkBody('id', 'id must be an ObjectId').isMongoId(); req.checkBody('id', 'id must be an ObjectId').isMongoId();
req.checkBody('name', 'Name must be at least 3 characters') req.checkBody('challengeType', 'must be a number').isNumber();
.isString() req.checkBody('solution', 'solution must be a URL').isURL();
.isLength({ min: 3 });
req.checkBody('challengeType', 'must be a number')
.isNumber();
req.checkBody('solution', 'solution must be a url').isURL();
const errors = req.validationErrors(true); const errors = req.validationErrors(true);
@ -221,9 +210,8 @@ module.exports = function(app) {
const completedChallenge = _.pick( const completedChallenge = _.pick(
body, body,
[ 'id', 'name', 'solution', 'githubLink', 'challengeType' ] [ 'id', 'solution', 'githubLink', 'challengeType' ]
); );
completedChallenge.challengeType = +completedChallenge.challengeType;
completedChallenge.completedDate = Date.now(); completedChallenge.completedDate = Date.now();
if ( if (