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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 1
|
|
3
|
+
sidebar_label: 'What is SPA?'
|
|
4
|
+
title: 'Single Page Application'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
In order to work with the React library effectively, it is essential to have a clear understanding of SPA, as React is specifically designed to facilitate the development of such applications.
|
|
9
|
+
|
|
10
|
+
## What is SPA?
|
|
11
|
+
|
|
12
|
+
An application that loads a single HTML page with all the essential assets, including JavaScript and CSS, needed for its operation is known as a single-page application.
|
|
13
|
+
|
|
14
|
+
In a single-page application, any interactions with the page or subsequent pages do not necessitate a round trip to the server, resulting in the page **not being reloaded**.
|
|
15
|
+
|
|
16
|
+
## Why Go for SPAs?
|
|
17
|
+
|
|
18
|
+
Back in the old days, when browsers weren't as advanced as they are now and JavaScript didn't work as well, every page came from a server. When a user clicked on something, a new request went to the server, and the browser then loaded the new page.
|
|
19
|
+
|
|
20
|
+
Only a few creative products went a different way from this usual method and tried out new ways.
|
|
21
|
+
## Who is using the SPA?
|
|
22
|
+
**Popular Examples of Single Page Applications:**
|
|
23
|
+
|
|
24
|
+
* Gmail
|
|
25
|
+
|
|
26
|
+
* Google Maps
|
|
27
|
+
|
|
28
|
+
* Facebook
|
|
29
|
+
|
|
30
|
+
* Twitter
|
|
31
|
+
|
|
32
|
+
* Google Drive
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Advantages of SPAs
|
|
36
|
+
|
|
37
|
+
👍 SPA delivers a **quicker user experience**, eliminating the need to wait for communication between the client and server or for the browser to re-render the page. Instead, instant feedback can be given.
|
|
38
|
+
|
|
39
|
+
👍 SPA **uses fewer resources on the server**. This is because you can focus on making a good API instead of spending time on server stuff.
|
|
40
|
+
|
|
41
|
+
👍 This makes it a **great option if you're thinking of creating a mobile app** based on the API, as you can entirely reuse your existing server-side code.
|
|
42
|
+
|
|
43
|
+
👍 **Converting SPA into [Progressive Web Apps](https://developer.chrome.com/blog/getting-started-pwa) is a simple process.** This enables you to provide local caching and support offline experiences for your services or deliver improved error messages when users need to be online.
|
|
44
|
+
|
|
45
|
+
👍 SPAs are an **excellent choice for team collaboration**. Backend developers can focus on building a robust API, while frontend developers can dedicate their efforts to creating an outstanding user experience using the API developed in the backend.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Disadvantages of SPAs
|
|
49
|
+
|
|
50
|
+
While Single Page Applications (SPAs) offer advantages, they also have some downsides. Here are some cons to consider:
|
|
51
|
+
|
|
52
|
+
👎 **Initial Loading Time:** SPAs may take longer to load initially because the entire application needs to be loaded at once. This can lead to a slower first-time user experience.
|
|
53
|
+
|
|
54
|
+
👎 **Search Engine Optimization (SEO) Challenges:** SPAs heavily rely on JavaScript, and some search engines may face difficulties in crawling and indexing the content. This can potentially affect the website's search engine ranking.
|
|
55
|
+
|
|
56
|
+
👎 **Resource Intensiveness:** SPAs can be resource-intensive, especially for less powerful devices or slower internet connections. This may result in performance issues on devices with limited capabilities.
|
|
57
|
+
|
|
58
|
+
👎 **Security Concerns:** SPAs rely heavily on client-side processing, which may expose certain vulnerabilities if proper security measures are not implemented. Cross-site scripting (XSS) attacks are one example of potential security issues.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
:::caution
|
|
62
|
+
It's important to carefully weigh the pros and cons of SPAs based on the specific requirements of your project before deciding to use this approach.
|
|
63
|
+
:::
|
|
64
|
+
|
|
65
|
+
## Changing the Way Navigation Works
|
|
66
|
+
|
|
67
|
+
In SPAs, the usual browser navigation is not there, so you have to take care of the URLs manually.
|
|
68
|
+
|
|
69
|
+
The part of the application handling URLs is called the router. Some frameworks, like Ember, do this automatically, but others need additional libraries for it, such as **React Router**.
|
|
70
|
+
|
|
71
|
+
**What's the challenge?**
|
|
72
|
+
In the beginning, many developers creating Single Page Applications didn't pay much attention to this, causing a common problem known as the "broken back button." Since the browser's regular navigation was taken over, the URL didn't change when moving around the application. As a result, when users pressed the back button, hoping to return to the previous screen, they might end up on a website they visited a long time ago.
|
|
73
|
+
|
|
74
|
+
Nowadays, there are solutions for this using the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) provided by browsers, but most of the time, you'll use a library like [**React Router**](/docs/module-2/react-router), which internally makes use of that API.
|
|
@@ -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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
80
|
+
:::
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Login (new component)
|
|
85
|
+
|
|
86
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+
});
|