Skip to content

Commit 954117c

Browse files
authored
chore(tools): display chapters in challenge editor (freeCodeCamp#62050)
1 parent eb2c74b commit 954117c

File tree

21 files changed

+622
-58
lines changed

21 files changed

+622
-58
lines changed

tools/challenge-editor/api/configs/paths.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,14 @@ export const CHALLENGE_DIR = join(
3030
'english',
3131
'blocks'
3232
);
33+
34+
export const ENGLISH_LANG_DIR = join(
35+
process.cwd(),
36+
'..',
37+
'..',
38+
'..',
39+
'client',
40+
'i18n',
41+
'locales',
42+
'english'
43+
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
interface SuperBlock {
2+
title: string;
3+
intro: string[];
4+
blocks: string[];
5+
modules?: string[];
6+
chapters?: string[];
7+
}
8+
9+
export interface Intro {
10+
[key: string]: SuperBlock;
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Request, Response } from 'express';
2+
import { getBlocks } from '../utils/get-full-stack-blocks';
3+
4+
export const moduleBlockRoute = async (
5+
req: Request,
6+
res: Response
7+
): Promise<void> => {
8+
const { superblock, chapter, module } = req.params;
9+
10+
const steps = await getBlocks(superblock, chapter, module);
11+
12+
res.json(steps);
13+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Request, Response } from 'express';
2+
import { getModules } from '../utils/get-full-stack-blocks';
3+
4+
export const moduleRoute = async (
5+
req: Request,
6+
res: Response
7+
): Promise<void> => {
8+
const { superblock, chapter } = req.params;
9+
10+
const steps = await getModules(superblock, chapter);
11+
12+
res.json(steps);
13+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Request, Response } from 'express';
2+
import { getStepContent } from '../utils/get-step-contents';
3+
4+
export const stepRoute = async (req: Request, res: Response): Promise<void> => {
5+
const { superblock, block, step } = req.params;
6+
7+
const stepContents = await getStepContent(superblock, block, step);
8+
res.json(stepContents);
9+
};

tools/challenge-editor/api/server.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { saveRoute } from './routes/save-route';
99
import { stepRoute } from './routes/step-route';
1010
import { superblockRoute } from './routes/super-block-route';
1111
import { toolsRoute } from './routes/tools-route';
12+
import { moduleRoute } from './routes/module-route';
13+
import { moduleBlockRoute } from './routes/module-block-route';
1214

1315
const app = express();
1416

@@ -29,6 +31,14 @@ app.post('/:superblock/:block/:step', (req, res, next) => {
2931
saveRoute(req, res).catch(next);
3032
});
3133

34+
app.get(`/:superblock/chapters/:chapter`, (req, res, next) => {
35+
moduleRoute(req, res).catch(next);
36+
});
37+
38+
app.get(`/:superblock/chapters/:chapter/modules/:module`, (req, res, next) => {
39+
moduleBlockRoute(req, res).catch(next);
40+
});
41+
3242
app.get('/:superblock/:block/:step', (req, res, next) => {
3343
stepRoute(req, res).catch(next);
3444
});
Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,69 @@
11
import { readFile } from 'fs/promises';
22
import { join } from 'path';
3-
import { SUPERBLOCK_META_DIR, CHALLENGE_DIR } from '../configs/paths';
3+
import {
4+
SUPERBLOCK_META_DIR,
5+
BLOCK_META_DIR,
6+
ENGLISH_LANG_DIR
7+
} from '../configs/paths';
48

59
import { SuperBlockMeta } from '../interfaces/superblock-meta';
10+
import { PartialMeta } from '../interfaces/partial-meta';
11+
12+
import { Intro } from '../interfaces/intro';
613

714
type Block = {
815
name: string;
916
path: string;
1017
};
1118

12-
export const getBlocks = async (sup: string): Promise<Block[]> => {
19+
type BlockLocation = {
20+
blocks: Block[];
21+
currentSuperBlock: string;
22+
};
23+
24+
const chapterBasedSuperBlocks = ['full-stack-developer'];
25+
26+
export const getBlocks = async (sup: string): Promise<BlockLocation> => {
1327
const superBlockDataPath = join(SUPERBLOCK_META_DIR, sup + '.json');
1428
const superBlockMetaFile = await readFile(superBlockDataPath, {
1529
encoding: 'utf8'
1630
});
1731
const superBlockMeta = JSON.parse(superBlockMetaFile) as SuperBlockMeta;
32+
33+
const introDataPath = join(ENGLISH_LANG_DIR, 'intro.json');
34+
const introFile = await readFile(introDataPath, {
35+
encoding: 'utf8'
36+
});
37+
const introData = JSON.parse(introFile) as Intro;
38+
1839
let blocks: { name: string; path: string }[] = [];
1940

20-
if (sup === 'full-stack-developer') {
21-
const moduleBlockData = await Promise.all(
22-
superBlockMeta.chapters!.flatMap(async chapter => {
23-
return await Promise.all(
24-
chapter.modules.flatMap(async module => {
25-
return module.blocks!.flatMap(block => {
26-
const filePath = join(CHALLENGE_DIR, block);
27-
return {
28-
name: block,
29-
path: filePath
30-
};
31-
});
32-
})
33-
);
34-
})
35-
);
36-
blocks = moduleBlockData.flat().flat();
41+
if (chapterBasedSuperBlocks.includes(sup)) {
42+
blocks = superBlockMeta.chapters!.map(chapter => {
43+
const chapters = Object.entries(introData[sup]['chapters']!);
44+
const chapterTrueName = chapters.filter(
45+
x => x[0] === chapter.dashedName
46+
)[0][1];
47+
return {
48+
name: chapterTrueName,
49+
path: 'chapters/' + chapter.dashedName
50+
};
51+
});
3752
} else {
3853
blocks = await Promise.all(
3954
superBlockMeta.blocks!.map(async block => {
40-
const filePath = join(CHALLENGE_DIR, block);
55+
const blockStructurePath = join(BLOCK_META_DIR, block + '.json');
56+
const blockMetaFile = await readFile(blockStructurePath, {
57+
encoding: 'utf8'
58+
});
59+
const blockMeta = JSON.parse(blockMetaFile) as PartialMeta;
4160
return {
42-
name: block,
43-
path: filePath
61+
name: blockMeta.name,
62+
path: block
4463
};
4564
})
4665
);
4766
}
4867

49-
return blocks;
68+
return { blocks: blocks, currentSuperBlock: introData[sup].title };
5069
};
Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,133 @@
11
import { readFile } from 'fs/promises';
22
import { join } from 'path';
3-
import { SUPERBLOCK_META_DIR, CHALLENGE_DIR } from '../configs/paths';
3+
import {
4+
SUPERBLOCK_META_DIR,
5+
BLOCK_META_DIR,
6+
ENGLISH_LANG_DIR
7+
} from '../configs/paths';
48
import { SuperBlockMeta } from '../interfaces/superblock-meta';
9+
import { PartialMeta } from '../interfaces/partial-meta';
10+
import { Intro } from '../interfaces/intro';
511

612
type Block = {
713
name: string;
814
path: string;
915
};
1016

11-
export const getModules = async (chap: string): Promise<string[]> => {
12-
const superBlockDataPath = join(
13-
SUPERBLOCK_META_DIR,
14-
'full-stack-developer' + '.json'
15-
);
17+
type Module = {
18+
name: string;
19+
path: string;
20+
};
21+
22+
type BlockLocation = {
23+
blocks: Block[];
24+
currentModule: string;
25+
currentChapter: string;
26+
};
27+
28+
type ModuleLocation = {
29+
modules: Module[];
30+
currentChapter: string;
31+
currentSuperBlock: string;
32+
};
33+
34+
export const getModules = async (
35+
superBlock: string,
36+
chap: string
37+
): Promise<ModuleLocation> => {
38+
const superBlockDataPath = join(SUPERBLOCK_META_DIR, superBlock + '.json');
1639

1740
const superBlockMetaFile = await readFile(superBlockDataPath, {
1841
encoding: 'utf8'
1942
});
2043
const superBlockMeta = JSON.parse(superBlockMetaFile) as SuperBlockMeta;
2144

45+
const introDataPath = join(ENGLISH_LANG_DIR, 'intro.json');
46+
const introFile = await readFile(introDataPath, {
47+
encoding: 'utf8'
48+
});
49+
const introData = JSON.parse(introFile) as Intro;
50+
51+
const chapters = Object.entries(introData[superBlock]['chapters']!);
52+
2253
const chapter = superBlockMeta.chapters!.filter(
2354
x => x.dashedName === chap
2455
)[0];
2556

26-
return await Promise.all(
27-
chapter.modules!.map(async module => module.dashedName)
57+
const chapterTrueName = chapters.filter(x => x[0] === chap)[0][1];
58+
59+
let modules: Module[] = [];
60+
61+
modules = await Promise.all(
62+
chapter.modules!.map(module => {
63+
const modules = Object.entries(introData[superBlock]['modules']!);
64+
const moduleTrueName = modules.filter(
65+
x => x[0] === module.dashedName
66+
)[0][1];
67+
return { name: moduleTrueName, path: 'modules/' + module.dashedName };
68+
})
2869
);
70+
71+
return {
72+
modules: modules,
73+
currentChapter: chapterTrueName,
74+
currentSuperBlock: introData[superBlock].title
75+
};
2976
};
3077

31-
export const getBlocks = async (module: string): Promise<Block[]> => {
32-
const superBlockDataPath = join(
33-
SUPERBLOCK_META_DIR,
34-
'full-stack-developer' + '.json'
35-
);
78+
export const getBlocks = async (
79+
superBlock: string,
80+
chapterName: string,
81+
moduleName: string
82+
): Promise<BlockLocation> => {
83+
const superBlockDataPath = join(SUPERBLOCK_META_DIR, superBlock + '.json');
3684

3785
const superBlockMetaFile = await readFile(superBlockDataPath, {
3886
encoding: 'utf8'
3987
});
4088
const superBlockMeta = JSON.parse(superBlockMetaFile) as SuperBlockMeta;
4189

42-
const foundModule = superBlockMeta
43-
.chapters!.flatMap(x => x.modules)
44-
.filter(x => x.dashedName === module)[0];
90+
const introDataPath = join(ENGLISH_LANG_DIR, 'intro.json');
91+
const introFile = await readFile(introDataPath, {
92+
encoding: 'utf8'
93+
});
94+
const introData = JSON.parse(introFile) as Intro;
95+
96+
const modules = Object.entries(introData[superBlock]['modules']!);
97+
98+
const moduleTrueName = modules.filter(x => x[0] === moduleName)[0][1];
99+
100+
const chapters = Object.entries(introData[superBlock]['chapters']!);
101+
102+
const chapterTrueName = chapters.filter(x => x[0] === chapterName)[0][1];
103+
104+
const foundChapter = superBlockMeta.chapters?.filter(
105+
chapter => chapter.dashedName === chapterName
106+
)[0];
107+
108+
const foundModule = foundChapter?.modules.filter(
109+
module => module.dashedName === moduleName
110+
)[0];
45111

46112
let blocks: { name: string; path: string }[] = [];
47113

48114
blocks = await Promise.all(
49-
foundModule.blocks!.map(async block => {
50-
const filePath = join(CHALLENGE_DIR, block);
115+
foundModule!.blocks!.map(async block => {
116+
const blockStructurePath = join(BLOCK_META_DIR, block + '.json');
117+
const blockMetaFile = await readFile(blockStructurePath, {
118+
encoding: 'utf8'
119+
});
120+
const blockMeta = JSON.parse(blockMetaFile) as PartialMeta;
51121
return {
52-
name: block,
53-
path: filePath
122+
name: blockMeta.name,
123+
path: block
54124
};
55125
})
56126
);
57-
return blocks;
127+
128+
return {
129+
blocks: blocks,
130+
currentModule: moduleTrueName,
131+
currentChapter: chapterTrueName
132+
};
58133
};

tools/challenge-editor/api/utils/get-steps.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { join } from 'path';
44
import matter from 'gray-matter';
55

66
import { PartialMeta } from '../interfaces/partial-meta';
7-
import { BLOCK_META_DIR, CHALLENGE_DIR } from '../configs/paths';
7+
import {
8+
BLOCK_META_DIR,
9+
CHALLENGE_DIR,
10+
ENGLISH_LANG_DIR
11+
} from '../configs/paths';
12+
import { Intro } from '../interfaces/intro';
813

914
const getFileOrder = (id: string, meta: PartialMeta) => {
1015
return meta.challengeOrder.findIndex(({ id: f }) => f === id);
@@ -16,7 +21,16 @@ type Step = {
1621
path: string;
1722
};
1823

19-
export const getSteps = async (sup: string, block: string): Promise<Step[]> => {
24+
type StepLocation = {
25+
steps: Step[];
26+
currentBlock: string;
27+
currentSuperBlock: string;
28+
};
29+
30+
export const getSteps = async (
31+
sup: string,
32+
block: string
33+
): Promise<StepLocation> => {
2034
//const superMetaPath = join(SUPERBLOCK_META_DIR, sup + ".json");
2135

2236
//const superMetaData = JSON.parse(
@@ -27,6 +41,13 @@ export const getSteps = async (sup: string, block: string): Promise<Step[]> => {
2741

2842
const blockFolderPath = join(BLOCK_META_DIR, block + '.json');
2943

44+
const introDataPath = join(ENGLISH_LANG_DIR, 'intro.json');
45+
const introFile = await readFile(introDataPath, {
46+
encoding: 'utf8'
47+
});
48+
49+
const introData = JSON.parse(introFile) as Intro;
50+
3051
const blockMetaData = JSON.parse(
3152
await readFile(blockFolderPath, { encoding: 'utf8' })
3253
) as PartialMeta;
@@ -47,8 +68,14 @@ export const getSteps = async (sup: string, block: string): Promise<Step[]> => {
4768
})
4869
);
4970

50-
return stepData.sort(
71+
const steps = stepData.sort(
5172
(a, b) =>
5273
getFileOrder(a.id, blockMetaData) - getFileOrder(b.id, blockMetaData)
5374
);
75+
76+
return {
77+
steps: steps,
78+
currentBlock: blockMetaData.name,
79+
currentSuperBlock: introData[sup].title
80+
};
5481
};

0 commit comments

Comments
 (0)