Submit projects
This commit is contained in:
@ -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'
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
"challengeType": {
|
"challengeType": {
|
||||||
"type": "string"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"MDNlinks": {
|
"MDNlinks": {
|
||||||
"type": "array"
|
"type": "array"
|
||||||
|
@ -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 (
|
||||||
|
Reference in New Issue
Block a user