mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-20 12:03:11 -04:00
feat(curriculum): add Full Stack Open Skeleton (#61974)
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
This commit is contained in:
@@ -4898,6 +4898,45 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"full-stack-open": {
|
||||
"title": "Full Stack Open",
|
||||
"intro": ["A good intro is to be added here."],
|
||||
"blocks": {
|
||||
"cat-blog-page": {
|
||||
"title": "Build a Cat Blog Page",
|
||||
"intro": [
|
||||
"In this workshop, you will build an HTML only blog page using semantic elements including the <code>main</code>, <code>nav</code>, <code>article</code> and <code>footer</code> elements."
|
||||
]
|
||||
}
|
||||
},
|
||||
"chapters": {
|
||||
"part-0": "Fundamentals of Web Apps",
|
||||
"part-1": "Introduction to React",
|
||||
"part-2": "Communicating with Servers",
|
||||
"part-3": "Programming a Server with NodeJS and Express",
|
||||
"part-4": "Testing Express Servers, User Administration",
|
||||
"part-5": "Testing React Apps",
|
||||
"part-6": "Advanced State Management",
|
||||
"part-7": "React router, custom hooks, styling app with CSS and webpack",
|
||||
"part-8": "GraphQL",
|
||||
"part-9": "TypeScript",
|
||||
"part-10": "React Native",
|
||||
"part-11": "CI/CD",
|
||||
"part-12": "Containers",
|
||||
"part-13": "Using Relational Databases"
|
||||
},
|
||||
"modules": {
|
||||
"basic-html": "Basic HTML"
|
||||
},
|
||||
"module-intros": {
|
||||
"basic-html": {
|
||||
"title": "Basic HTML",
|
||||
"intro": [
|
||||
"Learn how to build simple webpages using HTML tags to add text, images, and links."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"daily-coding-challenge": {
|
||||
"title": "Daily Coding Challenge",
|
||||
"blocks": {
|
||||
|
||||
@@ -50,6 +50,7 @@ const iconMap = {
|
||||
[SuperBlocks.PythonForEverybody]: PythonIcon,
|
||||
[SuperBlocks.BasicHtml]: Code,
|
||||
[SuperBlocks.SemanticHtml]: Code,
|
||||
[SuperBlocks.FullStackOpen]: Code,
|
||||
[SuperBlocks.DevPlayground]: Code
|
||||
};
|
||||
|
||||
|
||||
9
client/src/pages/learn/full-stack-open/index.md
Normal file
9
client/src/pages/learn/full-stack-open/index.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Full Stack Open
|
||||
superBlock: full-stack-open
|
||||
certification: full-stack-open
|
||||
---
|
||||
|
||||
## Full Stack Open
|
||||
|
||||
The [Full Stack Open](https://fullstackopen.com/en/) curriculum, created by the University of Helsinki, covers modern web development with JavaScript. The curriculum focuses on building single page applications with ReactJS that use REST APIs built with Node.js. It also covers GraphQL APIs, TypeScript, and testing.
|
||||
@@ -7,7 +7,9 @@ import { SuperBlocks } from '../../../../../shared/config/curriculum';
|
||||
import DropDown from '../../../assets/icons/dropdown';
|
||||
// TODO: source the superblock structure via a GQL query, rather than directly
|
||||
// from the curriculum
|
||||
import superBlockStructure from '../../../../../curriculum/structure/superblocks/full-stack-developer.json';
|
||||
import fullStackCert from '../../../../../curriculum/structure/superblocks/full-stack-developer.json';
|
||||
import fullStackOpen from '../../../../../curriculum/structure/superblocks/full-stack-open.json';
|
||||
|
||||
import { ChapterIcon } from '../../../assets/chapter-icon';
|
||||
import { BlockLayouts, BlockTypes } from '../../../../../shared/config/blocks';
|
||||
import { FsdChapters } from '../../../../../shared/config/chapters';
|
||||
@@ -58,44 +60,6 @@ interface SuperBlockAccordionProps {
|
||||
completedChallengeIds: string[];
|
||||
}
|
||||
|
||||
const modules = superBlockStructure.chapters.flatMap<Module>(
|
||||
({ modules }) => modules
|
||||
);
|
||||
const chapters = superBlockStructure.chapters;
|
||||
|
||||
const isLinkModule = (name: string) => {
|
||||
const module = modules.find(module => module.dashedName === name);
|
||||
|
||||
return module?.moduleType === 'review';
|
||||
};
|
||||
|
||||
const getBlockToChapterMap = () => {
|
||||
const blockToChapterMap = new Map<string, string>();
|
||||
chapters.forEach(chapter => {
|
||||
chapter.modules.forEach(module => {
|
||||
module.blocks.forEach(block => {
|
||||
blockToChapterMap.set(block, chapter.dashedName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return blockToChapterMap;
|
||||
};
|
||||
|
||||
const getBlockToModuleMap = () => {
|
||||
const blockToModuleMap = new Map<string, string>();
|
||||
modules.forEach(module => {
|
||||
module.blocks.forEach(block => {
|
||||
blockToModuleMap.set(block, module.dashedName);
|
||||
});
|
||||
});
|
||||
|
||||
return blockToModuleMap;
|
||||
};
|
||||
|
||||
const blockToChapterMap = getBlockToChapterMap();
|
||||
const blockToModuleMap = getBlockToModuleMap();
|
||||
|
||||
const Chapter = ({
|
||||
dashedName,
|
||||
children,
|
||||
@@ -210,8 +174,59 @@ export const SuperBlockAccordion = ({
|
||||
chosenBlock,
|
||||
completedChallengeIds
|
||||
}: SuperBlockAccordionProps) => {
|
||||
function getSuperblockStructure(superBlock: SuperBlocks) {
|
||||
switch (superBlock) {
|
||||
case SuperBlocks.FullStackOpen:
|
||||
return fullStackOpen;
|
||||
case SuperBlocks.FullStackDeveloper:
|
||||
return fullStackCert;
|
||||
default:
|
||||
throw new Error("The SuperBlock structure hasn't been imported.");
|
||||
}
|
||||
}
|
||||
|
||||
const superBlockStructure = getSuperblockStructure(superBlock);
|
||||
|
||||
const modules = superBlockStructure.chapters.flatMap<Module>(
|
||||
({ modules }) => modules
|
||||
);
|
||||
|
||||
const isLinkModule = (name: string) => {
|
||||
const module = modules.find(module => module.dashedName === name);
|
||||
|
||||
return module?.moduleType === 'review';
|
||||
};
|
||||
|
||||
const getBlockToChapterMap = () => {
|
||||
const blockToChapterMap = new Map<string, string>();
|
||||
superBlockStructure.chapters.forEach(chapter => {
|
||||
chapter.modules.forEach(module => {
|
||||
module.blocks.forEach(block => {
|
||||
blockToChapterMap.set(block, chapter.dashedName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return blockToChapterMap;
|
||||
};
|
||||
|
||||
const getBlockToModuleMap = () => {
|
||||
const blockToModuleMap = new Map<string, string>();
|
||||
modules.forEach(module => {
|
||||
module.blocks.forEach(block => {
|
||||
blockToModuleMap.set(block, module.dashedName);
|
||||
});
|
||||
});
|
||||
|
||||
return blockToModuleMap;
|
||||
};
|
||||
|
||||
const blockToChapterMap = getBlockToChapterMap();
|
||||
const blockToModuleMap = getBlockToModuleMap();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { allChapters } = useMemo(() => {
|
||||
const chapters = superBlockStructure.chapters;
|
||||
const populateBlocks = (blocks: string[]) =>
|
||||
blocks.map(block => {
|
||||
const blockChallenges = challenges.filter(
|
||||
@@ -237,7 +252,7 @@ export const SuperBlockAccordion = ({
|
||||
}));
|
||||
|
||||
return { allChapters };
|
||||
}, [challenges]);
|
||||
}, [challenges, superBlockStructure.chapters]);
|
||||
|
||||
// Expand the outer layers in order to reveal the chosen block.
|
||||
const expandedChapter = blockToChapterMap.get(chosenBlock);
|
||||
|
||||
@@ -187,7 +187,8 @@ const superBlockNames = {
|
||||
'basic-html': 'basic-html',
|
||||
'semantic-html': 'semantic-html',
|
||||
'a1-professional-chinese': 'a1-professional-chinese',
|
||||
'dev-playground': 'dev-playground'
|
||||
'dev-playground': 'dev-playground',
|
||||
'full-stack-open': 'full-stack-open'
|
||||
};
|
||||
|
||||
const superBlockToFilename = Object.entries(superBlockNames).reduce(
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"basic-html",
|
||||
"semantic-html",
|
||||
"a1-professional-chinese",
|
||||
"dev-playground"
|
||||
"dev-playground",
|
||||
"full-stack-open"
|
||||
],
|
||||
"certifications": [
|
||||
"a2-english-for-developers",
|
||||
|
||||
154
curriculum/structure/superblocks/full-stack-open.json
Normal file
154
curriculum/structure/superblocks/full-stack-open.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"chapters": [
|
||||
{
|
||||
"dashedName": "part-0",
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-1",
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-2",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-3",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-4",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-5",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-6",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-7",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-8",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-9",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-10",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-11",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-12",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"dashedName": "part-13",
|
||||
"comingSoon": true,
|
||||
"modules": [
|
||||
{
|
||||
"dashedName": "basic-html",
|
||||
"comingSoon": true,
|
||||
"blocks": ["cat-blog-page"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -286,7 +286,8 @@ export const superBlockToCertMap: {
|
||||
[SuperBlocks.RosettaCode]: null,
|
||||
[SuperBlocks.BasicHtml]: null,
|
||||
[SuperBlocks.SemanticHtml]: null,
|
||||
[SuperBlocks.DevPlayground]: null
|
||||
[SuperBlocks.DevPlayground]: null,
|
||||
[SuperBlocks.FullStackOpen]: null
|
||||
};
|
||||
|
||||
export type CertSlug = (typeof Certification)[keyof typeof Certification];
|
||||
|
||||
@@ -32,7 +32,8 @@ export enum SuperBlocks {
|
||||
PythonForEverybody = 'python-for-everybody',
|
||||
BasicHtml = 'basic-html',
|
||||
SemanticHtml = 'semantic-html',
|
||||
DevPlayground = 'dev-playground'
|
||||
DevPlayground = 'dev-playground',
|
||||
FullStackOpen = 'full-stack-open'
|
||||
}
|
||||
|
||||
export const languageSuperBlocks = [
|
||||
@@ -115,6 +116,7 @@ export const superBlockStages: StageMap = {
|
||||
],
|
||||
[SuperBlockStage.Next]: [],
|
||||
[SuperBlockStage.Upcoming]: [
|
||||
SuperBlocks.FullStackOpen,
|
||||
SuperBlocks.A2Spanish,
|
||||
SuperBlocks.A2Chinese,
|
||||
SuperBlocks.A1Chinese,
|
||||
@@ -325,7 +327,10 @@ export const notAuditedSuperBlocks: NotAuditedSuperBlocks = {
|
||||
|
||||
Object.freeze(notAuditedSuperBlocks);
|
||||
|
||||
export const chapterBasedSuperBlocks = [SuperBlocks.FullStackDeveloper];
|
||||
export const chapterBasedSuperBlocks = [
|
||||
SuperBlocks.FullStackDeveloper,
|
||||
SuperBlocks.FullStackOpen
|
||||
];
|
||||
Object.freeze(chapterBasedSuperBlocks);
|
||||
|
||||
type Config = {
|
||||
|
||||
Reference in New Issue
Block a user