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.
Files changed (115) hide show
  1. package/README.md +46 -0
  2. package/bin/workshop.js +6 -0
  3. package/dist/auth-check.d.ts +1 -0
  4. package/dist/auth-check.js +45 -0
  5. package/dist/auth-check.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +156 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/content/content/module-1/task/module-1_home-task_components.md +257 -0
  10. package/dist/content/content/module-1/task/module-1_home-task_good_practices.md +69 -0
  11. package/dist/content/content/module-1/task/module-1_home-task_task.md +253 -0
  12. package/dist/content/content/module-1/tests/Button.test.tsx +22 -0
  13. package/dist/content/content/module-1/tests/CourseCard.test.tsx +64 -0
  14. package/dist/content/content/module-1/tests/CourseInfo.test.tsx +73 -0
  15. package/dist/content/content/module-1/tests/Courses.test.tsx +87 -0
  16. package/dist/content/content/module-1/tests/Header.test.tsx +23 -0
  17. package/dist/content/content/module-1/tests/Input.test.tsx +36 -0
  18. package/dist/content/content/module-1/tests/helpers.test.ts +13 -0
  19. package/dist/content/content/module-1/theory/module-1_elements-render.md +66 -0
  20. package/dist/content/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
  21. package/dist/content/content/module-1/theory/module-1_hooks_useState.md +131 -0
  22. package/dist/content/content/module-1/theory/module-1_react.md +64 -0
  23. package/dist/content/content/module-1/theory/module-1_spa.md +74 -0
  24. package/dist/content/content/module-2/task/module-2_home-task_components.md +313 -0
  25. package/dist/content/content/module-2/task/module-2_home-task_good_practices.md +35 -0
  26. package/dist/content/content/module-2/task/module-2_home-task_task.md +210 -0
  27. package/dist/content/content/module-2/tests/App.test.tsx +54 -0
  28. package/dist/content/content/module-2/tests/Login.test.tsx +73 -0
  29. package/dist/content/content/module-2/tests/Registration.test.tsx +70 -0
  30. package/dist/content/content/module-2/tests/SearchBar.test.tsx +39 -0
  31. package/dist/content/content/module-2/theory/module-2_custom-hooks.md +201 -0
  32. package/dist/content/content/module-2/theory/module-2_hooks.md +117 -0
  33. package/dist/content/content/module-2/theory/module-2_react-router.md +328 -0
  34. package/dist/content/content/module-3/task/module-3_home-task_components.md +94 -0
  35. package/dist/content/content/module-3/task/module-3_home-task_good_practices.md +26 -0
  36. package/dist/content/content/module-3/task/module-3_home-task_task.md +170 -0
  37. package/dist/content/content/module-3/tests/App.test.tsx +54 -0
  38. package/dist/content/content/module-3/tests/Courses.test.tsx +87 -0
  39. package/dist/content/content/module-3/tests/CreateAuthor.test.tsx +44 -0
  40. package/dist/content/content/module-3/tests/CreateCourse.test.tsx +122 -0
  41. package/dist/content/content/module-3/theory/module-3_redux-hooks.md +194 -0
  42. package/dist/content/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
  43. package/dist/content/content/module-4/task/module-4_home-task_components.md +187 -0
  44. package/dist/content/content/module-4/task/module-4_home-task_task.md +139 -0
  45. package/dist/content/content/module-4/tests/App.test.tsx +54 -0
  46. package/dist/content/content/module-4/tests/Courses.test.tsx +87 -0
  47. package/dist/content/content/module-4/tests/CreateCourse.test.tsx +122 -0
  48. package/dist/content/content/module-4/tests/Login.test.tsx +73 -0
  49. package/dist/content/content/module-4/theory/module-4_async-redux.md +99 -0
  50. package/dist/content/content/module-4/theory/module-4_private-routes.md +55 -0
  51. package/dist/content/content/module-5/task/module-5_home-task_instruction.md +68 -0
  52. package/dist/content/content/module-5/task/module-5_home-task_task.md +154 -0
  53. package/dist/content/content/module-5/tests/App.test.tsx +54 -0
  54. package/dist/content/content/module-5/tests/CourseCard.test.tsx +64 -0
  55. package/dist/content/content/module-5/tests/Courses.test.tsx +87 -0
  56. package/dist/content/content/module-5/tests/Header.test.tsx +23 -0
  57. package/dist/content/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
  58. package/dist/content/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
  59. package/dist/content/module-1/task/module-1_home-task_components.md +257 -0
  60. package/dist/content/module-1/task/module-1_home-task_good_practices.md +69 -0
  61. package/dist/content/module-1/task/module-1_home-task_task.md +253 -0
  62. package/dist/content/module-1/tests/Button.test.tsx +22 -0
  63. package/dist/content/module-1/tests/CourseCard.test.tsx +64 -0
  64. package/dist/content/module-1/tests/CourseInfo.test.tsx +73 -0
  65. package/dist/content/module-1/tests/Courses.test.tsx +87 -0
  66. package/dist/content/module-1/tests/Header.test.tsx +23 -0
  67. package/dist/content/module-1/tests/Input.test.tsx +36 -0
  68. package/dist/content/module-1/tests/helpers.test.ts +13 -0
  69. package/dist/content/module-1/theory/module-1_elements-render.md +66 -0
  70. package/dist/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
  71. package/dist/content/module-1/theory/module-1_hooks_useState.md +131 -0
  72. package/dist/content/module-1/theory/module-1_react.md +64 -0
  73. package/dist/content/module-1/theory/module-1_spa.md +74 -0
  74. package/dist/content/module-2/task/module-2_home-task_components.md +313 -0
  75. package/dist/content/module-2/task/module-2_home-task_good_practices.md +35 -0
  76. package/dist/content/module-2/task/module-2_home-task_task.md +210 -0
  77. package/dist/content/module-2/tests/App.test.tsx +54 -0
  78. package/dist/content/module-2/tests/Login.test.tsx +73 -0
  79. package/dist/content/module-2/tests/Registration.test.tsx +70 -0
  80. package/dist/content/module-2/tests/SearchBar.test.tsx +39 -0
  81. package/dist/content/module-2/theory/module-2_custom-hooks.md +201 -0
  82. package/dist/content/module-2/theory/module-2_hooks.md +117 -0
  83. package/dist/content/module-2/theory/module-2_react-router.md +328 -0
  84. package/dist/content/module-3/task/module-3_home-task_components.md +94 -0
  85. package/dist/content/module-3/task/module-3_home-task_good_practices.md +26 -0
  86. package/dist/content/module-3/task/module-3_home-task_task.md +170 -0
  87. package/dist/content/module-3/tests/App.test.tsx +54 -0
  88. package/dist/content/module-3/tests/Courses.test.tsx +87 -0
  89. package/dist/content/module-3/tests/CreateAuthor.test.tsx +44 -0
  90. package/dist/content/module-3/tests/CreateCourse.test.tsx +122 -0
  91. package/dist/content/module-3/theory/module-3_redux-hooks.md +194 -0
  92. package/dist/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
  93. package/dist/content/module-4/task/module-4_home-task_components.md +187 -0
  94. package/dist/content/module-4/task/module-4_home-task_task.md +139 -0
  95. package/dist/content/module-4/tests/App.test.tsx +54 -0
  96. package/dist/content/module-4/tests/Courses.test.tsx +87 -0
  97. package/dist/content/module-4/tests/CreateCourse.test.tsx +122 -0
  98. package/dist/content/module-4/tests/Login.test.tsx +73 -0
  99. package/dist/content/module-4/theory/module-4_async-redux.md +99 -0
  100. package/dist/content/module-4/theory/module-4_private-routes.md +55 -0
  101. package/dist/content/module-5/task/module-5_home-task_instruction.md +68 -0
  102. package/dist/content/module-5/task/module-5_home-task_task.md +154 -0
  103. package/dist/content/module-5/tests/App.test.tsx +54 -0
  104. package/dist/content/module-5/tests/CourseCard.test.tsx +64 -0
  105. package/dist/content/module-5/tests/Courses.test.tsx +87 -0
  106. package/dist/content/module-5/tests/Header.test.tsx +23 -0
  107. package/dist/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
  108. package/dist/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
  109. package/dist/content-loader.d.ts +7 -0
  110. package/dist/content-loader.js +26 -0
  111. package/dist/content-loader.js.map +1 -0
  112. package/dist/context-builder.d.ts +2 -0
  113. package/dist/context-builder.js +116 -0
  114. package/dist/context-builder.js.map +1 -0
  115. package/package.json +40 -0
