Merge pull request #10041 from BerkeleyTrue/regression/add-current-challenge
Fix(accounts): show challenge info on user profile
This commit is contained in:
@ -12,21 +12,10 @@ import {
|
|||||||
delayedRedirect,
|
delayedRedirect,
|
||||||
createErrorObservable
|
createErrorObservable
|
||||||
} from '../../../redux/actions';
|
} from '../../../redux/actions';
|
||||||
|
import createNameIdMap from '../../../../utils/create-name-id-map';
|
||||||
|
|
||||||
const { fetchChallenge, fetchChallenges, replaceChallenge } = types;
|
const { fetchChallenge, fetchChallenges, replaceChallenge } = types;
|
||||||
|
|
||||||
function createNameIdMap(entities) {
|
|
||||||
const { challenge } = entities;
|
|
||||||
return {
|
|
||||||
...entities,
|
|
||||||
challengeIdToName: Object.keys(challenge)
|
|
||||||
.reduce((map, challengeName) => {
|
|
||||||
map[challenge[challengeName].id] = challenge[challengeName].dashedName;
|
|
||||||
return map;
|
|
||||||
}, {})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function fetchChallengesSaga(action$, getState, { services }) {
|
export default function fetchChallengesSaga(action$, getState, { services }) {
|
||||||
return action$
|
return action$
|
||||||
.filter(({ type }) => (
|
.filter(({ type }) => (
|
||||||
|
12
common/utils/create-name-id-map.js
Normal file
12
common/utils/create-name-id-map.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// createNameIdMap(entities: Object) => Object
|
||||||
|
export default function createNameIdMap(entities) {
|
||||||
|
const { challenge } = entities;
|
||||||
|
return {
|
||||||
|
...entities,
|
||||||
|
challengeIdToName: Object.keys(challenge)
|
||||||
|
.reduce((map, challengeName) => {
|
||||||
|
map[challenge[challengeName].id] = challenge[challengeName].dashedName;
|
||||||
|
return map;
|
||||||
|
}, {})
|
||||||
|
};
|
||||||
|
}
|
@ -3,15 +3,12 @@ import moment from 'moment-timezone';
|
|||||||
import { Observable } from 'rx';
|
import { Observable } from 'rx';
|
||||||
import debugFactory from 'debug';
|
import debugFactory from 'debug';
|
||||||
|
|
||||||
import supportedLanguages from '../../common/utils/supported-languages';
|
|
||||||
import {
|
import {
|
||||||
frontEndChallengeId,
|
frontEndChallengeId,
|
||||||
dataVisChallengeId,
|
dataVisChallengeId,
|
||||||
backEndChallengeId
|
backEndChallengeId
|
||||||
} from '../utils/constantStrings.json';
|
} from '../utils/constantStrings.json';
|
||||||
|
|
||||||
import certTypes from '../utils/certTypes.json';
|
import certTypes from '../utils/certTypes.json';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ifNoUser401,
|
ifNoUser401,
|
||||||
ifNoUserRedirectTo
|
ifNoUserRedirectTo
|
||||||
@ -22,6 +19,9 @@ import {
|
|||||||
calcCurrentStreak,
|
calcCurrentStreak,
|
||||||
calcLongestStreak
|
calcLongestStreak
|
||||||
} from '../utils/user-stats';
|
} from '../utils/user-stats';
|
||||||
|
import supportedLanguages from '../../common/utils/supported-languages';
|
||||||
|
import createNameIdMap from '../../common/utils/create-name-id-map';
|
||||||
|
import { cachedMap } from '../utils/map';
|
||||||
|
|
||||||
const debug = debugFactory('fcc:boot:user');
|
const debug = debugFactory('fcc:boot:user');
|
||||||
const sendNonUserToMap = ifNoUserRedirectTo('/map');
|
const sendNonUserToMap = ifNoUserRedirectTo('/map');
|
||||||
@ -85,25 +85,35 @@ function getChallengeGroup(challenge) {
|
|||||||
return 'challenges';
|
return 'challenges';
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildDisplayChallenges(challengeMap: Object, tz: String) => Observable[{
|
// buildDisplayChallenges(
|
||||||
|
// entities: { challenge: Object, challengeIdToName: Object },
|
||||||
|
// challengeMap: Object,
|
||||||
|
// tz: String
|
||||||
|
// ) => Observable[{
|
||||||
// algorithms: Array,
|
// algorithms: Array,
|
||||||
// projects: Array,
|
// projects: Array,
|
||||||
// challenges: Array
|
// challenges: Array
|
||||||
// }]
|
// }]
|
||||||
function buildDisplayChallenges(challengeMap = {}, timezone) {
|
function buildDisplayChallenges(
|
||||||
return Observable.from(Object.keys(challengeMap))
|
{ challenge: challengeMap = {}, challengeIdToName },
|
||||||
.map(challengeId => challengeMap[challengeId])
|
userChallengeMap = {},
|
||||||
.map(challenge => {
|
timezone
|
||||||
let finalChallenge = { ...challenge };
|
) {
|
||||||
if (challenge.completedDate) {
|
return Observable.from(Object.keys(userChallengeMap))
|
||||||
|
.map(challengeId => userChallengeMap[challengeId])
|
||||||
|
.map(userChallenge => {
|
||||||
|
const challengeId = userChallenge.id;
|
||||||
|
const challenge = challengeMap[ challengeIdToName[challengeId] ];
|
||||||
|
let finalChallenge = { ...userChallenge, ...challenge };
|
||||||
|
if (userChallenge.completedDate) {
|
||||||
finalChallenge.completedDate = moment
|
finalChallenge.completedDate = moment
|
||||||
.tz(challenge.completedDate, timezone)
|
.tz(userChallenge.completedDate, timezone)
|
||||||
.format(dateFormat);
|
.format(dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (challenge.lastUpdated) {
|
if (userChallenge.lastUpdated) {
|
||||||
finalChallenge.lastUpdated = moment
|
finalChallenge.lastUpdated = moment
|
||||||
.tz(challenge.lastUpdated, timezone)
|
.tz(userChallenge.lastUpdated, timezone)
|
||||||
.format(dateFormat);
|
.format(dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +138,8 @@ module.exports = function(app) {
|
|||||||
const router = app.loopback.Router();
|
const router = app.loopback.Router();
|
||||||
const api = app.loopback.Router();
|
const api = app.loopback.Router();
|
||||||
const User = app.models.User;
|
const User = app.models.User;
|
||||||
|
const Block = app.models.Block;
|
||||||
|
const map$ = cachedMap(Block);
|
||||||
function findUserByUsername$(username, fields) {
|
function findUserByUsername$(username, fields) {
|
||||||
return observeQuery(
|
return observeQuery(
|
||||||
User,
|
User,
|
||||||
@ -187,7 +199,7 @@ module.exports = function(app) {
|
|||||||
(req, res) => res.redirect(req.url.replace('full-stack', 'back-end'))
|
(req, res) => res.redirect(req.url.replace('full-stack', 'back-end'))
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get('/:username', returnUser);
|
router.get('/:username', showUserProfile);
|
||||||
|
|
||||||
app.use('/:lang', router);
|
app.use('/:lang', router);
|
||||||
app.use(api);
|
app.use(api);
|
||||||
@ -248,7 +260,7 @@ module.exports = function(app) {
|
|||||||
return res.redirect('/' + username);
|
return res.redirect('/' + username);
|
||||||
}
|
}
|
||||||
|
|
||||||
function returnUser(req, res, next) {
|
function showUserProfile(req, res, next) {
|
||||||
const username = req.params.username.toLowerCase();
|
const username = req.params.username.toLowerCase();
|
||||||
const { user } = req;
|
const { user } = req;
|
||||||
|
|
||||||
@ -313,7 +325,12 @@ module.exports = function(app) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildDisplayChallenges(userPortfolio.challengeMap, timezone)
|
return map$.map(({ entities }) => createNameIdMap(entities))
|
||||||
|
.flatMap(entities => buildDisplayChallenges(
|
||||||
|
entities,
|
||||||
|
userPortfolio.challengeMap,
|
||||||
|
timezone
|
||||||
|
))
|
||||||
.map(displayChallenges => ({
|
.map(displayChallenges => ({
|
||||||
...userPortfolio,
|
...userPortfolio,
|
||||||
...displayChallenges,
|
...displayChallenges,
|
||||||
|
@ -1,134 +1,12 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { Observable } from 'rx';
|
import { Observable } from 'rx';
|
||||||
import { Schema, valuesOf, arrayOf, normalize } from 'normalizr';
|
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { nameify, unDasherize } from '../utils';
|
import { unDasherize } from '../utils';
|
||||||
import supportedLanguages from '../../common/utils/supported-languages';
|
import { mapChallengeToLang, cachedMap, getMapForLang } from '../utils/map';
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV !== 'production';
|
const isDev = process.env.NODE_ENV !== 'production';
|
||||||
const isBeta = !!process.env.BETA;
|
const isBeta = !!process.env.BETA;
|
||||||
const challengesRegex = /^(bonfire|waypoint|zipline|basejump|checkpoint)/i;
|
const challengesRegex = /^(bonfire|waypoint|zipline|basejump|checkpoint)/i;
|
||||||
const log = debug('fcc:services:challenges');
|
const log = debug('fcc:services:map');
|
||||||
const challenge = new Schema('challenge', { idAttribute: 'dashedName' });
|
|
||||||
const block = new Schema('block', { idAttribute: 'dashedName' });
|
|
||||||
const superBlock = new Schema('superBlock', { idAttribute: 'dashedName' });
|
|
||||||
|
|
||||||
block.define({
|
|
||||||
challenges: arrayOf(challenge)
|
|
||||||
});
|
|
||||||
|
|
||||||
superBlock.define({
|
|
||||||
blocks: arrayOf(block)
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapSchema = valuesOf(superBlock);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* interface ChallengeMap {
|
|
||||||
* result: [superBlockDashedName: String]
|
|
||||||
* entities: {
|
|
||||||
* superBlock: {
|
|
||||||
* [superBlockDashedName: String]: {
|
|
||||||
* blocks: [blockDashedName: String]
|
|
||||||
* }
|
|
||||||
* },
|
|
||||||
* block: {
|
|
||||||
* [blockDashedName: String]: {
|
|
||||||
* challenges: [challengeDashedName: String]
|
|
||||||
* }
|
|
||||||
* },
|
|
||||||
* challenge: {
|
|
||||||
* [challengeDashedName: String]: Challenge
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
function cachedMap(Block) {
|
|
||||||
const query = {
|
|
||||||
include: 'challenges',
|
|
||||||
order: ['superOrder ASC', 'order ASC']
|
|
||||||
};
|
|
||||||
return Block.find$(query)
|
|
||||||
.flatMap(blocks => Observable.from(blocks.map(block => block.toJSON())))
|
|
||||||
.reduce((map, block) => {
|
|
||||||
if (map[block.superBlock]) {
|
|
||||||
map[block.superBlock].blocks.push(block);
|
|
||||||
} else {
|
|
||||||
map[block.superBlock] = {
|
|
||||||
title: _.startCase(block.superBlock),
|
|
||||||
order: block.superOrder,
|
|
||||||
name: nameify(_.startCase(block.superBlock)),
|
|
||||||
dashedName: block.superBlock,
|
|
||||||
blocks: [block],
|
|
||||||
message: block.superBlockMessage
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}, {})
|
|
||||||
.map(map => normalize(map, mapSchema))
|
|
||||||
.map(map => {
|
|
||||||
// make sure challenges are in the right order
|
|
||||||
map.entities.block = Object.keys(map.entities.block)
|
|
||||||
// turn map into array
|
|
||||||
.map(key => map.entities.block[key])
|
|
||||||
// perform re-order
|
|
||||||
.map(block => {
|
|
||||||
block.challenges = block.challenges.reduce((accu, dashedName) => {
|
|
||||||
const index = map.entities.challenge[dashedName].suborder;
|
|
||||||
accu[index - 1] = dashedName;
|
|
||||||
return accu;
|
|
||||||
}, []);
|
|
||||||
return block;
|
|
||||||
})
|
|
||||||
// turn back into map
|
|
||||||
.reduce((blockMap, block) => {
|
|
||||||
blockMap[block.dashedName] = block;
|
|
||||||
return blockMap;
|
|
||||||
}, {});
|
|
||||||
return map;
|
|
||||||
})
|
|
||||||
.map(map => {
|
|
||||||
// re-order superBlocks result
|
|
||||||
const result = Object.keys(map.result).reduce((result, supName) => {
|
|
||||||
const index = map.entities.superBlock[supName].order;
|
|
||||||
result[index] = supName;
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
return {
|
|
||||||
...map,
|
|
||||||
result
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.shareReplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapChallengeToLang({ translations = {}, ...challenge }, lang) {
|
|
||||||
if (!supportedLanguages[lang]) {
|
|
||||||
lang = 'en';
|
|
||||||
}
|
|
||||||
const translation = translations[lang] || {};
|
|
||||||
if (lang !== 'en') {
|
|
||||||
challenge = {
|
|
||||||
...challenge,
|
|
||||||
...translation
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return challenge;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMapForLang(lang) {
|
|
||||||
return ({ entities: { challenge: challengeMap, ...entities }, result }) => {
|
|
||||||
entities.challenge = Object.keys(challengeMap)
|
|
||||||
.reduce((translatedChallengeMap, key) => {
|
|
||||||
translatedChallengeMap[key] = mapChallengeToLang(
|
|
||||||
challengeMap[key],
|
|
||||||
lang
|
|
||||||
);
|
|
||||||
return translatedChallengeMap;
|
|
||||||
}, {});
|
|
||||||
return { result, entities };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldNotFilterComingSoon({ isComingSoon, isBeta: challengeIsBeta }) {
|
function shouldNotFilterComingSoon({ isComingSoon, isBeta: challengeIsBeta }) {
|
||||||
return isDev ||
|
return isDev ||
|
||||||
|
136
server/utils/map.js
Normal file
136
server/utils/map.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { Observable } from 'rx';
|
||||||
|
import { Schema, valuesOf, arrayOf, normalize } from 'normalizr';
|
||||||
|
|
||||||
|
import { nameify } from '../utils';
|
||||||
|
import supportedLanguages from '../../common/utils/supported-languages';
|
||||||
|
|
||||||
|
const challenge = new Schema('challenge', { idAttribute: 'dashedName' });
|
||||||
|
const block = new Schema('block', { idAttribute: 'dashedName' });
|
||||||
|
const superBlock = new Schema('superBlock', { idAttribute: 'dashedName' });
|
||||||
|
|
||||||
|
block.define({
|
||||||
|
challenges: arrayOf(challenge)
|
||||||
|
});
|
||||||
|
|
||||||
|
superBlock.define({
|
||||||
|
blocks: arrayOf(block)
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapSchema = valuesOf(superBlock);
|
||||||
|
let mapObservableCache;
|
||||||
|
/*
|
||||||
|
* interface ChallengeMap {
|
||||||
|
* result: [superBlockDashedName: String]
|
||||||
|
* entities: {
|
||||||
|
* superBlock: {
|
||||||
|
* [superBlockDashedName: String]: {
|
||||||
|
* blocks: [blockDashedName: String]
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* block: {
|
||||||
|
* [blockDashedName: String]: {
|
||||||
|
* challenges: [challengeDashedName: String]
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* challenge: {
|
||||||
|
* [challengeDashedName: String]: Challenge
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export function cachedMap(Block) {
|
||||||
|
if (mapObservableCache) {
|
||||||
|
return mapObservableCache;
|
||||||
|
}
|
||||||
|
const query = {
|
||||||
|
include: 'challenges',
|
||||||
|
order: ['superOrder ASC', 'order ASC']
|
||||||
|
};
|
||||||
|
const map$ = Block.find$(query)
|
||||||
|
.flatMap(blocks => Observable.from(blocks.map(block => block.toJSON())))
|
||||||
|
.reduce((map, block) => {
|
||||||
|
if (map[block.superBlock]) {
|
||||||
|
map[block.superBlock].blocks.push(block);
|
||||||
|
} else {
|
||||||
|
map[block.superBlock] = {
|
||||||
|
title: _.startCase(block.superBlock),
|
||||||
|
order: block.superOrder,
|
||||||
|
name: nameify(_.startCase(block.superBlock)),
|
||||||
|
dashedName: block.superBlock,
|
||||||
|
blocks: [block],
|
||||||
|
message: block.superBlockMessage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, {})
|
||||||
|
.map(map => normalize(map, mapSchema))
|
||||||
|
.map(map => {
|
||||||
|
// make sure challenges are in the right order
|
||||||
|
map.entities.block = Object.keys(map.entities.block)
|
||||||
|
// turn map into array
|
||||||
|
.map(key => map.entities.block[key])
|
||||||
|
// perform re-order
|
||||||
|
.map(block => {
|
||||||
|
block.challenges = block.challenges.reduce((accu, dashedName) => {
|
||||||
|
const index = map.entities.challenge[dashedName].suborder;
|
||||||
|
accu[index - 1] = dashedName;
|
||||||
|
return accu;
|
||||||
|
}, []);
|
||||||
|
return block;
|
||||||
|
})
|
||||||
|
// turn back into map
|
||||||
|
.reduce((blockMap, block) => {
|
||||||
|
blockMap[block.dashedName] = block;
|
||||||
|
return blockMap;
|
||||||
|
}, {});
|
||||||
|
return map;
|
||||||
|
})
|
||||||
|
.map(map => {
|
||||||
|
// re-order superBlocks result
|
||||||
|
const result = Object.keys(map.result).reduce((result, supName) => {
|
||||||
|
const index = map.entities.superBlock[supName].order;
|
||||||
|
result[index] = supName;
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
return {
|
||||||
|
...map,
|
||||||
|
result
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.shareReplay();
|
||||||
|
mapObservableCache = map$;
|
||||||
|
return map$;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapChallengeToLang(
|
||||||
|
{ translations = {}, ...challenge },
|
||||||
|
lang
|
||||||
|
) {
|
||||||
|
if (!supportedLanguages[lang]) {
|
||||||
|
lang = 'en';
|
||||||
|
}
|
||||||
|
const translation = translations[lang] || {};
|
||||||
|
if (lang !== 'en') {
|
||||||
|
challenge = {
|
||||||
|
...challenge,
|
||||||
|
...translation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMapForLang(lang) {
|
||||||
|
return ({ entities: { challenge: challengeMap, ...entities }, result }) => {
|
||||||
|
entities.challenge = Object.keys(challengeMap)
|
||||||
|
.reduce((translatedChallengeMap, key) => {
|
||||||
|
translatedChallengeMap[key] = mapChallengeToLang(
|
||||||
|
challengeMap[key],
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
return translatedChallengeMap;
|
||||||
|
}, {});
|
||||||
|
return { result, entities };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -64,20 +64,6 @@ block content
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var cal = new CalHeatMap();
|
var cal = new CalHeatMap();
|
||||||
var calendar = !{JSON.stringify(calender)};
|
var calendar = !{JSON.stringify(calender)};
|
||||||
/*
|
|
||||||
var estUTCOffset = -5;
|
|
||||||
// moment returns the utc offset in minutes
|
|
||||||
var userUTCOffset = moment().utcOffset() / 60;
|
|
||||||
var secondsToOffset =
|
|
||||||
(estUTCOffset - userUTCOffset) * 3600;
|
|
||||||
var offsetCalendar = {};
|
|
||||||
for (var prop in calendar) {
|
|
||||||
if (calendar.hasOwnProperty(prop)) {
|
|
||||||
var offsetProp = prop + secondsToOffset;
|
|
||||||
offsetCalendar[offsetProp] = calendar[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
cal.init({
|
cal.init({
|
||||||
itemSelector: "#cal-heatmap",
|
itemSelector: "#cal-heatmap",
|
||||||
domain: "month",
|
domain: "month",
|
||||||
@ -118,13 +104,13 @@ block content
|
|||||||
for challenge in projects
|
for challenge in projects
|
||||||
tr
|
tr
|
||||||
td.col-xs-5.hidden-xs
|
td.col-xs-5.hidden-xs
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name), target='_blank')= removeOldTerms(challenge.name)
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}', target='_blank')= challenge.name
|
||||||
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
||||||
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
||||||
td.col-xs-2.hidden-xs
|
td.col-xs-2.hidden-xs
|
||||||
a(href=challenge.solution, target='_blank') View project
|
a(href=challenge.solution, target='_blank') View project
|
||||||
td.col-xs-12.visible-xs
|
td.col-xs-12.visible-xs
|
||||||
a(href=challenge.solution, target='_blank')= removeOldTerms(challenge.name)
|
a(href=challenge.solution, target='_blank')= challenge.name
|
||||||
if (algorithms.length > 0)
|
if (algorithms.length > 0)
|
||||||
.col-sm-12
|
.col-sm-12
|
||||||
table.table.table-striped
|
table.table.table-striped
|
||||||
@ -136,19 +122,19 @@ block content
|
|||||||
th.col-xs-2.hidden-xs Solution
|
th.col-xs-2.hidden-xs Solution
|
||||||
for challenge in algorithms
|
for challenge in algorithms
|
||||||
tr
|
tr
|
||||||
td.col-xs-5.hidden-xs= removeOldTerms(challenge.name)
|
td.col-xs-5.hidden-xs= challenge.name
|
||||||
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
||||||
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
||||||
td.col-xs-2.hidden-xs
|
td.col-xs-2.hidden-xs
|
||||||
if (challenge.solution)
|
if (challenge.solution)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank') View solution
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}?solution=#{encodeURIComponent(encodeFcc(challenge.solution))}', target='_blank') View solution
|
||||||
else
|
else
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name)) View this challenge
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}') View this challenge
|
||||||
td.col-xs-12.visible-xs
|
td.col-xs-12.visible-xs
|
||||||
if (challenge.solution)
|
if (challenge.solution)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank')= removeOldTerms(challenge.name)
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}?solution=#{encodeURIComponent(encodeFcc(challenge.solution))}', target='_blank')= challenge.name
|
||||||
else
|
else
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name))= removeOldTerms(challenge.name)
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}')= challenge.name
|
||||||
if (challenges.length > 0)
|
if (challenges.length > 0)
|
||||||
.col-sm-12
|
.col-sm-12
|
||||||
table.table.table-striped
|
table.table.table-striped
|
||||||
@ -160,20 +146,20 @@ block content
|
|||||||
th.col-xs-2.hidden-xs Solution
|
th.col-xs-2.hidden-xs Solution
|
||||||
for challenge in challenges
|
for challenge in challenges
|
||||||
tr
|
tr
|
||||||
td.col-xs-5.hidden-xs= removeOldTerms(challenge.name)
|
td.col-xs-5.hidden-xs= challenge.name
|
||||||
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
||||||
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
||||||
td.col-xs-2.hidden-xs
|
td.col-xs-2.hidden-xs
|
||||||
if (challenge.solution && challenge.name)
|
if (challenge.solution && challenge.name)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank') View solution
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}?solution=#{encodeURIComponent(encodeFcc(challenge.solution))}', target='_blank') View solution
|
||||||
else if (challenge.name)
|
else if (challenge.name)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name)) View this challenge
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}') View this challenge
|
||||||
else
|
else
|
||||||
span N/A
|
span N/A
|
||||||
td.col-xs-12.visible-xs
|
td.col-xs-12.visible-xs
|
||||||
if (challenge.solution && challenge.name)
|
if (challenge.solution && challenge.name)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank')= removeOldTerms(challenge.name)
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}?solution=#{encodeURIComponent(encodeFcc(challenge.solution))}', target='_blank')= challenge.name
|
||||||
else if (challenge.name)
|
else if (challenge.name)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name))= removeOldTerms(challenge.name)
|
a(href='/challenges/#{challenge.block}/#{challenge.dashedName}')= challenge.name
|
||||||
else
|
else
|
||||||
span N/A
|
span N/A
|
||||||
|
Reference in New Issue
Block a user