diff --git a/client/src/templates/Challenges/classic/editor.tsx b/client/src/templates/Challenges/classic/editor.tsx index e1735647010..5ca8442d0b8 100644 --- a/client/src/templates/Challenges/classic/editor.tsx +++ b/client/src/templates/Challenges/classic/editor.tsx @@ -34,10 +34,7 @@ import { } from '../../../redux/prop-types'; import { editorToneOptions } from '../../../utils/tone/editor-config'; import { editorNotes } from '../../../utils/tone/editor-notes'; -import { - challengeTypes, - isFinalProject -} from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared/config/challenge-types'; import { executeChallenge, saveEditorContent, @@ -66,8 +63,8 @@ import { setScrollbarArrowStyles } from '../utils/index'; import { getScrollbarWidth } from '../../../utils/scrollbar-width'; +import { isProjectBased } from '../../../utils/curriculum-layout'; import LowerJaw from './lower-jaw'; - import './editor.css'; const MonacoEditor = Loadable(() => import('react-monaco-editor')); @@ -511,7 +508,7 @@ const Editor = (props: EditorProps): JSX.Element => { monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter ], run: () => { - if (props.usesMultifileEditor && !isFinalProject(props.challengeType)) { + if (props.usesMultifileEditor && !isProjectBased(props.challengeType)) { if (challengeIsComplete()) { tryToSubmitChallenge(); } else { diff --git a/client/src/templates/Challenges/components/hotkeys.tsx b/client/src/templates/Challenges/components/hotkeys.tsx index 67b143859d0..e35206b67b8 100644 --- a/client/src/templates/Challenges/components/hotkeys.tsx +++ b/client/src/templates/Challenges/components/hotkeys.tsx @@ -23,7 +23,7 @@ import { challengeTestsSelector } from '../redux/selectors'; import './hotkeys.css'; -import { isFinalProject } from '../../../../../shared/config/challenge-types'; +import { isProjectBased } from '../../../utils/curriculum-layout'; import type { EditorProps } from '../classic/editor'; const mapStateToProps = createSelector( @@ -121,7 +121,7 @@ function Hotkeys({ if ( usesMultifileEditor && typeof challengeType == 'number' && - !isFinalProject(challengeType) + !isProjectBased(challengeType) ) { if (testsArePassing) { submitChallenge(); diff --git a/client/src/templates/Introduction/components/block.tsx b/client/src/templates/Introduction/components/block.tsx index c671623e72a..6da94eb13d9 100644 --- a/client/src/templates/Introduction/components/block.tsx +++ b/client/src/templates/Introduction/components/block.tsx @@ -19,15 +19,7 @@ import { completedChallengesSelector } from '../../../redux/selectors'; import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types'; import { playTone } from '../../../utils/tone'; import { makeExpandedBlockSelector, toggleBlock } from '../redux'; -import { - isNewJsCert, - isSciCompPyCert, - isNewRespCert -} from '../../../utils/is-a-cert'; -import { - isCodeAllyPractice, - isFinalProject -} from '../../../../../shared/config/challenge-types'; +import { isGridBased, isProjectBased } from '../../../utils/curriculum-layout'; import Challenges from './challenges'; import '../intro.css'; @@ -118,21 +110,12 @@ class Block extends Component { }); const isProjectBlock = challenges.some(({ challenge }) => { - const isTakeHomeProject = blockDashedName === 'take-home-projects'; - - const projectCondition = [ - isFinalProject(challenge.challengeType), - isCodeAllyPractice(challenge.challengeType) - ].some(Boolean); - - return projectCondition && !isTakeHomeProject; + return isProjectBased(challenge.challengeType, blockDashedName); }); - const isGridBlock = [ - isNewRespCert(superBlock), - isNewJsCert(superBlock), - isSciCompPyCert(superBlock) && !isProjectBlock - ].some(Boolean); + const isGridBlock = challenges.some(({ challenge }) => { + return isGridBased(superBlock, challenge.challengeType); + }); const isAudited = isAuditedSuperBlock(curriculumLocale, superBlock, { showNewCurriculum, diff --git a/client/src/utils/curriculum-layout.ts b/client/src/utils/curriculum-layout.ts new file mode 100644 index 00000000000..b4e818f2b1c --- /dev/null +++ b/client/src/utils/curriculum-layout.ts @@ -0,0 +1,47 @@ +import { challengeTypes } from '../../../shared/config/challenge-types'; +import { SuperBlocks } from '../../../shared/config/superblocks'; + +// Show a grid layout on the superblock level + +const gridBasedSuperBlocks = [ + SuperBlocks.RespWebDesignNew, + SuperBlocks.JsAlgoDataStructNew, + SuperBlocks.SciCompPy +]; + +export const isGridBased = ( + superBlock: SuperBlocks, + challengeType: unknown = null +) => { + // Python projects should not be displayed as a grid, but should be displayed + // as a list of projects. Otherwise, if we do not do this the project will be + // shown as a single certificaton project, which is not what we want. + + if (challengeType === 10) return false; + + return gridBasedSuperBlocks.includes(superBlock); +}; + +// Show a single project in a certification layout + +const projectBasedChallengeTypes = [ + challengeTypes.frontEndProject, + challengeTypes.backEndProject, + challengeTypes.jsProject, + challengeTypes.pythonProject, + challengeTypes.codeAllyCert, + challengeTypes.multifileCertProject, + challengeTypes.exam, + challengeTypes.codeAllyPractice +]; + +export const isProjectBased = ( + challengeType: number, + blockDashedName: unknown = null +) => { + // Is project based but should be collapsable, this differs from the + // other projects which are not collapsable. + if (blockDashedName === 'take-home-projects') return false; + + return projectBasedChallengeTypes.includes(challengeType); +}; diff --git a/client/src/utils/get-completion-percentage.ts b/client/src/utils/get-completion-percentage.ts index 1131a054f57..fc1516f46e7 100644 --- a/client/src/utils/get-completion-percentage.ts +++ b/client/src/utils/get-completion-percentage.ts @@ -1,5 +1,5 @@ import { AllChallengesInfo } from '../redux/prop-types'; -import { isFinalProject } from '../../../shared/config/challenge-types'; +import { isProjectBased } from './curriculum-layout'; export function getCompletedPercentage( completedChallengesIds: string[] = [], @@ -48,7 +48,7 @@ export const getCurrentBlockIds = ( .filter(edge => edge.node.challenge.block === block) .map(edge => edge.node.challenge.id); - return isFinalProject(challengeType) + return isProjectBased(challengeType) ? currentCertificateIds : currentBlockIds; }; diff --git a/client/src/utils/is-a-cert.ts b/client/src/utils/is-a-cert.ts index 67ed7c918ff..98bd0c11f21 100644 --- a/client/src/utils/is-a-cert.ts +++ b/client/src/utils/is-a-cert.ts @@ -1,25 +1,9 @@ import { SuperBlocks } from '../../../shared/config/superblocks'; -export function isNewRespCert(superBlock: string): boolean { - return superBlock === String(SuperBlocks.RespWebDesignNew); -} - export function isOldRespCert(superBlock: string): boolean { return superBlock === String(SuperBlocks.RespWebDesign); } -export function isNewJsCert(superBlock: string): boolean { - return superBlock === String(SuperBlocks.JsAlgoDataStructNew); -} - export function isRelationalDbCert(superBlock: string): boolean { return superBlock === String(SuperBlocks.RelationalDb); } - -export function isCollegeAlgebraPyCert(superBlock: string): boolean { - return superBlock === String(SuperBlocks.CollegeAlgebraPy); -} - -export function isSciCompPyCert(superBlock: string): boolean { - return superBlock === String(SuperBlocks.SciCompPy); -} diff --git a/shared/config/challenge-types.ts b/shared/config/challenge-types.ts index ed1fea5ef87..392d41b5a38 100644 --- a/shared/config/challenge-types.ts +++ b/shared/config/challenge-types.ts @@ -50,28 +50,6 @@ export const challengeTypes = { fillInTheBlank }; -export const isFinalProject = (challengeType: number) => { - // TODO: remove the type check once everything is converted to TS - if (typeof challengeType !== 'number') - throw Error('challengeType must be a number'); - return ( - challengeType === frontEndProject || - challengeType === backEndProject || - challengeType === jsProject || - challengeType === pythonProject || - challengeType === codeAllyCert || - challengeType === multifileCertProject || - challengeType === exam - ); -}; - -export const isCodeAllyPractice = (challengeType: number) => { - // TODO: remove the type check once everything is converted to TS - if (typeof challengeType !== 'number') - throw Error('challengeType must be a number'); - return challengeType === codeAllyPractice; -}; - export const hasNoTests = (challengeType: number): boolean => challengeType === multipleChoice || challengeType === theOdinProject ||