epam-ai-conductor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/bin/workshop.js +6 -0
- package/dist/auth-check.d.ts +1 -0
- package/dist/auth-check.js +45 -0
- package/dist/auth-check.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +156 -0
- package/dist/cli.js.map +1 -0
- package/dist/content/content/module-1/task/module-1_home-task_components.md +257 -0
- package/dist/content/content/module-1/task/module-1_home-task_good_practices.md +69 -0
- package/dist/content/content/module-1/task/module-1_home-task_task.md +253 -0
- package/dist/content/content/module-1/tests/Button.test.tsx +22 -0
- package/dist/content/content/module-1/tests/CourseCard.test.tsx +64 -0
- package/dist/content/content/module-1/tests/CourseInfo.test.tsx +73 -0
- package/dist/content/content/module-1/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-1/tests/Header.test.tsx +23 -0
- package/dist/content/content/module-1/tests/Input.test.tsx +36 -0
- package/dist/content/content/module-1/tests/helpers.test.ts +13 -0
- package/dist/content/content/module-1/theory/module-1_elements-render.md +66 -0
- package/dist/content/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
- package/dist/content/content/module-1/theory/module-1_hooks_useState.md +131 -0
- package/dist/content/content/module-1/theory/module-1_react.md +64 -0
- package/dist/content/content/module-1/theory/module-1_spa.md +74 -0
- package/dist/content/content/module-2/task/module-2_home-task_components.md +313 -0
- package/dist/content/content/module-2/task/module-2_home-task_good_practices.md +35 -0
- package/dist/content/content/module-2/task/module-2_home-task_task.md +210 -0
- package/dist/content/content/module-2/tests/App.test.tsx +54 -0
- package/dist/content/content/module-2/tests/Login.test.tsx +73 -0
- package/dist/content/content/module-2/tests/Registration.test.tsx +70 -0
- package/dist/content/content/module-2/tests/SearchBar.test.tsx +39 -0
- package/dist/content/content/module-2/theory/module-2_custom-hooks.md +201 -0
- package/dist/content/content/module-2/theory/module-2_hooks.md +117 -0
- package/dist/content/content/module-2/theory/module-2_react-router.md +328 -0
- package/dist/content/content/module-3/task/module-3_home-task_components.md +94 -0
- package/dist/content/content/module-3/task/module-3_home-task_good_practices.md +26 -0
- package/dist/content/content/module-3/task/module-3_home-task_task.md +170 -0
- package/dist/content/content/module-3/tests/App.test.tsx +54 -0
- package/dist/content/content/module-3/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-3/tests/CreateAuthor.test.tsx +44 -0
- package/dist/content/content/module-3/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/content/module-3/theory/module-3_redux-hooks.md +194 -0
- package/dist/content/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
- package/dist/content/content/module-4/task/module-4_home-task_components.md +187 -0
- package/dist/content/content/module-4/task/module-4_home-task_task.md +139 -0
- package/dist/content/content/module-4/tests/App.test.tsx +54 -0
- package/dist/content/content/module-4/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-4/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/content/module-4/tests/Login.test.tsx +73 -0
- package/dist/content/content/module-4/theory/module-4_async-redux.md +99 -0
- package/dist/content/content/module-4/theory/module-4_private-routes.md +55 -0
- package/dist/content/content/module-5/task/module-5_home-task_instruction.md +68 -0
- package/dist/content/content/module-5/task/module-5_home-task_task.md +154 -0
- package/dist/content/content/module-5/tests/App.test.tsx +54 -0
- package/dist/content/content/module-5/tests/CourseCard.test.tsx +64 -0
- package/dist/content/content/module-5/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-5/tests/Header.test.tsx +23 -0
- package/dist/content/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
- package/dist/content/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
- package/dist/content/module-1/task/module-1_home-task_components.md +257 -0
- package/dist/content/module-1/task/module-1_home-task_good_practices.md +69 -0
- package/dist/content/module-1/task/module-1_home-task_task.md +253 -0
- package/dist/content/module-1/tests/Button.test.tsx +22 -0
- package/dist/content/module-1/tests/CourseCard.test.tsx +64 -0
- package/dist/content/module-1/tests/CourseInfo.test.tsx +73 -0
- package/dist/content/module-1/tests/Courses.test.tsx +87 -0
- package/dist/content/module-1/tests/Header.test.tsx +23 -0
- package/dist/content/module-1/tests/Input.test.tsx +36 -0
- package/dist/content/module-1/tests/helpers.test.ts +13 -0
- package/dist/content/module-1/theory/module-1_elements-render.md +66 -0
- package/dist/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
- package/dist/content/module-1/theory/module-1_hooks_useState.md +131 -0
- package/dist/content/module-1/theory/module-1_react.md +64 -0
- package/dist/content/module-1/theory/module-1_spa.md +74 -0
- package/dist/content/module-2/task/module-2_home-task_components.md +313 -0
- package/dist/content/module-2/task/module-2_home-task_good_practices.md +35 -0
- package/dist/content/module-2/task/module-2_home-task_task.md +210 -0
- package/dist/content/module-2/tests/App.test.tsx +54 -0
- package/dist/content/module-2/tests/Login.test.tsx +73 -0
- package/dist/content/module-2/tests/Registration.test.tsx +70 -0
- package/dist/content/module-2/tests/SearchBar.test.tsx +39 -0
- package/dist/content/module-2/theory/module-2_custom-hooks.md +201 -0
- package/dist/content/module-2/theory/module-2_hooks.md +117 -0
- package/dist/content/module-2/theory/module-2_react-router.md +328 -0
- package/dist/content/module-3/task/module-3_home-task_components.md +94 -0
- package/dist/content/module-3/task/module-3_home-task_good_practices.md +26 -0
- package/dist/content/module-3/task/module-3_home-task_task.md +170 -0
- package/dist/content/module-3/tests/App.test.tsx +54 -0
- package/dist/content/module-3/tests/Courses.test.tsx +87 -0
- package/dist/content/module-3/tests/CreateAuthor.test.tsx +44 -0
- package/dist/content/module-3/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/module-3/theory/module-3_redux-hooks.md +194 -0
- package/dist/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
- package/dist/content/module-4/task/module-4_home-task_components.md +187 -0
- package/dist/content/module-4/task/module-4_home-task_task.md +139 -0
- package/dist/content/module-4/tests/App.test.tsx +54 -0
- package/dist/content/module-4/tests/Courses.test.tsx +87 -0
- package/dist/content/module-4/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/module-4/tests/Login.test.tsx +73 -0
- package/dist/content/module-4/theory/module-4_async-redux.md +99 -0
- package/dist/content/module-4/theory/module-4_private-routes.md +55 -0
- package/dist/content/module-5/task/module-5_home-task_instruction.md +68 -0
- package/dist/content/module-5/task/module-5_home-task_task.md +154 -0
- package/dist/content/module-5/tests/App.test.tsx +54 -0
- package/dist/content/module-5/tests/CourseCard.test.tsx +64 -0
- package/dist/content/module-5/tests/Courses.test.tsx +87 -0
- package/dist/content/module-5/tests/Header.test.tsx +23 -0
- package/dist/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
- package/dist/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
- package/dist/content-loader.d.ts +7 -0
- package/dist/content-loader.js +26 -0
- package/dist/content-loader.js.map +1 -0
- package/dist/context-builder.d.ts +2 -0
- package/dist/context-builder.js +116 -0
- package/dist/context-builder.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# react-workshop-cli
|
|
2
|
+
|
|
3
|
+
Socratic mentor CLI for the React Fundamentals course, powered by CodeMie + Claude.
|
|
4
|
+
|
|
5
|
+
The agent knows the full course content and test expectations but guides students with questions, not answers.
|
|
6
|
+
|
|
7
|
+
## Student setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. Clone the template
|
|
11
|
+
git clone <template-repo-url> my-react-app
|
|
12
|
+
cd my-react-app
|
|
13
|
+
|
|
14
|
+
# 2. Install the workshop CLI
|
|
15
|
+
npm install -g react-workshop-cli # once codemie installs claude
|
|
16
|
+
|
|
17
|
+
# 3. Start a module session
|
|
18
|
+
react-workshop start --module 1
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
On first run it will walk you through CodeMie setup (EPAM SSO login).
|
|
22
|
+
|
|
23
|
+
### Slash commands during the session
|
|
24
|
+
|
|
25
|
+
| Command | What it does |
|
|
26
|
+
|----------|-------------|
|
|
27
|
+
| `/hint` | One targeted hint — a question, not code |
|
|
28
|
+
| `/check` | Reviews your src/ against task criteria |
|
|
29
|
+
| `/step` | Suggests the single most logical next step |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Course coordinator — updating content
|
|
34
|
+
|
|
35
|
+
When coursebook or test files change, re-sync and rebuild:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Repos must be siblings of this directory:
|
|
39
|
+
# ../react-fundamentals-coursebook
|
|
40
|
+
# ../react-fundamentals-app-tests
|
|
41
|
+
|
|
42
|
+
npm run sync-content # copies from sibling repos into src/content/
|
|
43
|
+
npm run build # compiles TS + copies content into dist/
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then publish the new version of the package.
|
package/bin/workshop.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureAuth(): Promise<void>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { spawnSync } from 'child_process';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
const GLOBAL_CONFIG = join(homedir(), '.codemie', 'codemie-cli.config.json');
|
|
8
|
+
export async function ensureAuth() {
|
|
9
|
+
if (existsSync(GLOBAL_CONFIG))
|
|
10
|
+
return;
|
|
11
|
+
console.log();
|
|
12
|
+
console.log(chalk.bold.cyan('╔══════════════════════════════════════════════════════╗'));
|
|
13
|
+
console.log(chalk.bold.cyan('║ React Workshop — First Time Setup ║'));
|
|
14
|
+
console.log(chalk.bold.cyan('╚══════════════════════════════════════════════════════╝'));
|
|
15
|
+
console.log();
|
|
16
|
+
console.log(chalk.white('To use the workshop mentor you need a CodeMie account.'));
|
|
17
|
+
console.log();
|
|
18
|
+
console.log(chalk.bold('Steps:'));
|
|
19
|
+
console.log(chalk.white(' 1. Go to ') + chalk.cyan('https://codemie.ai'));
|
|
20
|
+
console.log(chalk.white(' 2. Click ') + chalk.bold('"Sign in with EPAM SSO"'));
|
|
21
|
+
console.log(chalk.white(' 3. Once logged in, come back here'));
|
|
22
|
+
console.log();
|
|
23
|
+
const { ready } = await inquirer.prompt([{
|
|
24
|
+
type: 'confirm',
|
|
25
|
+
name: 'ready',
|
|
26
|
+
message: 'Have you signed into CodeMie? Press Enter to run setup...',
|
|
27
|
+
default: true,
|
|
28
|
+
}]);
|
|
29
|
+
if (!ready) {
|
|
30
|
+
console.log(chalk.yellow('\nSetup cancelled. Run `react-workshop start` again when ready.'));
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(chalk.cyan('Running codemie setup...'));
|
|
35
|
+
console.log();
|
|
36
|
+
const result = spawnSync('codemie', ['setup'], { stdio: 'inherit' });
|
|
37
|
+
if (result.status !== 0 || !existsSync(GLOBAL_CONFIG)) {
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(chalk.red('✗ Setup did not complete. Please run `codemie setup` manually and try again.'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
console.log();
|
|
43
|
+
console.log(chalk.green('✓ CodeMie configured. Starting your mentor session...\n'));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=auth-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-check.js","sourceRoot":"","sources":["../src/auth-check.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { spawnSync } from 'child_process';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
import inquirer from 'inquirer';
|
|
10
|
+
import * as pty from 'node-pty';
|
|
11
|
+
import { ensureAuth } from './auth-check.js';
|
|
12
|
+
import { buildContext, MODULE_TITLES } from './context-builder.js';
|
|
13
|
+
import { TOTAL_MODULES } from './content-loader.js';
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
function readVersion() {
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
18
|
+
return pkg.version;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return '0.1.0';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function resolveCodemieClaudePath() {
|
|
25
|
+
const result = spawnSync('which', ['codemie-claude'], { encoding: 'utf-8' });
|
|
26
|
+
if (result.status !== 0)
|
|
27
|
+
return null;
|
|
28
|
+
return result.stdout.trim();
|
|
29
|
+
}
|
|
30
|
+
function launchMentor(moduleNum, targetDir, execPath) {
|
|
31
|
+
const env = { ...process.env };
|
|
32
|
+
const startMessage = `Hi! I'm starting Module ${moduleNum}. Please introduce the task and help me get going.`;
|
|
33
|
+
const cols = process.stdout.columns || 220;
|
|
34
|
+
const rows = process.stdout.rows || 50;
|
|
35
|
+
const child = pty.spawn(process.execPath, [execPath, '--model', 'claude-sonnet-4-6'], {
|
|
36
|
+
name: 'xterm-color',
|
|
37
|
+
cols,
|
|
38
|
+
rows,
|
|
39
|
+
cwd: targetDir,
|
|
40
|
+
env,
|
|
41
|
+
});
|
|
42
|
+
const BOX = 56; // inner width
|
|
43
|
+
const pad = (s) => s.length >= BOX ? s.slice(0, BOX - 1) + ' ' : s.padEnd(BOX);
|
|
44
|
+
const rawTitle = MODULE_TITLES[moduleNum] ?? `Module ${moduleNum}`;
|
|
45
|
+
const titleLine = ` Module ${moduleNum}: ${rawTitle}`;
|
|
46
|
+
const banner = '\r\n' +
|
|
47
|
+
chalk.bold.cyan(` ╔${'═'.repeat(BOX)}╗`) + '\r\n' +
|
|
48
|
+
chalk.bold.cyan(' ║') + chalk.bold.white(pad(' ⚛ React Course Workshop')) + chalk.bold.cyan('║') + '\r\n' +
|
|
49
|
+
chalk.bold.cyan(' ║') + chalk.cyan(pad(titleLine)) + chalk.bold.cyan('║') + '\r\n' +
|
|
50
|
+
chalk.bold.cyan(' ║') + chalk.gray(pad(' /hint · /check · /step')) + chalk.bold.cyan('║') + '\r\n' +
|
|
51
|
+
chalk.bold.cyan(` ╚${'═'.repeat(BOX)}╝`) + '\r\n';
|
|
52
|
+
// Single handler: pipe PTY → stdout, inject banner + start message at first prompt
|
|
53
|
+
let bannerShown = false;
|
|
54
|
+
let injected = false;
|
|
55
|
+
child.onData((data) => {
|
|
56
|
+
// Insert banner synchronously right before the first ❯ prompt chunk
|
|
57
|
+
if (!bannerShown && data.includes('❯')) {
|
|
58
|
+
bannerShown = true;
|
|
59
|
+
process.stdout.write(banner);
|
|
60
|
+
}
|
|
61
|
+
process.stdout.write(data);
|
|
62
|
+
// After banner is shown, inject the start message
|
|
63
|
+
if (bannerShown && !injected) {
|
|
64
|
+
injected = true;
|
|
65
|
+
setTimeout(() => child.write(`${startMessage}\r`), 150);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// Pipe stdin to PTY so student can type normally after injection
|
|
69
|
+
process.stdin.setRawMode(true);
|
|
70
|
+
process.stdin.resume();
|
|
71
|
+
process.stdin.on('data', (chunk) => child.write(chunk.toString()));
|
|
72
|
+
// Resize PTY on terminal resize
|
|
73
|
+
process.stdout.on('resize', () => {
|
|
74
|
+
child.resize(process.stdout.columns || 220, process.stdout.rows || 50);
|
|
75
|
+
});
|
|
76
|
+
child.onExit(({ exitCode }) => {
|
|
77
|
+
process.stdin.setRawMode(false);
|
|
78
|
+
process.stdin.pause();
|
|
79
|
+
process.exit(exitCode ?? 0);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const program = new Command();
|
|
83
|
+
program
|
|
84
|
+
.name('react-workshop')
|
|
85
|
+
.description('React course workshop — Socratic mentor powered by CodeMie')
|
|
86
|
+
.version(readVersion());
|
|
87
|
+
program
|
|
88
|
+
.command('start')
|
|
89
|
+
.description('Start a mentor session for a module')
|
|
90
|
+
.option('-m, --module <number>', 'Module number (1–5)')
|
|
91
|
+
.option('-d, --dir <path>', 'Path to your React project (default: current directory)', process.cwd())
|
|
92
|
+
.action(async (opts) => {
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(chalk.bold.cyan(' React Course Workshop'));
|
|
95
|
+
console.log(chalk.gray(' Powered by CodeMie + Claude\n'));
|
|
96
|
+
// 1. Auth check
|
|
97
|
+
await ensureAuth();
|
|
98
|
+
// 2. Resolve module number
|
|
99
|
+
let moduleNum;
|
|
100
|
+
if (opts.module) {
|
|
101
|
+
moduleNum = parseInt(opts.module, 10);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const { selected } = await inquirer.prompt([{
|
|
105
|
+
type: 'list',
|
|
106
|
+
name: 'selected',
|
|
107
|
+
message: 'Which module would you like to work on?',
|
|
108
|
+
choices: Array.from({ length: TOTAL_MODULES }, (_, i) => ({
|
|
109
|
+
name: `Module ${i + 1}`,
|
|
110
|
+
value: i + 1,
|
|
111
|
+
})),
|
|
112
|
+
default: 0,
|
|
113
|
+
}]);
|
|
114
|
+
moduleNum = selected;
|
|
115
|
+
}
|
|
116
|
+
if (moduleNum < 1 || moduleNum > TOTAL_MODULES) {
|
|
117
|
+
console.error(chalk.red(`\n✗ Invalid module number. Choose between 1 and ${TOTAL_MODULES}.`));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
// 3. Check codemie-claude is available
|
|
121
|
+
const codemieClaudePath = resolveCodemieClaudePath();
|
|
122
|
+
if (!codemieClaudePath) {
|
|
123
|
+
console.log();
|
|
124
|
+
console.log(chalk.red('✗ codemie-claude is not installed.'));
|
|
125
|
+
console.log(chalk.white(' Install it with:'), chalk.cyan('codemie install claude'));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
// 4. Build context into target dir
|
|
129
|
+
const spinner = ora(`Preparing Module ${moduleNum} mentor context...`).start();
|
|
130
|
+
try {
|
|
131
|
+
buildContext(moduleNum, opts.dir);
|
|
132
|
+
spinner.succeed(`Module ${moduleNum} context ready`);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
spinner.fail('Failed to build context');
|
|
136
|
+
console.error(chalk.red(err.message));
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
// 5. Show what was set up
|
|
140
|
+
console.log();
|
|
141
|
+
console.log(chalk.green(' ✓ .claude/CLAUDE.md — mentor persona & rules'));
|
|
142
|
+
console.log(chalk.green(` ✓ .claude/module-theory.md — Module ${moduleNum} theory`));
|
|
143
|
+
console.log(chalk.green(` ✓ .claude/module-task.md — Module ${moduleNum} task & criteria`));
|
|
144
|
+
console.log(chalk.green(` ✓ .claude/module-tests.md — Module ${moduleNum} test expectations`));
|
|
145
|
+
console.log(chalk.green(' ✓ .claude/commands/hint.md — /hint'));
|
|
146
|
+
console.log(chalk.green(' ✓ .claude/commands/check.md — /check'));
|
|
147
|
+
console.log(chalk.green(' ✓ .claude/commands/step.md — /step'));
|
|
148
|
+
console.log();
|
|
149
|
+
console.log(chalk.bold(` Starting Module ${moduleNum} mentor session in:`), chalk.cyan(opts.dir));
|
|
150
|
+
console.log(chalk.gray(' Type /hint, /check, or /step anytime during the session.'));
|
|
151
|
+
console.log();
|
|
152
|
+
// 6. Launch codemie-claude — blocks until student exits
|
|
153
|
+
launchMentor(moduleNum, opts.dir, codemieClaudePath);
|
|
154
|
+
});
|
|
155
|
+
program.parse(process.argv);
|
|
156
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 2
|
|
3
|
+
sidebar_label: "COMPONENTS"
|
|
4
|
+
title: Components description for Tasks 1
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
:::tip
|
|
8
|
+
**You need just follow from one step to another.**
|
|
9
|
+
The description was created in such a way
|
|
10
|
+
that you do not need to navigate through the document at random.
|
|
11
|
+
:::
|
|
12
|
+
|
|
13
|
+
:::info
|
|
14
|
+
You can watch [this (link)](https://www.youtube.com/watch?v=Cla1WwguArA) 7 minutes tutorial video about components creation 😉
|
|
15
|
+
:::
|
|
16
|
+
|
|
17
|
+
## Header
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
[Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2905-67147&t=2XuYekWUy7fL4Ba9-0)
|
|
21
|
+
|
|
22
|
+
### Create `Logo` component.
|
|
23
|
+
|
|
24
|
+
Logo component contains only logo picture (you can use any picture, you like).
|
|
25
|
+
|
|
26
|
+
:::info
|
|
27
|
+
Logo component is just a function component which returns `img` tag.
|
|
28
|
+
:::
|
|
29
|
+
|
|
30
|
+
:::tip
|
|
31
|
+
If you use TypeScript and have this error
|
|
32
|
+

|
|
33
|
+
Try to find solution [here](https://stackoverflow.com/questions/52759220/importing-images-in-typescript-react-cannot-find-module)
|
|
34
|
+
:::
|
|
35
|
+
|
|
36
|
+
### Create `Button` component.
|
|
37
|
+
|
|
38
|
+
Button component is a function component which returns `button` tag.
|
|
39
|
+
|
|
40
|
+
:::tip
|
|
41
|
+
You will use Button component in several places through your app,
|
|
42
|
+
so you should use `props` for this component such as `buttonText` and `onClick`
|
|
43
|
+
(and any props, that you want).
|
|
44
|
+
|
|
45
|
+
If you want to repeat how to work with `props` you can:
|
|
46
|
+
⭐ read theoretical material again [(link)](/docs/module-1/components-and-props);
|
|
47
|
+
⭐ watch this tutorial [(link)](https://www.youtube.com/watch?v=m7OWXtbiXX8&list=RDCMUC80PWRj_ZU8Zu0HSMNVwKWw&index=3).
|
|
48
|
+
:::
|
|
49
|
+
|
|
50
|
+
### Create `Header` component.
|
|
51
|
+
|
|
52
|
+
Header should be in `App` component.
|
|
53
|
+
|
|
54
|
+
`Header` component should include:
|
|
55
|
+
|
|
56
|
+
- `Logo` component;
|
|
57
|
+
|
|
58
|
+
- Logout button (`Button` component **WITHOUT** any functionality for the current task).
|
|
59
|
+
|
|
60
|
+
[Small demo](../components-and-props#how-to-create-a-simple-function-component)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Courses
|
|
65
|
+
|
|
66
|
+
### Create `CourseCard` component.
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
[Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2905-67147&t=GislgRTjkGsyZ4Xy-0)
|
|
70
|
+
|
|
71
|
+
This component contains all info about the course.
|
|
72
|
+
|
|
73
|
+
For the current task you should use `mockedCoursesList` array for courses info.
|
|
74
|
+
|
|
75
|
+
`CourseCard` components should be rendered in `Courses` component (you will create it in the next step).
|
|
76
|
+
|
|
77
|
+
`CourseCard` component should contain the following information:
|
|
78
|
+
|
|
79
|
+
1. Title (Course name);
|
|
80
|
+
|
|
81
|
+
2. Duration (format: hh:mm + 'hours'). In the `mockedCoursesList` duration is specified in minutes;
|
|
82
|
+
|
|
83
|
+
:::info
|
|
84
|
+
If `hh` < 10 (0,1,2,3,4,5,6,7,8,9) => '0' + h:mm + 'hours' (02:20 hours)
|
|
85
|
+
If `mm` < 10 (0,1,2,3,4,5,6,7,8,9) => hh:'0' + m + 'hours' (10:06 hours)
|
|
86
|
+
If `hh` = 1 => hh:mm + 'hour' (01:30 hour)
|
|
87
|
+
:::
|
|
88
|
+
|
|
89
|
+
3. Creation date (format: dd.mm.yyyy);
|
|
90
|
+
|
|
91
|
+
4. Description;
|
|
92
|
+
|
|
93
|
+
5. `Show course` button.
|
|
94
|
+
When user clicks `Show course` button `CourseInfo` component with current course info should replace `Courses` component.
|
|
95
|
+
(You will create it after `Courses` component).
|
|
96
|
+
|
|
97
|
+
6. `Authors` list:
|
|
98
|
+
- the authors' names should be displayed on the one line;
|
|
99
|
+
|
|
100
|
+
- if all authors' names do not fit on one line,
|
|
101
|
+
then the extra text should be cut off and '...' should be added at the end of line.
|
|
102
|
+
|
|
103
|
+
:::caution
|
|
104
|
+
In `mockedCoursesList` each course has only authors' `ids`.
|
|
105
|
+
To define authors' names you should find them in `mockedAuthorsList` by id.
|
|
106
|
+
:::
|
|
107
|
+
|
|
108
|
+
### Mocked courses and authors data.
|
|
109
|
+
|
|
110
|
+
Save these data as constants and use in your App:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
export const mockedCoursesList = [
|
|
114
|
+
{
|
|
115
|
+
id: "de5aaa59-90f5-4dbc-b8a9-aaf205c551ba",
|
|
116
|
+
title: "JavaScript",
|
|
117
|
+
description: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum
|
|
118
|
+
has been the industry's standard dummy text ever since the 1500s, when an unknown
|
|
119
|
+
printer took a galley of type and scrambled it to make a type specimen book. It has survived
|
|
120
|
+
not only five centuries, but also the leap into electronic typesetting, remaining essentially u
|
|
121
|
+
nchanged.`,
|
|
122
|
+
creationDate: "08/03/2021",
|
|
123
|
+
duration: 160,
|
|
124
|
+
authors: [
|
|
125
|
+
"27cc3006-e93a-4748-8ca8-73d06aa93b6d",
|
|
126
|
+
"f762978b-61eb-4096-812b-ebde22838167",
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "b5630fdd-7bf7-4d39-b75a-2b5906fd0916",
|
|
131
|
+
title: "Angular",
|
|
132
|
+
description: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum
|
|
133
|
+
has been the industry's standard dummy text ever since the 1500s, when an unknown
|
|
134
|
+
printer took a galley of type and scrambled it to make a type specimen book.`,
|
|
135
|
+
creationDate: "10/11/2020",
|
|
136
|
+
duration: 210,
|
|
137
|
+
authors: [
|
|
138
|
+
"df32994e-b23d-497c-9e4d-84e4dc02882f",
|
|
139
|
+
"095a1817-d45b-4ed7-9cf7-b2417bcbf748",
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
export const mockedAuthorsList = [
|
|
145
|
+
{
|
|
146
|
+
id: "27cc3006-e93a-4748-8ca8-73d06aa93b6d",
|
|
147
|
+
name: "Vasiliy Dobkin",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "f762978b-61eb-4096-812b-ebde22838167",
|
|
151
|
+
name: "Nicolas Kim",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "df32994e-b23d-497c-9e4d-84e4dc02882f",
|
|
155
|
+
name: "Anna Sidorenko",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "095a1817-d45b-4ed7-9cf7-b2417bcbf748",
|
|
159
|
+
name: "Valentina Larina",
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `EmptyCourseList` component
|
|
165
|
+
|
|
166
|
+

|
|
167
|
+
[Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2906-66989&t=GislgRTjkGsyZ4Xy-0)
|
|
168
|
+
|
|
169
|
+
`EmptyCourseList` component should be rendered instead of `Courses` components when
|
|
170
|
+
we do not have any course.
|
|
171
|
+
|
|
172
|
+
`EmptyCourseList` component should include:
|
|
173
|
+
|
|
174
|
+
- Title with text: 'Course List is Empty'.
|
|
175
|
+
|
|
176
|
+
- Subtitle text: 'Please use "Add New Course" button to add your first course'.
|
|
177
|
+
|
|
178
|
+
- `Add New Course` button **WITHOUT** functionality for current task. (Reuse `Button` Component).
|
|
179
|
+
|
|
180
|
+
### Create `Courses` component.
|
|
181
|
+
|
|
182
|
+

|
|
183
|
+
[Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2907-67422&t=2XuYekWUy7fL4Ba9-0)
|
|
184
|
+
|
|
185
|
+
`Courses` component should be rendered in `App` component.
|
|
186
|
+
|
|
187
|
+
`Courses` component should include:
|
|
188
|
+
|
|
189
|
+
- `SearchBar` component (optional extra task).
|
|
190
|
+
|
|
191
|
+
- `CourseCard` component.
|
|
192
|
+
|
|
193
|
+
- `Add new course` button WITHOUT any functionality. (Reuse `Button` component).
|
|
194
|
+
|
|
195
|
+
:::tip
|
|
196
|
+
You should import `mockedCoursesList` and `mockedAuthorsList` to the `App` component and pass it to the `Courses` component as a prop.
|
|
197
|
+
Use loop for `CourseCard` rendering.
|
|
198
|
+
Please, follow [this link](https://react.dev/learn/rendering-lists#rendering-data-from-arrays) to the **"Embedding map() in JSX"** topic to see the example.
|
|
199
|
+
:::
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Course info
|
|
204
|
+
|
|
205
|
+

|
|
206
|
+
[Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=6130-322&t=2XuYekWUy7fL4Ba9-0)
|
|
207
|
+
|
|
208
|
+
- This component shows information about the course.
|
|
209
|
+
|
|
210
|
+
- This component contains:
|
|
211
|
+
- ID of course;
|
|
212
|
+
- Title;
|
|
213
|
+
- Description;
|
|
214
|
+
- Duration;
|
|
215
|
+
- List of authors;
|
|
216
|
+
- Creation date;
|
|
217
|
+
- `Back to courses` button.
|
|
218
|
+
|
|
219
|
+
- This component should be rendered instead of `Courses` component.
|
|
220
|
+
|
|
221
|
+
- Pass course information to the `CourseInfo` component using `props`.
|
|
222
|
+
|
|
223
|
+
- By clicking `Back` button `CourseInfo` component should be replaced by `Courses` component.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Search. Extra Task
|
|
228
|
+
|
|
229
|
+

|
|
230
|
+
[Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2905-67147&t=2XuYekWUy7fL4Ba9-0)
|
|
231
|
+
|
|
232
|
+
### Create `SearchBar` component.
|
|
233
|
+
|
|
234
|
+
`SearchBar` should be in Courses component (above the list of courses).
|
|
235
|
+
|
|
236
|
+
`SearchBar` component should include:
|
|
237
|
+
|
|
238
|
+
- `Input` component;
|
|
239
|
+
|
|
240
|
+
- `Button` component.
|
|
241
|
+
|
|
242
|
+
#### Add functionality for searching courses:
|
|
243
|
+
|
|
244
|
+
- User should have ability to search course by **title** and **id**;
|
|
245
|
+
|
|
246
|
+
- The search is performed by the occurrence of characters in the string,
|
|
247
|
+
and **not just by a match at the beginning of the string**;
|
|
248
|
+
|
|
249
|
+
- **Case-insensitive** search;
|
|
250
|
+
|
|
251
|
+
- When user clicks on `Search` button it displays all courses that match the search query;
|
|
252
|
+
|
|
253
|
+
- All courses are displayed when user cleans search field.
|
|
254
|
+
|
|
255
|
+
<sub>(Search by title example)</sub>
|
|
256
|
+
|
|
257
|
+

|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 3
|
|
3
|
+
sidebar_label: 'GOOD PRACTICES'
|
|
4
|
+
title: GOOD PRACTICES
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# GOOD PRACTICES THAT YOU CAN APPLY FOR THIS TASK:
|
|
8
|
+
1. Place `css` files in the folder with the component.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
```
|
|
12
|
+
src
|
|
13
|
+
|
|
|
14
|
+
|- components
|
|
15
|
+
|
|
|
16
|
+
|-Header
|
|
17
|
+
|-Header.jsx //component
|
|
18
|
+
|-header.css //styles for this component
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
2. Use `label` tag for input. [Link to the W3C.](https://www.w3schools.com/tags/tag_label.asp)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
3. Use `key` attribute for list rendering. [Link to the documentation.](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
4. Use constants to avoid a hardcode. *For example, when passing string properties to a component:*
|
|
28
|
+
```js
|
|
29
|
+
// BAD
|
|
30
|
+
<Button text='Search' />
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
// GOOD
|
|
34
|
+
//constants.js
|
|
35
|
+
const BUTTON_TEXT = 'Search';
|
|
36
|
+
|
|
37
|
+
//Button.jsx
|
|
38
|
+
import {BUTTON_TEXT} from './constants.js';
|
|
39
|
+
|
|
40
|
+
<Button text={BUTTON_TEXT} />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
5. Add empty string between import groups. *Example:*
|
|
44
|
+
```js
|
|
45
|
+
import from '[node_modules]'
|
|
46
|
+
|
|
47
|
+
import from '[own_components]'
|
|
48
|
+
import from '[constants]'
|
|
49
|
+
|
|
50
|
+
import from '[styles]'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
6. Use common `index.js` file for components and helpers. [Link to the article](https://www.digitalocean.com/community/tutorials/react-index-js-public-interfaces).
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
7. If component does not have any logic you can return the markup immediately. *Examples:*
|
|
57
|
+
```js
|
|
58
|
+
import logo from '../../assets/logo.jpg';
|
|
59
|
+
|
|
60
|
+
export const Logo = () => <img src={logo} />;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
or
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
export const Button = ({ text, onClick }) => (
|
|
67
|
+
<button onClick={onClick}>{text}</button>
|
|
68
|
+
);
|
|
69
|
+
```
|