@@ -0,0 +1,313 @@
1
+ ---
2
+ sidebar_position: 2
3
+ sidebar_label: "COMPONENTS"
4
+ title: Components description for Tasks 2
5
+ ---
6
+
7
+ ## App component
8
+
9
+ ### Add the router to the App component.
10
+
11
+ All movements on the pages of the application can be carried out through the [react-router-dom](https://reactrouter.com/en/main/start/tutorial).
12
+ Router should be described in `App.jsx`.
13
+ [Adding routes](/docs/module-2/react-router#adding-routes).
14
+
15
+ ---
16
+
17
+ ## Registration (new component)
18
+
19
+ ![](images/registration.jpg)
20
+ [Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2932-219&t=2XuYekWUy7fL4Ba9-0)
21
+
22
+ ### Create `Registration` component.
23
+
24
+ - This component should contain the following elements:
25
+ - `name` field;
26
+
27
+ - `email` field;
28
+
29
+ - `password` field;
30
+
31
+ - `Registration` button;
32
+
33
+ - link that navigates to `Login` component.
34
+
35
+ - This component should be rendered by the route `/registration`.
36
+ `Registration` component should have functionality to send request to API for creating a new user.
37
+ See `/register` endpoint in API Swagger.
38
+
39
+ - After successful registration, user is redirected to the `Login` page by route `/login`.
40
+
41
+ :::tip
42
+ For redirection you can use [`Link`](https://reactrouter.com/en/main/components/link) or
43
+ [`useNavigate`](https://reactrouter.com/en/main/hooks/use-navigate) hook.
44
+ :::
45
+
46
+ - Use `form` tag.
47
+
48
+ - You should reuse `Input` and `Button` components.
49
+
50
+ - Request should be sent by `submit` event. Use `onSubmit` props for `form`.
51
+
52
+ - Validation should be added (all fields are required, empty values are not allowed). When the user submits the form and fields are not valid, validation errors should be displayed.
53
+
54
+ ![](images/registration_validation.jpg)
55
+ [Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2932-257&t=2XuYekWUy7fL4Ba9-0)
56
+
57
+ ### Example of POST request using `fetch`:
58
+
59
+ ```js
60
+ const newUser = {
61
+ name,
62
+ password,
63
+ email,
64
+ };
65
+
66
+ const response = await fetch("http://localhost:4000/register", {
67
+ method: "POST",
68
+ body: JSON.stringify(newUser),
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ },
72
+ });
73
+
74
+ const result = await response.json();
75
+ ```
76
+
77
+ :::warning
78
+ There is an email validation on the back end side. In case of incorrect email format you will get an error.
79
+ ![](images/mail-error.jpg)
80
+ :::
81
+
82
+ ---
83
+
84
+ ## Login (new component)
85
+
86
+ ![](images/login.jpg)
87
+ [Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2927-216&t=2XuYekWUy7fL4Ba9-0)
88
+
89
+ ### Create `Login` component.
90
+
91
+ - This component should contain the following elements:
92
+ - `email` field;
93
+
94
+ - `password` field;
95
+
96
+ - `Login` button;
97
+
98
+ - Link to `Registration` component (use `Link` from `react-router-dom`).
99
+
100
+ - `Login` should be rendered by route `/login`;
101
+
102
+ - `Login` should have functionality that
103
+ sends request to API for getting token.
104
+ See `/login` endpoint in API Swagger.
105
+
106
+ - Use `form` tag.
107
+
108
+ - You should use `Input` and `Button` components.
109
+
110
+ - Request should be sent by `submit` event. Use `onSubmit` props for `form`.
111
+
112
+ - Validation should be added (all fields are required, empty values are not allowed). When the user submits the form and fields are not valid, validation errors should be displayed.
113
+
114
+ ![](images/login_validation.jpg)
115
+ [Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2932-191&t=2XuYekWUy7fL4Ba9-0)
116
+
117
+ - Response contains value `result`, it's user's token. You should save it to the `localStorage`.
118
+
119
+ - After successful login, user is redirected to the `Courses` page by route `/courses`.
120
+
121
+ ---
122
+
123
+ ## Course info
124
+
125
+ ### Implement a new feature for `CourseInfo` component:
126
+
127
+ - This component should be rendered by route `/courses/:courseId`.
128
+
129
+ - Find a course in courses list by an id using `courseId` path-param.
130
+
131
+ - Use `react-router-dom` [hook](https://reactrouter.com/en/main/hooks/use-params) `useParams` to get courseId from url.
132
+
133
+ - `Back` button should navigate to `/courses` and then the `Courses` component appears.
134
+ Use [Link](https://reactrouter.com/en/main/components/link) from `react-router-dom`.
135
+
136
+ ---
137
+
138
+ ## Courses
139
+
140
+ ### Implement new features for `Courses` component:
141
+
142
+ - Component `Courses` should be opened by route `/courses`;
143
+
144
+ - If there is a token in the `localStorage`, then App navigates to the `/courses` by default.
145
+
146
+ - **You don't have to get courses and authors lists from backend.** Please continue using mock data. We will use API for these data in the next module.
147
+
148
+ - Use [Link](https://reactrouter.com/en/main/components/link) from `react-router-dom` for `Add New Course` button.
149
+
150
+ - When user clicks the `Add New Course` button the App navigates to `/courses/add`.
151
+
152
+ ---
153
+
154
+ ## Add new course
155
+
156
+ ![](images/empty_create_courses_form.jpg)
157
+ [Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=2907-67422&t=2XuYekWUy7fL4Ba9-0)
158
+
159
+ ### Create `Input` component.
160
+
161
+ **Input component** should contain `input` and `label` tags.
162
+
163
+ :::note
164
+ Using a `label` tag for `input` is considered good practice.
165
+ [Usage and benefits of label tag.](https://www.w3schools.com/tags/tag_label.asp)
166
+ :::
167
+
168
+ :::tip
169
+ You will use `Input component` in several places through your app,
170
+ so you should use `props` for this component such as `labelText`, `placeholderText` and `onChange`
171
+ (and any props, that you want).
172
+ :::
173
+
174
+ ### Create `AuthorItem` component.
175
+
176
+ ![](images/author_item.jpg)
177
+ This component should be rendered in the `CreateCourse` component.
178
+
179
+ `AuthorItem` component should contain the following elements:
180
+
181
+ - Author's name;
182
+
183
+ - `Button` component (functionality is described below);
184
+
185
+ ### Create `CreateCourse` component.
186
+
187
+ This component should be rendered by route `courses/add`.
188
+
189
+ `CreateCourse` component should contain the following elements:
190
+
191
+ - **Title** (input) - field for input course name. Text length should be at least 2 characters;
192
+
193
+ - **Description** (textarea) - text length should be at least 2 characters;
194
+
195
+ - **Authors** - contains a list of all authors and their corresponding `Add author` buttons
196
+ (use loop and `AuthorItem` component);
197
+
198
+ - **Course authors** - contains a list of authors course and their corresponding `Delete author` buttons
199
+ (use loop and `AuthorItem` component);
200
+
201
+ - **Delete author** - when user clicks on this button the corresponding author disappears from the
202
+ **Course authors** list and shows in **Authors**;
203
+
204
+ - **Add author** - when user clicks on this button the corresponding author disappears from the
205
+ **Authors** list and shows in **Course authors**. New author should be added to the initial author's list;
206
+
207
+ - **Author field** (input) - author name length should be at least 2 characters;
208
+
209
+ - **Create author** (button) - when user clicks on this button:
210
+ - the new author appears in **Authors**;
211
+ - the author's id is generated automatically;
212
+ - clean input.
213
+
214
+ - **Duration** - this part provides logic for adding course duration time.
215
+ - the duration of the course is entered in minutes;
216
+ - for the correct display of the course duration,
217
+ you need to format minutes into hours and minutes;
218
+ - duration should be more than 0 minutes;
219
+ - user should have an ability to enter ONLY numbers into the field.
220
+
221
+ - **Create course** (button) - when user clicks on this button:
222
+ - User is navigated to the route`/courses`;
223
+ - New course is added to the courses list.
224
+
225
+ :::caution
226
+ `Create course` button should always be available.
227
+ :::
228
+
229
+ :::danger
230
+ ALL FIELDS ARE REQUIRED. Validation should be added.
231
+ :::
232
+
233
+ In case, when the user clicks on the `Create course` button, and some
234
+ field is not filled in, then validation error messages should be displayed.
235
+
236
+ ![](images/add_new_course_validation.jpg)
237
+ [Figma link](https://www.figma.com/design/9MsU97rn21GuQIT01xwLuc/React-Fundamentals-Course?node-id=6131-439&t=2XuYekWUy7fL4Ba9-0)
238
+
239
+ :::tip
240
+ Look at [One more example for controlled inputs (from Home Task)](/docs/module-1/forms#-one-more-example-for-controlled-inputs-from-home-task)
241
+ :::
242
+
243
+ ### Course model
244
+
245
+ ```js
246
+ // Course
247
+ {
248
+ id: string;
249
+ title: string;
250
+ description: string;
251
+ creationDate: string;
252
+ duration: number;
253
+ authors: [authorId];
254
+ }
255
+ ```
256
+
257
+ ### Author model
258
+
259
+ ```js
260
+ // Author
261
+ {
262
+ id: string;
263
+ name: string;
264
+ }
265
+ ```
266
+
267
+ - Component `CreateCourse` should be opened by router `/courses/add`.
268
+
269
+ - When user clicks on `Create course` button, App navigates to `/courses`
270
+ (the new course should be in the course list in `Courses` component).
271
+
272
+ ### To sum up
273
+
274
+ Now that you have created the `CreateCourse` component you see that it is quite large
275
+ and a new person will find it difficult to understand.
276
+
277
+ **The `CreateCourse` component is an ideal place to practice [composition](/docs/module-1/components-and-props#extracting-components).**
278
+
279
+ :::info
280
+ Field **id** for course and for the author should be generated automatically.
281
+
282
+ To do this, you can use the [UUID library](https://www.npmjs.com/package/uuid)
283
+ or any way you like.
284
+ :::
285
+
286
+ :::info
287
+ Field **creationDate** created automatically base on the current date.
288
+ Date format: dd/mm/yyyy (see examples in mockedCoursesList).
289
+ :::
290
+
291
+ :::tip
292
+ Study [this documentation](https://react.dev/reference/react-dom/components#common) to do **conditional rendering** for `CreateCourse` component. (Suggested syntax is (logical && operator)[https://react.dev/reference/react-dom/components#common], easy to read and use.)
293
+ :::
294
+
295
+ ---
296
+
297
+ ## Header
298
+
299
+ ### Implement a new feature for `Header` component:
300
+
301
+ - Show user's name if he is logged in.
302
+
303
+ - When user clicks on `Logout` button, App should navigate to `/login`
304
+ and you should remove `token` from the localStorage.
305
+
306
+ - `Logout` button and user's name should not be on Login and Registration pages.
307
+
308
+ ---
309
+
310
+ :::tip
311
+ You can use hook [`useLocation`](https://reactrouter.com/en/main/hooks/use-location) to determine the current pages.
312
+ :::
313
+
@@ -0,0 +1,35 @@
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. It's a good practice to check response status to provide default behavior for failed requests.
9
+
10
+ Example:
11
+ ```jsx
12
+ function SignUpButton(props) {
13
+ const [hasError, setError] = React.useState(false);
14
+ const handleClick = async () => {
15
+ try {
16
+ await api.signUp();
17
+ } catch(error) {
18
+ errorService.log(error)
19
+ setError(true);
20
+ }
21
+ }
22
+ if (hasError) {
23
+ return <p>Sorry, Sign up failed!</p>;
24
+ }
25
+ return <button onClick={handleClick}>Sign up</button>;
26
+ }
27
+ ```
28
+
29
+ 2. Show duration time in a format [hh:mm] hour(s)
30
+
31
+ In this exercise, think about the helper methods you already created!
32
+
33
+ 3. Naming convention
34
+
35
+ Check your variables, state names! Pay attention to (naming conventions)[https://dev.to/michi/tips-on-naming-boolean-variables-cleaner-code-35ig].
@@ -0,0 +1,210 @@
1
+ ---
2
+ sidebar_position: 1
3
+ sidebar_label: 'TASK'
4
+ title: 'Module 2. Router'
5
+ ---
6
+ This is the second part of the application.
7
+ In this assignment,
8
+ you should continue to work with your project with a slight modification.
9
+ This module is designed to teach how to work with routing in React.
10
+
11
+
12
+ ## Prerequisite:
13
+
14
+ First, clone and run repo with a server:
15
+
16
+ 1. Clone repo with a server [(link)](https://autocode.git.epam.com/ld-autocode-js-programs/react-fundamentals/react-fundamentals-app-api).
17
+
18
+ 2. Go to server project folder, install dependencies and run server:
19
+ ```http request
20
+ npm install
21
+
22
+ npm run start
23
+ ```
24
+ 3. Now you have a backend for your application.
25
+
26
+ Go to the url: [http://localhost:4000/api](http://localhost:4000/api)
27
+
28
+ You should see a page with all APIs:
29
+ ![](images/SWAGGER.jpg)
30
+
31
+ :::caution
32
+ These are the implemented APIs for the app, BUT **you should only use the ones that are specified in the task**.
33
+ :::
34
+ ___
35
+
36
+ Secondly, you need to install the `react-router-dom` module in your project using npm:
37
+
38
+ 1. Go to your project directory.
39
+
40
+ 2. Create a new branch for Module 2 task.
41
+
42
+ 3. Install `react-router-dom`.
43
+ ```http request
44
+ npm install react-router-dom --save
45
+ ```
46
+
47
+ 3. Run your project
48
+ ```http request
49
+ npm run start
50
+ ```
51
+
52
+ ## Project structure requirements
53
+
54
+ 1. Create new folders and `jsx` files for each component:
55
+ ```
56
+ src
57
+ |-- components
58
+ | |-- CreateCourse
59
+ | | |__ components
60
+ | | |-- AuthorItem
61
+ | | |__ AuthorItem.jsx/.tsx
62
+ | | |__ CreateCourse.jsx/.tsx
63
+ | |
64
+ | |-- Login
65
+ | | |__ Login.jsx/.tsx
66
+ | |
67
+ | |-- Registration
68
+ | | |__ Registration.jsx/.tsx
69
+ | |
70
+ | |__ ...
71
+ |__ ...
72
+ ```
73
+
74
+
75
+ 2. For sending requests to API you should use [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) or
76
+ [axios](https://www.npmjs.com/package/axios).
77
+
78
+
79
+ 3. APIs from SWAGGER for Module 2:
80
+ - `/login` [POST]
81
+ - `/register` [POST]
82
+
83
+ ___
84
+ ## Criteria (35 points max)
85
+
86
+ ### Common
87
+
88
+ * [3 points] - Use `react-router-dom` hooks: [`useParams`](https://reactrouter.com/en/main/hooks/use-params), [`useNavigate`](https://reactrouter.com/en/main/hooks/use-navigate) etc.
89
+
90
+ * [5 points] - Add data type checking for props to all components using
91
+ [PropTypes](https://react.dev/reference/react/Component#static-proptypes) or [TypeScript](https://create-react-app.dev/docs/adding-typescript/).
92
+ **If you already use TypeScript, please ignore this requirement.**
93
+
94
+ ### [Registration Component](/docs/module-2/home-task/components#registration-new-component)
95
+ * [2 points] - Create component **Registration** with.
96
+
97
+ * [1 point] - Registration form should appear after clicking on the *Registration* [link](https://reactrouter.com/en/main/components/link)
98
+ on the `Login` form.
99
+
100
+
101
+ * [1 point] - Registration form should appear by route `/registration`.
102
+
103
+
104
+ * [1 points] - Registration should have an auth functionality.
105
+ User enters email, name and password, presses the `Registration` button then application sends request to API.
106
+ See `/register` endpoint in API Swagger.
107
+ After successful registration application
108
+ [navigates](https://reactrouter.com/en/main/components/navigate)
109
+ you to `Login` page.
110
+
111
+
112
+ * [1 point] - Validation required for all fields.
113
+
114
+
115
+ ### [Login Component](/docs/module-2/home-task/components#login-new-component)
116
+ * [2 points] - Create component **Login**.
117
+
118
+
119
+ * [1 point] - Login should be shown after [first open application](/docs/module-2/react-router#redirect) by route `/login`.
120
+
121
+
122
+ * [1 point] - Login form should appear after clicking on the [link](https://reactrouter.com/en/main/components/link) *Login* on the `Registration` form.
123
+
124
+
125
+ * [1 points] - Login should have an auth functionality.
126
+ When you entered an email and password application sends request to API.
127
+ See `/login` endpoint in API Swagger.
128
+ After successful login application navigates to `Courses` page.
129
+
130
+
131
+ * [2 points] - Save token from API after login.
132
+ Add functionality that check if token in `localStorage`.
133
+ If token is in the `localStorage` app automatically
134
+ navigates to `/courses` route.
135
+
136
+
137
+ * [1 point] - Validation required for all fields.
138
+
139
+
140
+ ### [CourseInfo Component](/docs/module-2/home-task/components#course-info)
141
+
142
+
143
+ * [2 points] - Show information about the course. Use route `/courses/:courseId`
144
+ (`courseId` - id of the current course).
145
+
146
+
147
+ * [2 points] - To find out which course info you should render on `CourseInfo` page,
148
+ you should use
149
+ id of the course from [path-parameters](https://reactrouter.com/en/main/hooks/use-params).
150
+
151
+ * [2 points] - `Back` button should navigate to the route `/courses`.
152
+
153
+
154
+ ### [Courses Component](/docs/module-2/home-task/components#courses)
155
+
156
+ * [2 points] - Component **Courses** should be opened by route `/courses`.
157
+
158
+ * [2 points] - Show `Courses` component by default if there is token in the `localStorage`.
159
+
160
+ * [2 points] - Navigate to the route `courses/add` by clicking `Add New Course` button.
161
+
162
+ ### [CreateCourse Component](/docs/module-2/home-task/components#add-new-course)
163
+
164
+ * [1 point] - Possibility to add a title.
165
+
166
+
167
+ * [1 point] - Possibility to add a description.
168
+
169
+
170
+ * [1 point] - Possibility to add a duration.
171
+
172
+
173
+ * [2 points] - Show duration time in a format «hh:mm».
174
+ Example: **122 min** should be showed as **02:02 hours**.
175
+
176
+
177
+ * [1 point] - Add logic for creating a new author.
178
+
179
+
180
+ * [1 point] - Add logic for adding an author to **Course authors**.
181
+
182
+
183
+ * [1 point] - Add logic for deleting an author from **Course authors**.
184
+
185
+
186
+ * [3 points] - Add logic for saving course (new course should be presented in the courses list).
187
+
188
+
189
+ * [1 points] - Add validation for required fields.
190
+
191
+
192
+ * [2 point] - Open **CreateCourse** component by route `/courses/add`.
193
+
194
+
195
+ ### [Header Component](/docs/module-2/home-task/components#header)
196
+ * [2 point] - Add logout functionality.
197
+ When user clicks `Logout` button in the `Header` component, token should be
198
+ removed from `localStorage` and user is navigated to `Login` page.
199
+
200
+ * [2 point] - Get user's name from the `Login` response and display it in the header.
201
+
202
+ * [2 point] - Remove user's name for the `Login` and `Registration` pages.
203
+
204
+ * [2 point] - Remove `Logout` button for the `Login` and `Registration` pages.
205
+
206
+
207
+ :::info
208
+ Please find detailed description of components and functionality in the [COMPONENTS](components) section.
209
+ :::
210
+
@@ -0,0 +1,54 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { MemoryRouter } from "react-router-dom";
4
+ import App from "../App";
5
+
6
+ describe("App", () => {
7
+ test("renders Login component when token is not present", () => {
8
+ localStorage.removeItem("token");
9
+ render(
10
+ <MemoryRouter initialEntries={["/"]}>
11
+ <App />
12
+ </MemoryRouter>
13
+ );
14
+
15
+ expect(
16
+ screen.getByText(/If you don't have an account you can/)
17
+ ).toBeInTheDocument();
18
+ });
19
+
20
+ test("renders Courses component when token is present", () => {
21
+ localStorage.setItem("token", "token");
22
+ render(
23
+ <MemoryRouter initialEntries={["/"]}>
24
+ <App />
25
+ </MemoryRouter>
26
+ );
27
+
28
+ const courseElements = screen.getAllByTestId("courseCard");
29
+
30
+ expect(courseElements[0]).toBeInTheDocument();
31
+ });
32
+
33
+ test("renders Registration component", () => {
34
+ render(
35
+ <MemoryRouter initialEntries={["/registration"]}>
36
+ <App />
37
+ </MemoryRouter>
38
+ );
39
+
40
+ expect(screen.getByLabelText("name")).toBeInTheDocument();
41
+ expect(screen.getByLabelText("email")).toBeInTheDocument();
42
+ expect(screen.getByLabelText("password")).toBeInTheDocument();
43
+ });
44
+
45
+ test("renders course add component", () => {
46
+ render(
47
+ <MemoryRouter initialEntries={["/courses/add"]}>
48
+ <App />
49
+ </MemoryRouter>
50
+ );
51
+
52
+ expect(screen.getByTestId("titleInput")).toBeInTheDocument();
53
+ });
54
+ });
@@ -0,0 +1,73 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, fireEvent, screen } from "@testing-library/react";
3
+ import { MemoryRouter } from "react-router-dom";
4
+ import { Login } from "../components/Login";
5
+ import * as services from "../../../services";
6
+
7
+ beforeEach(() => {
8
+ jest.spyOn(services, "login").mockImplementation(
9
+ jest.fn(() =>
10
+ Promise.resolve({
11
+ result: "token",
12
+ user: {
13
+ name: "John Doe",
14
+ },
15
+ })
16
+ ) as jest.Mock
17
+ );
18
+
19
+ jest.spyOn(global, "fetch").mockImplementation(
20
+ jest.fn(() =>
21
+ Promise.resolve({
22
+ ok: true,
23
+ json: () =>
24
+ Promise.resolve({
25
+ result: "token",
26
+ user: {
27
+ name: "John Doe",
28
+ },
29
+ }),
30
+ })
31
+ ) as jest.Mock
32
+ );
33
+ });
34
+
35
+ describe("Login", () => {
36
+ test("renders login form", () => {
37
+ render(
38
+ <MemoryRouter>
39
+ <Login />
40
+ </MemoryRouter>
41
+ );
42
+
43
+ expect(screen.getByLabelText("email")).toBeInTheDocument();
44
+ expect(screen.getByLabelText("password")).toBeInTheDocument();
45
+ expect(screen.getByText("Login")).toBeInTheDocument();
46
+ expect(
47
+ screen.getByText(/If you don't have an account you can/)
48
+ ).toBeInTheDocument();
49
+ expect(screen.getByRole("link")).toBeInTheDocument();
50
+ });
51
+
52
+ test("submits login form", async () => {
53
+ render(
54
+ <MemoryRouter>
55
+ <Login />
56
+ </MemoryRouter>
57
+ );
58
+
59
+ fireEvent.change(screen.getByLabelText("email"), {
60
+ target: { value: "test@example.com" },
61
+ });
62
+ fireEvent.change(screen.getByLabelText("password"), {
63
+ target: { value: "password123" },
64
+ });
65
+
66
+ fireEvent.click(screen.getByText("login"));
67
+
68
+ expect(services.login).toHaveBeenCalledWith({
69
+ email: "test@example.com",
70
+ password: "password123",
71
+ });
72
+ });
73
+ });