@pplancq/react-template 1.0.0 → 1.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/.eslintrc.js +1 -1
- package/.lintstagedrc.js +1 -0
- package/.stylelintrc.js +1 -1
- package/CHANGELOG.md +33 -0
- package/README.md +9 -213
- package/_README.md +239 -0
- package/_gitignore +3 -1
- package/package.json +31 -19
- package/scripts/migrateToVite.js +98 -0
- package/scripts/removeDemo.js +87 -0
- package/src/api/constant.ts +16 -0
- package/src/api/demoApi.ts +6 -40
- package/src/api/fetchApi.ts +49 -0
- package/src/config/queryClientConfig.ts +1 -1
- package/src/hooks/api/useDemoApi.ts +2 -2
- package/src/pages/Demo/Demo.tsx +1 -2
- package/src/pages/Demo/ReactQueryDemo/ReactQueryDemo.tsx +6 -6
- package/src/pages/Demo/__tests__/__snapshots__/Demo.test.tsx.snap +1 -1
- package/src/pages/{Error/Error.module.css → UnexpectedError/UnexpectedError.module.css} +1 -1
- package/src/pages/{Error/Error.tsx → UnexpectedError/UnexpectedError.tsx} +3 -3
- package/src/pages/UnexpectedError/index.ts +1 -0
- package/src/providers/QueryClientProvider/QueryClientProvider.tsx +2 -2
- package/src/routing/routes.tsx +2 -2
- package/src/types/api.ts +6 -0
- package/src/types/demoApi.ts +19 -3
- package/src/pages/Error/index.ts +0 -1
package/.eslintrc.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = { extends: ['@pplancq/eslint-config/react', '@pplancq/eslint-config/vitest'] };
|
|
1
|
+
module.exports = { extends: ['@pplancq/eslint-config/react', '@pplancq/eslint-config/vitest', '@pplancq/eslint-config/prettier'] };
|
package/.lintstagedrc.js
CHANGED
package/.stylelintrc.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = { extends: ['@pplancq/stylelint-config'] };
|
|
1
|
+
module.exports = { extends: ['@pplancq/stylelint-config/prettier'] };
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
## @pplancq/react-template [1.1.0](https://github.com/pplancq/dev-tools/compare/@pplancq/react-template@1.0.0...@pplancq/react-template@1.1.0) (2024-03-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **react-template:** add fetchApi example ([c2318c3](https://github.com/pplancq/dev-tools/commit/c2318c36097b9d4e3d1c33a15045dafd1a45c8d5))
|
|
7
|
+
* **webpack-config:** add note about the inclusion of environment variable ([e6e4e31](https://github.com/pplancq/dev-tools/commit/e6e4e310aa1a631a4e6013939c52a025f548b31d))
|
|
8
|
+
* **react-template:** add peerDependencies of commitLint and webpack config ([d501781](https://github.com/pplancq/dev-tools/commit/d501781810aae970db0553fd14935e549a0385fc))
|
|
9
|
+
* **react-template:** add script for migrate to vite ([d5d3337](https://github.com/pplancq/dev-tools/commit/d5d333786677f14b846298eba0739c9f55872949))
|
|
10
|
+
* **react-template:** add script for remove Demo ([355d887](https://github.com/pplancq/dev-tools/commit/355d887afdbe09a4b63073ebed6718846eaf717b))
|
|
11
|
+
* **react-template:** add support for yarn and pnpm to script migrate to vite ([b4b9d6b](https://github.com/pplancq/dev-tools/commit/b4b9d6ba9b95f3810b2cfcb03a861a02caf8af8a))
|
|
12
|
+
* **create-react-app:** add support for yarn and pnpm ([eafc39d](https://github.com/pplancq/dev-tools/commit/eafc39d972b178ca21ed307166a9ba394161803f))
|
|
13
|
+
* **react-template:** add test report on .gitignore ([072a240](https://github.com/pplancq/dev-tools/commit/072a2405ed421d22b9ba1b9b11f524a9aa7d0889))
|
|
14
|
+
* **react-template:** rename Error page to UnexpectedError ([c6eea7b](https://github.com/pplancq/dev-tools/commit/c6eea7b1d3e7dd6589945949d03c25485d22fc6c))
|
|
15
|
+
* **react-template:** update eslint un stylelint config ([82a4df3](https://github.com/pplancq/dev-tools/commit/82a4df3e8a3ce1932f915f11ca3495aa43d2aca5))
|
|
16
|
+
* **react-template:** update react-query@3 to @tanstack/react-query@5 ([f79e642](https://github.com/pplancq/dev-tools/commit/f79e6425668bd22b2e278e85a68fc1c214ed8586))
|
|
17
|
+
* **react-template:** update readme ([3aa2543](https://github.com/pplancq/dev-tools/commit/3aa2543948a697f6604f4984884184d3f285d297))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* **react-template:** ambiguous Spacing before <span> ([56c9a8e](https://github.com/pplancq/dev-tools/commit/56c9a8e558a8e0c7e62faba4bf7db85278eb7f80))
|
|
23
|
+
* **react-template:** fix peerDependencies of commitlint-config ([fe0dd7c](https://github.com/pplancq/dev-tools/commit/fe0dd7ca706e4287b36e2ed861a509a8dc7742e2))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Dependencies
|
|
28
|
+
|
|
29
|
+
* **@pplancq/commitlint-config:** upgraded to 2.0.0
|
|
30
|
+
* **@pplancq/eslint-config:** upgraded to 2.0.0
|
|
31
|
+
* **@pplancq/stylelint-config:** upgraded to 2.0.0
|
|
32
|
+
* **@pplancq/webpack-config:** upgraded to 1.1.0
|
|
33
|
+
|
|
1
34
|
## @pplancq/react-template 1.0.0 (2024-02-05)
|
|
2
35
|
|
|
3
36
|
|
package/README.md
CHANGED
|
@@ -1,221 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
# @pplancq/react-template
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This package is a ready-to-use template for starting up a new React application. It has been designed for easy use via the `@pplancq/create-react-app` script.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Usage
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- npm (Node Package Manager)
|
|
7
|
+
To create a new React project, use the following command:
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
1. Clone this repository to your computer.
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
git clone https://github.com/votre-utilisateur/mon-projet-awesome.git mon-projet
|
|
16
|
-
cd mon-projet
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
2. Install dependencies.
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npm install
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
In the project directory, you can run the following commands:
|
|
27
|
-
|
|
28
|
-
### `npm start`
|
|
29
|
-
|
|
30
|
-
Launches the application in development mode.
|
|
31
|
-
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
|
32
|
-
|
|
33
|
-
The page will reload automatically if you make edits.
|
|
34
|
-
|
|
35
|
-
You will also see lint errors in the console.
|
|
36
|
-
|
|
37
|
-
### `npm test`
|
|
38
|
-
|
|
39
|
-
Launches the test runner in interactive mode.
|
|
40
|
-
|
|
41
|
-
### `npm run build`
|
|
42
|
-
|
|
43
|
-
Builds the application for production in the `build` folder.
|
|
44
|
-
|
|
45
|
-
It correctly bundles React in production mode and optimizes the build for better performance.
|
|
46
|
-
|
|
47
|
-
The build is minified, and the filenames include hashes.
|
|
48
|
-
Your application is ready to be deployed!
|
|
49
|
-
|
|
50
|
-
### `npm run lint`
|
|
51
|
-
|
|
52
|
-
Allows you to see lint errors without fixing them. This command initiates the linting process with three linters: **eslint**, **stylelint**, and **tsc** (TypeScript Compiler).
|
|
53
|
-
|
|
54
|
-
### `npm run eslint:fix`
|
|
55
|
-
|
|
56
|
-
Allows you to fix eslint errors.
|
|
57
|
-
|
|
58
|
-
### `npm run stylelint:fix`
|
|
59
|
-
|
|
60
|
-
Allows you to fix style lint errors.
|
|
61
|
-
|
|
62
|
-
## 🏗 <span id="project-structure">Project Structure</span>
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
📁 my-projet
|
|
66
|
-
├── 📁 src
|
|
67
|
-
│ ├── 📁 ui
|
|
68
|
-
│ │ ├── 📁 Atoms
|
|
69
|
-
│ │ ├── 📁 Molecules
|
|
70
|
-
│ │ ├── 📁 Organisms
|
|
71
|
-
│ │ ├── 📁 Templates
|
|
72
|
-
│ ├── 📁 components
|
|
73
|
-
│ │ ├── 📁 formFields
|
|
74
|
-
│ ├── 📁 providers
|
|
75
|
-
│ ├── 📁 pages
|
|
76
|
-
│ │ ├── 📁 homePage
|
|
77
|
-
│ │ ├── 📁 page1
|
|
78
|
-
│ │ │ ├── 📁 sousPage1
|
|
79
|
-
│ ├── 📁 forms
|
|
80
|
-
│ │ ├── 📁 hooks
|
|
81
|
-
│ │ ├── 📁 risk
|
|
82
|
-
│ │ ├── 📁 contact
|
|
83
|
-
│ ├── 📁 hooks
|
|
84
|
-
│ │ ├── 📁 api
|
|
85
|
-
│ │ ├── 📁 useCustom1
|
|
86
|
-
│ │ ├── 📁 useCustom2
|
|
87
|
-
│ ├── 📁 utils
|
|
88
|
-
│ │ ├── 📁 services
|
|
89
|
-
│ │ ├── 📁 tests
|
|
90
|
-
│ │ ├── 📁 helpers
|
|
91
|
-
│ ├── 📁 routing
|
|
92
|
-
│ ├── 📁 api
|
|
93
|
-
│ ├── 📁 types
|
|
94
|
-
│ ├── 📁 assets
|
|
95
|
-
│ ├── 📁 config
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 📚 Folder Definitions
|
|
99
|
-
|
|
100
|
-
Here is the project folder structure, with a brief description of each folder:
|
|
101
|
-
|
|
102
|
-
| Folder | Description|
|
|
103
|
-
| -------- | -------- |
|
|
104
|
-
| **📁 src** | ***The root directory of the application source code.***
|
|
105
|
-
| [**📁 ui**](./src/ui/README.md) | Contains reusable components designed to be used across projects. Components can be basic (Atoms), more complex (Molecules), higher-level components (Organisms), or page templates (Templates). |
|
|
106
|
-
| [**📁 components**](./src/components/README.md) | Contains project-specific reusable components, for example, form components using React Hook Form. |
|
|
107
|
-
| [**📁 providers**](./src/providers/README.md) | This folder contains context providers or custom hooks that provide data to the entire application. |
|
|
108
|
-
| [**📁 pages**](./src/pages/README.md) | Contains the pages of the application. Each subfolder represents a distinct page or view of the application. |
|
|
109
|
-
| [**📁 forms**](./src/forms/README.md) | Contains the forms of the application, grouping hooks related to forms. |
|
|
110
|
-
| [**📁 hooks**](./src/hooks/README.md) | Includes custom hooks for various application features, such as API calls with React Query. |
|
|
111
|
-
| [**📁 utils**](./src/utils/README.md) | Contains utilities and services such as test files, utility functions, etc. |
|
|
112
|
-
| [**📁 routing**](./src/routing/README.md) | This folder is intended for native API calls using the `fetch`. function. These calls are essential for fetching real-time data from external sources, such as remote servers or web services. |
|
|
113
|
-
| [**📁 types**](./src/types/README.md) | Provides TypeScript type definitions to enhance the robustness of your code. |
|
|
114
|
-
| [**📁 assets**](./src/assets/README.md) | Contains static files such as images, fonts, etc., used in the application. |
|
|
115
|
-
| [**📁 config**](./src/config/README.md) | Contains all the important configurations and utilities needed for our project. |
|
|
116
|
-
|
|
117
|
-
## 🏗 <span id="component-structure">React Component Structure</span>
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
📁 ComponentName
|
|
121
|
-
├── 📁 __tests__
|
|
122
|
-
│ ├── 📄 ComponentName.feature
|
|
123
|
-
│ ├── 📄 ComponantName.steps.tsx
|
|
124
|
-
│ ├── 📄 ComponentName.test.tsx
|
|
125
|
-
├── 📄 index.ts
|
|
126
|
-
├── 📄 ComponentName.tsx
|
|
127
|
-
├── 📄 ComponentName.hook.ts
|
|
128
|
-
├── 📄 ComponentName.module.scss
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### 📚 Files Definitions
|
|
132
|
-
|
|
133
|
-
#### 📁 **tests**
|
|
134
|
-
|
|
135
|
-
The tests folder may contain unit tests specific to the package. The idea is to mainly test through functional tests. However, in some cases, it may be useful to test a component in isolation (for example, when developing a package reused between multiple projects).
|
|
136
|
-
|
|
137
|
-
#### 📄 index.ts
|
|
138
|
-
|
|
139
|
-
This file allows exposing the component and avoids having to redo imports if the file implementing the component changes its name.
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
export { ComponentName } from './ComponentName';
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
This will allow importing a component like this:
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
import { ComponentName } from '@Front/ComponentName';
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
#### 📄 ComponentName.tsx
|
|
152
|
-
|
|
153
|
-
Each file contains a single exported component. It will essentially contain the view and very little logic. If the component needs more logic, it is advisable to use a custom hook.
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
type ComponentNameProps = {
|
|
157
|
-
foo: string;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export const ComponentName = ({ foo }) => {
|
|
161
|
-
return (
|
|
162
|
-
<div>{foo}</div>
|
|
163
|
-
);
|
|
164
|
-
};
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
#### 📄 ComponentName.hook.ts
|
|
168
|
-
|
|
169
|
-
A custom hook allows moving the logic specific to a component outside of its view.
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
export type UseComponentNameProps = {
|
|
173
|
-
foo: string;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
export type UseComponentNameReturn = {
|
|
177
|
-
bar: string;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export const useComponentName = ({ foo }: UseComponentNameProps): UseComponentNameReturn => {
|
|
181
|
-
return {
|
|
182
|
-
bar: foo,
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
#### 📄 ComponentName.module.scss
|
|
189
|
-
|
|
190
|
-
The style specific to the component will be written in module form. This allows scoping the style of the component without overriding global style.
|
|
191
|
-
|
|
192
|
-
```css
|
|
193
|
-
.root {
|
|
194
|
-
background-color: red;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
import classes from './ComponentName.module.scss'
|
|
201
|
-
|
|
202
|
-
export const ComponentName = () => {
|
|
203
|
-
return (
|
|
204
|
-
<div className={classes.root}>Foo</div>
|
|
205
|
-
);
|
|
206
|
-
};
|
|
9
|
+
```shell
|
|
10
|
+
npm create @pplancq/react-app@latest <project_name>
|
|
207
11
|
```
|
|
208
|
-
### ❗ Naming Conventions
|
|
209
12
|
|
|
210
|
-
|
|
13
|
+
Replace <project_name> with the name of your new project.
|
|
211
14
|
|
|
212
|
-
##
|
|
15
|
+
## License
|
|
213
16
|
|
|
214
|
-
|
|
215
|
-
- [React Documentation.](https://react.dev/).
|
|
216
|
-
- [React Router Documentation.](https://reactrouter.com/en/main).
|
|
217
|
-
- [React Query Documentation.](https://tanstack.com/query/v3/).
|
|
218
|
-
- [React Hook Form Documentation.](https://react-hook-form.com/).
|
|
219
|
-
- Test
|
|
220
|
-
- [Vitest Documentation.](https://vitest.dev/).
|
|
221
|
-
- [Testing Library Documentation.](https://testing-library.com/).
|
|
17
|
+
This project is under the MIT license.
|
package/_README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
## 🚀 <span id="getting-started">Getting Started</span>
|
|
2
|
+
|
|
3
|
+
This project was bootstrapped with [@pplancq/create-react-app](https://github.com/pplancq/dev-tools/tree/main/packages/create-react-app).
|
|
4
|
+
|
|
5
|
+
### 🧾 Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js : [Download here](https://nodejs.org/) (Preferably, use [Volta](https://volta.sh/))
|
|
8
|
+
- npm (Node Package Manager)
|
|
9
|
+
|
|
10
|
+
### 🛠️ Installation
|
|
11
|
+
|
|
12
|
+
1. Clone this repository to your computer.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/votre-utilisateur/mon-projet-awesome.git mon-projet
|
|
16
|
+
cd mon-projet
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. Install dependencies.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
In the project directory, you can run the following commands:
|
|
27
|
+
|
|
28
|
+
### `npm start`
|
|
29
|
+
|
|
30
|
+
Launches the application in development mode.
|
|
31
|
+
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
|
32
|
+
|
|
33
|
+
The page will reload automatically if you make edits.
|
|
34
|
+
|
|
35
|
+
You will also see lint errors in the console.
|
|
36
|
+
|
|
37
|
+
### `npm test`
|
|
38
|
+
|
|
39
|
+
Launches the test runner in interactive mode.
|
|
40
|
+
|
|
41
|
+
### `npm run build`
|
|
42
|
+
|
|
43
|
+
Builds the application for production in the `build` folder.
|
|
44
|
+
|
|
45
|
+
It correctly bundles React in production mode and optimizes the build for better performance.
|
|
46
|
+
|
|
47
|
+
The build is minified, and the filenames include hashes.
|
|
48
|
+
Your application is ready to be deployed!
|
|
49
|
+
|
|
50
|
+
### `npm run lint`
|
|
51
|
+
|
|
52
|
+
Allows you to see lint errors without fixing them. This command initiates the linting process with three linters: **eslint**, **stylelint**, and **tsc** (TypeScript Compiler).
|
|
53
|
+
|
|
54
|
+
### `npm run eslint:fix`
|
|
55
|
+
|
|
56
|
+
Allows you to fix eslint errors.
|
|
57
|
+
|
|
58
|
+
### `npm run stylelint:fix`
|
|
59
|
+
|
|
60
|
+
Allows you to fix style lint errors.
|
|
61
|
+
|
|
62
|
+
### `npm run remove:demo`
|
|
63
|
+
|
|
64
|
+
To remove the demo application.
|
|
65
|
+
|
|
66
|
+
### `npm run migrate:vite`
|
|
67
|
+
|
|
68
|
+
To migrate from webpack to vite.
|
|
69
|
+
|
|
70
|
+
## 🏗 <span id="project-structure">Project Structure</span>
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
📁 my-projet
|
|
74
|
+
├── 📁 src
|
|
75
|
+
│ ├── 📁 ui
|
|
76
|
+
│ │ ├── 📁 Atoms
|
|
77
|
+
│ │ ├── 📁 Molecules
|
|
78
|
+
│ │ ├── 📁 Organisms
|
|
79
|
+
│ │ ├── 📁 Templates
|
|
80
|
+
│ ├── 📁 components
|
|
81
|
+
│ │ ├── 📁 formFields
|
|
82
|
+
│ ├── 📁 providers
|
|
83
|
+
│ ├── 📁 pages
|
|
84
|
+
│ │ ├── 📁 homePage
|
|
85
|
+
│ │ ├── 📁 page1
|
|
86
|
+
│ │ │ ├── 📁 sousPage1
|
|
87
|
+
│ ├── 📁 forms
|
|
88
|
+
│ │ ├── 📁 hooks
|
|
89
|
+
│ │ ├── 📁 risk
|
|
90
|
+
│ │ ├── 📁 contact
|
|
91
|
+
│ ├── 📁 hooks
|
|
92
|
+
│ │ ├── 📁 api
|
|
93
|
+
│ │ ├── 📁 useCustom1
|
|
94
|
+
│ │ ├── 📁 useCustom2
|
|
95
|
+
│ ├── 📁 utils
|
|
96
|
+
│ │ ├── 📁 services
|
|
97
|
+
│ │ ├── 📁 tests
|
|
98
|
+
│ │ ├── 📁 helpers
|
|
99
|
+
│ ├── 📁 routing
|
|
100
|
+
│ ├── 📁 api
|
|
101
|
+
│ ├── 📁 types
|
|
102
|
+
│ ├── 📁 assets
|
|
103
|
+
│ ├── 📁 config
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 📚 Folder Definitions
|
|
107
|
+
|
|
108
|
+
Here is the project folder structure, with a brief description of each folder:
|
|
109
|
+
|
|
110
|
+
| Folder | Description|
|
|
111
|
+
| -------- | -------- |
|
|
112
|
+
| **📁 src** | ***The root directory of the application source code.***
|
|
113
|
+
| [**📁 ui**](./src/ui/README.md) | Contains reusable components designed to be used across projects. Components can be basic (Atoms), more complex (Molecules), higher-level components (Organisms), or page templates (Templates). |
|
|
114
|
+
| [**📁 components**](./src/components/README.md) | Contains project-specific reusable components, for example, form components using React Hook Form. |
|
|
115
|
+
| [**📁 providers**](./src/providers/README.md) | This folder contains context providers or custom hooks that provide data to the entire application. |
|
|
116
|
+
| [**📁 pages**](./src/pages/README.md) | Contains the pages of the application. Each subfolder represents a distinct page or view of the application. |
|
|
117
|
+
| [**📁 forms**](./src/forms/README.md) | Contains the forms of the application, grouping hooks related to forms. |
|
|
118
|
+
| [**📁 hooks**](./src/hooks/README.md) | Includes custom hooks for various application features, such as API calls with React Query. |
|
|
119
|
+
| [**📁 utils**](./src/utils/README.md) | Contains utilities and services such as test files, utility functions, etc. |
|
|
120
|
+
| [**📁 routing**](./src/routing/README.md) | This folder is intended for native API calls using the `fetch`. function. These calls are essential for fetching real-time data from external sources, such as remote servers or web services. |
|
|
121
|
+
| [**📁 types**](./src/types/README.md) | Provides TypeScript type definitions to enhance the robustness of your code. |
|
|
122
|
+
| [**📁 assets**](./src/assets/README.md) | Contains static files such as images, fonts, etc., used in the application. |
|
|
123
|
+
| [**📁 config**](./src/config/README.md) | Contains all the important configurations and utilities needed for our project. |
|
|
124
|
+
|
|
125
|
+
## 🏗 <span id="component-structure">React Component Structure</span>
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
📁 ComponentName
|
|
129
|
+
├── 📁 __tests__
|
|
130
|
+
│ ├── 📄 ComponentName.feature
|
|
131
|
+
│ ├── 📄 ComponantName.steps.tsx
|
|
132
|
+
│ ├── 📄 ComponentName.test.tsx
|
|
133
|
+
├── 📄 index.ts
|
|
134
|
+
├── 📄 ComponentName.tsx
|
|
135
|
+
├── 📄 ComponentName.hook.ts
|
|
136
|
+
├── 📄 ComponentName.module.scss
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 📚 Files Definitions
|
|
140
|
+
|
|
141
|
+
#### 📁 **tests**
|
|
142
|
+
|
|
143
|
+
The tests folder may contain unit tests specific to the package. The idea is to mainly test through functional tests. However, in some cases, it may be useful to test a component in isolation (for example, when developing a package reused between multiple projects).
|
|
144
|
+
|
|
145
|
+
#### 📄 index.ts
|
|
146
|
+
|
|
147
|
+
This file allows exposing the component and avoids having to redo imports if the file implementing the component changes its name.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
export { ComponentName } from './ComponentName';
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This will allow importing a component like this:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { ComponentName } from '@Front/ComponentName';
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### 📄 ComponentName.tsx
|
|
160
|
+
|
|
161
|
+
Each file contains a single exported component. It will essentially contain the view and very little logic. If the component needs more logic, it is advisable to use a custom hook.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
type ComponentNameProps = {
|
|
165
|
+
foo: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const ComponentName = ({ foo }) => {
|
|
169
|
+
return (
|
|
170
|
+
<div>{foo}</div>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### 📄 ComponentName.hook.ts
|
|
176
|
+
|
|
177
|
+
A custom hook allows moving the logic specific to a component outside of its view.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
export type UseComponentNameProps = {
|
|
181
|
+
foo: string;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export type UseComponentNameReturn = {
|
|
185
|
+
bar: string;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const useComponentName = ({ foo }: UseComponentNameProps): UseComponentNameReturn => {
|
|
189
|
+
return {
|
|
190
|
+
bar: foo,
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### 📄 ComponentName.module.scss
|
|
197
|
+
|
|
198
|
+
The style specific to the component will be written in module form. This allows scoping the style of the component without overriding global style.
|
|
199
|
+
|
|
200
|
+
```css
|
|
201
|
+
.root {
|
|
202
|
+
background-color: red;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import classes from './ComponentName.module.scss'
|
|
209
|
+
|
|
210
|
+
export const ComponentName = () => {
|
|
211
|
+
return (
|
|
212
|
+
<div className={classes.root}>Foo</div>
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
### ❗ Naming Conventions
|
|
217
|
+
|
|
218
|
+
==> Component names, file names, and folder names should follow the **PascalCase** convention.
|
|
219
|
+
|
|
220
|
+
## ⚠️ Caution with Environment Variables
|
|
221
|
+
|
|
222
|
+
The webpack configuration of this project allows for environment variables to be included in the application bundle. This could potentially expose sensitive information if the bundle is publicly accessible.
|
|
223
|
+
|
|
224
|
+
It is recommended to only include non-sensitive environment variables in the application bundle. Sensitive information should not be exposed even if the bundle is publicly accessible.
|
|
225
|
+
|
|
226
|
+
Ensure you understand the security implications before including environment variables in your bundle.
|
|
227
|
+
|
|
228
|
+
Please note that the web configuration pre-filters environment variables via the default prefix ‘FRONT_’. This means that only environment variables starting with ‘FRONT_’ will be included in the bundle. This is an additional layer of security to prevent the accidental exposure of sensitive environment variables.
|
|
229
|
+
|
|
230
|
+
## 🙇 <span id="learnmore">Learn More</span>
|
|
231
|
+
|
|
232
|
+
- React & co
|
|
233
|
+
- [React Documentation.](https://react.dev/).
|
|
234
|
+
- [React Router Documentation.](https://reactrouter.com/en/main).
|
|
235
|
+
- [React Query Documentation.](https://tanstack.com/query/v3/).
|
|
236
|
+
- [React Hook Form Documentation.](https://react-hook-form.com/).
|
|
237
|
+
- Test
|
|
238
|
+
- [Vitest Documentation.](https://vitest.dev/).
|
|
239
|
+
- [Testing Library Documentation.](https://testing-library.com/).
|
package/_gitignore
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pplancq/react-template",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "react template",
|
|
6
6
|
"author": "pplancq <paul.plancq@outlook.fr>",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"stylelint": "stylelint \"src/**/*.{scss,css}\"",
|
|
21
21
|
"stylelint:fix": "stylelint \"src**/*.{scss,css}\" --fix",
|
|
22
22
|
"tsc": "tsc --noEmit",
|
|
23
|
+
"package:check": "npm exec --yes package-lock-utd@1.1.3",
|
|
24
|
+
"remove:demo": "./scripts/removeDemo.js",
|
|
25
|
+
"migrate:vite": "./scripts/migrateToVite.js",
|
|
23
26
|
"_prepare": "husky"
|
|
24
27
|
},
|
|
25
28
|
"bugs": {
|
|
@@ -31,39 +34,48 @@
|
|
|
31
34
|
],
|
|
32
35
|
"dependencies": {
|
|
33
36
|
"@hookform/resolvers": "^3.3.2",
|
|
37
|
+
"@tanstack/react-query": "^5.25.0",
|
|
38
|
+
"@tanstack/react-query-devtools": "^5.25.0",
|
|
34
39
|
"react": "^18.2.0",
|
|
35
40
|
"react-dom": "^18.2.0",
|
|
36
|
-
"react-hook-form": "^7.
|
|
37
|
-
"react-
|
|
38
|
-
"
|
|
39
|
-
"yup": "^1.3.3"
|
|
41
|
+
"react-hook-form": "^7.51.0",
|
|
42
|
+
"react-router-dom": "^6.22.3",
|
|
43
|
+
"yup": "^1.4.0"
|
|
40
44
|
},
|
|
41
45
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@pplancq/
|
|
44
|
-
"@pplancq/
|
|
45
|
-
"@pplancq/
|
|
46
|
-
"@pplancq/
|
|
47
|
-
"@pplancq/
|
|
48
|
-
"@pplancq/
|
|
49
|
-
"@
|
|
46
|
+
"@commitlint/cli": "^19.0.3",
|
|
47
|
+
"@pplancq/babel-config": "^1.0.0",
|
|
48
|
+
"@pplancq/commitlint-config": "*",
|
|
49
|
+
"@pplancq/eslint-config": "2.0.0",
|
|
50
|
+
"@pplancq/postcss-config": "^1.0.0",
|
|
51
|
+
"@pplancq/prettier-config": "^1.0.0",
|
|
52
|
+
"@pplancq/stylelint-config": "2.0.0",
|
|
53
|
+
"@pplancq/webpack-config": "^1.0.0",
|
|
50
54
|
"@testing-library/jest-dom": "^6.1.5",
|
|
51
55
|
"@testing-library/react": "^14.1.2",
|
|
52
56
|
"@testing-library/user-event": "^14.5.1",
|
|
53
|
-
"@types/react": "^18.2.
|
|
54
|
-
"@types/react-dom": "^18.2.
|
|
57
|
+
"@types/react": "^18.2.64",
|
|
58
|
+
"@types/react-dom": "^18.2.21",
|
|
55
59
|
"@vitejs/plugin-react": "^4.2.1",
|
|
56
|
-
"@vitest/coverage-v8": "^1.
|
|
60
|
+
"@vitest/coverage-v8": "^1.3.1",
|
|
57
61
|
"concurrently": "^8.2.2",
|
|
58
|
-
"
|
|
62
|
+
"eslint": "^8.57.0",
|
|
63
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
64
|
+
"husky": "^9.0.11",
|
|
59
65
|
"jsdom": "^24.0.0",
|
|
60
66
|
"lint-staged": "^15.2.0",
|
|
67
|
+
"prettier": "^3.2.5",
|
|
68
|
+
"stylelint": "^16.2.1",
|
|
69
|
+
"stylelint-prettier": "^5.0.0",
|
|
61
70
|
"tsc-files": "^1.1.4",
|
|
62
|
-
"typescript": "^5.
|
|
71
|
+
"typescript": "^5.4.2",
|
|
63
72
|
"vite-plugin-svgr": "^4.2.0",
|
|
64
73
|
"vite-tsconfig-paths": "^4.2.2",
|
|
65
74
|
"vitest": "^1.0.4",
|
|
66
|
-
"vitest-sonar-reporter": "^
|
|
75
|
+
"vitest-sonar-reporter": "^2.0.0",
|
|
76
|
+
"webpack": "^5.90.3",
|
|
77
|
+
"webpack-cli": "^5.1.4",
|
|
78
|
+
"webpack-dev-server": "^5.0.2"
|
|
67
79
|
},
|
|
68
80
|
"volta": {
|
|
69
81
|
"node": "20.10.0",
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
const { rmSync, writeFileSync, readFileSync } = require('fs');
|
|
4
|
+
const { resolve } = require('path');
|
|
5
|
+
|
|
6
|
+
const NPM = 'npm';
|
|
7
|
+
const YARN = 'yarn';
|
|
8
|
+
const PNPM = 'pnpm';
|
|
9
|
+
|
|
10
|
+
const runCommand = (command, options = { stdio: 'inherit' }) => {
|
|
11
|
+
try {
|
|
12
|
+
execSync(command, options);
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error(`Failed to execute ${command}`, e);
|
|
15
|
+
process.exit(-1);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getPackageManager = () => {
|
|
20
|
+
switch (true) {
|
|
21
|
+
case process.env.npm_config_user_agent.includes(YARN):
|
|
22
|
+
return YARN;
|
|
23
|
+
case process.env.npm_config_user_agent.includes(PNPM):
|
|
24
|
+
return PNPM;
|
|
25
|
+
default:
|
|
26
|
+
return NPM;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const packageManager = getPackageManager();
|
|
31
|
+
|
|
32
|
+
console.info('\nremove webpack ...');
|
|
33
|
+
runCommand(`${packageManager} remove @pplancq/webpack-config webpack webpack-cli webpack-dev-server`);
|
|
34
|
+
rmSync(resolve(__dirname, '../webpack.config.js'));
|
|
35
|
+
console.info('\ninstall vite package ...');
|
|
36
|
+
runCommand(
|
|
37
|
+
`${packageManager} ${packageManager === YARN ? 'add' : 'install'} --save-dev vite vite-plugin-eslint2 vite-plugin-stylelint`,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
writeFileSync(
|
|
41
|
+
resolve(__dirname, '../vite.config.mts'),
|
|
42
|
+
`import react from '@vitejs/plugin-react'
|
|
43
|
+
import { resolve } from 'path'
|
|
44
|
+
import { defineConfig, loadEnv } from 'vite'
|
|
45
|
+
import eslintPlugin from 'vite-plugin-eslint2'
|
|
46
|
+
import stylelintPlugin from 'vite-plugin-stylelint'
|
|
47
|
+
import svgr from 'vite-plugin-svgr'
|
|
48
|
+
import viteTsconfigPaths from 'vite-tsconfig-paths'
|
|
49
|
+
|
|
50
|
+
export default defineConfig(({ mode }) => {
|
|
51
|
+
const env = loadEnv(mode, process.cwd(), '')
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
plugins: [
|
|
55
|
+
react(),
|
|
56
|
+
viteTsconfigPaths(),
|
|
57
|
+
svgr(),
|
|
58
|
+
eslintPlugin({
|
|
59
|
+
useEslintrc: true,
|
|
60
|
+
emitErrorAsWarning: true,
|
|
61
|
+
cache: false
|
|
62
|
+
}),
|
|
63
|
+
stylelintPlugin({
|
|
64
|
+
emitErrorAsWarning: true,
|
|
65
|
+
cache: false
|
|
66
|
+
})
|
|
67
|
+
],
|
|
68
|
+
envPrefix: env.ENV_PREFIX ?? 'FRONT_',
|
|
69
|
+
server: {
|
|
70
|
+
port: parseInt(process.env.PORT ?? '3000', 10),
|
|
71
|
+
open: (process.env.BROWSER ?? 'false') === 'true'
|
|
72
|
+
},
|
|
73
|
+
root: resolve(__dirname, 'public'),
|
|
74
|
+
publicDir: resolve(__dirname, 'public'),
|
|
75
|
+
resolve: {
|
|
76
|
+
alias: { '/src': resolve(__dirname, 'src') }
|
|
77
|
+
},
|
|
78
|
+
build: {
|
|
79
|
+
outDir: resolve(__dirname, 'build')
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
`,
|
|
84
|
+
{ encoding: 'utf-8' },
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const packagesJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), { encoding: 'utf-8' }));
|
|
88
|
+
const { start, build, 'migrate:vite': _, ...scripts } = packagesJson.scripts;
|
|
89
|
+
packagesJson.scripts = { start: 'vite', build: 'vite build', preview: 'vite preview', ...scripts };
|
|
90
|
+
writeFileSync(resolve(__dirname, '../package.json'), JSON.stringify(packagesJson, null, 2), { encoding: 'utf-8' });
|
|
91
|
+
|
|
92
|
+
let indexHTML = readFileSync(resolve(__dirname, '../public/index.html'), { encoding: 'utf-8' });
|
|
93
|
+
indexHTML = indexHTML.replace(' </body>', ' <script type="module" src="/src/main.ts"></script>\n </body>');
|
|
94
|
+
writeFileSync(resolve(__dirname, '../public/index.html'), indexHTML, { encoding: 'utf-8' });
|
|
95
|
+
|
|
96
|
+
rmSync(resolve(__dirname, '../scripts/migrateToVite.js'));
|
|
97
|
+
|
|
98
|
+
console.info('\nThe application has been migrate to vite');
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { rmSync, readFileSync, writeFileSync } = require('fs');
|
|
3
|
+
const { resolve } = require('path');
|
|
4
|
+
|
|
5
|
+
const resolveSrc = path => resolve(__dirname, `../src${path}`);
|
|
6
|
+
|
|
7
|
+
rmSync(resolveSrc('/api/demoApi.ts'));
|
|
8
|
+
|
|
9
|
+
rmSync(resolveSrc('/assets/css/mainBody.css'));
|
|
10
|
+
rmSync(resolveSrc('/assets/css/reset.css'));
|
|
11
|
+
writeFileSync(resolveSrc('/assets/css/global.css'), ':root {\n --white-color: #fff;\n}\n', { encoding: 'utf-8' });
|
|
12
|
+
let assetsIndex = readFileSync(resolveSrc('/assets/css/index.ts'), { encoding: 'utf-8' });
|
|
13
|
+
assetsIndex = assetsIndex.replaceAll("import './reset.css';\nimport './mainBody.css';\n", "import './global.css';\n");
|
|
14
|
+
writeFileSync(resolveSrc('/assets/css/index.ts'), assetsIndex, { encoding: 'utf-8' });
|
|
15
|
+
|
|
16
|
+
rmSync(resolveSrc('/assets/images/logo.png'));
|
|
17
|
+
rmSync(resolveSrc('/assets/images/templatePlugins.png'));
|
|
18
|
+
|
|
19
|
+
rmSync(resolveSrc('/components/Footer'), { recursive: true });
|
|
20
|
+
rmSync(resolveSrc('/components/Header'), { recursive: true });
|
|
21
|
+
rmSync(resolveSrc('/components/TextInput'), { recursive: true });
|
|
22
|
+
|
|
23
|
+
rmSync(resolveSrc('/forms/ProfileForm'), { recursive: true });
|
|
24
|
+
|
|
25
|
+
rmSync(resolveSrc('/hooks/api/useDemoApi.ts'));
|
|
26
|
+
let queryKey = readFileSync(resolveSrc('/hooks/api/queryKey.ts'), { encoding: 'utf-8' });
|
|
27
|
+
queryKey = queryKey.replaceAll("export const demoQuery = () => ['dataWithDelay'];\n", '');
|
|
28
|
+
writeFileSync(resolveSrc('/hooks/api/queryKey.ts'), queryKey, { encoding: 'utf-8' });
|
|
29
|
+
|
|
30
|
+
rmSync(resolveSrc('/pages/Demo'), { recursive: true });
|
|
31
|
+
rmSync(resolveSrc('/pages/Layout'), { recursive: true });
|
|
32
|
+
rmSync(resolveSrc('/pages/UnexpectedError'), { recursive: true });
|
|
33
|
+
writeFileSync(resolveSrc('/pages/Home/index.ts'), "export { homeRoutes } from './routes';\n", { encoding: 'utf-8' });
|
|
34
|
+
writeFileSync(
|
|
35
|
+
resolveSrc('/pages/Home/routes.tsx'),
|
|
36
|
+
"import type { RouteObject } from 'react-router-dom';\n" +
|
|
37
|
+
"import { Home } from './Home';\n" +
|
|
38
|
+
'\n' +
|
|
39
|
+
'export const homeRoutes: RouteObject = {\n' +
|
|
40
|
+
' index: true,\n' +
|
|
41
|
+
' element: <Home />,\n' +
|
|
42
|
+
'};\n',
|
|
43
|
+
{ encoding: 'utf-8' },
|
|
44
|
+
);
|
|
45
|
+
writeFileSync(resolveSrc('/pages/Home/Home.tsx'), 'export const Home = () => {\n return <div>HomePage</div>;\n};\n', {
|
|
46
|
+
encoding: 'utf-8',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let appRoutes = readFileSync(resolveSrc('/routing/appRoutes.ts'), { encoding: 'utf-8' });
|
|
50
|
+
appRoutes = appRoutes.replaceAll(
|
|
51
|
+
"\n reactQueryDemo: '/reactQueryDemo',\n reactHookFormDemo: '/reactHookFormDemo',",
|
|
52
|
+
'',
|
|
53
|
+
);
|
|
54
|
+
writeFileSync(resolveSrc('/routing/appRoutes.ts'), appRoutes, { encoding: 'utf-8' });
|
|
55
|
+
let routes = readFileSync(resolveSrc('/routing/routes.tsx'), { encoding: 'utf-8' });
|
|
56
|
+
routes = routes.replaceAll(
|
|
57
|
+
"import { demoRoutes } from '@Front/pages/Demo';",
|
|
58
|
+
"import { homeRoutes } from '@Front/pages/Home';",
|
|
59
|
+
);
|
|
60
|
+
routes = routes.replaceAll("\nimport { Layout } from '@Front/pages/Layout';", '');
|
|
61
|
+
routes = routes.replaceAll("\nimport { UnexpectedError } from 'src/pages/UnexpectedError';", '');
|
|
62
|
+
routes = routes.replaceAll(
|
|
63
|
+
'element: <Layout />,\n children: [demoRoutes],\n errorElement: <UnexpectedError />,',
|
|
64
|
+
'children: [homeRoutes],',
|
|
65
|
+
);
|
|
66
|
+
writeFileSync(resolveSrc('/routing/routes.tsx'), routes, { encoding: 'utf-8' });
|
|
67
|
+
|
|
68
|
+
rmSync(resolveSrc('/types/demoApi.ts'));
|
|
69
|
+
rmSync(resolveSrc('/types/profileTypes.ts'));
|
|
70
|
+
|
|
71
|
+
rmSync(resolveSrc('/ui/atoms/Input'), { recursive: true });
|
|
72
|
+
rmSync(resolveSrc('/ui/atoms/Logo'), { recursive: true });
|
|
73
|
+
rmSync(resolveSrc('/ui/atoms/NavLink'), { recursive: true });
|
|
74
|
+
rmSync(resolveSrc('/ui/molecules/Navigation'), { recursive: true });
|
|
75
|
+
rmSync(resolveSrc('/ui/organisms/Footer'), { recursive: true });
|
|
76
|
+
rmSync(resolveSrc('/ui/organisms/Header'), { recursive: true });
|
|
77
|
+
rmSync(resolveSrc('/ui/organisms/MainContent'), { recursive: true });
|
|
78
|
+
rmSync(resolveSrc('/ui/templates/MainTemplate'), { recursive: true });
|
|
79
|
+
|
|
80
|
+
const packagesJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), { encoding: 'utf-8' }));
|
|
81
|
+
const { 'remove:demo': _, ...scripts } = packagesJson.scripts;
|
|
82
|
+
packagesJson.scripts = { ...scripts };
|
|
83
|
+
writeFileSync(resolve(__dirname, '../package.json'), JSON.stringify(packagesJson, null, 2), { encoding: 'utf-8' });
|
|
84
|
+
|
|
85
|
+
rmSync(resolve(__dirname, '../scripts/removeDemo.js'));
|
|
86
|
+
|
|
87
|
+
console.info('\nThe demo application has been removed\n');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export enum METHODS {
|
|
2
|
+
get = 'GET',
|
|
3
|
+
post = 'POST',
|
|
4
|
+
put = 'PUT',
|
|
5
|
+
patch = 'PATCH',
|
|
6
|
+
delete = 'DELETE',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export enum HEADERS {
|
|
10
|
+
contentType = 'content-type',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export enum MINE_TYPES {
|
|
14
|
+
json = 'application/json',
|
|
15
|
+
text = 'plain/text',
|
|
16
|
+
}
|
package/src/api/demoApi.ts
CHANGED
|
@@ -1,44 +1,10 @@
|
|
|
1
|
+
import { fetchApi } from '@Front/api/fetchApi';
|
|
1
2
|
import type { Users } from '@Front/types/demoApi';
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
{
|
|
5
|
-
|
|
6
|
-
firstName: 'John',
|
|
7
|
-
lastName: 'Doe',
|
|
8
|
-
age: 30,
|
|
9
|
-
email: 'john.doe@example.com',
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
id: 2,
|
|
13
|
-
firstName: 'Jane',
|
|
14
|
-
lastName: 'Smith',
|
|
15
|
-
age: 25,
|
|
16
|
-
email: 'jane.smith@example.com',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: 3,
|
|
20
|
-
firstName: 'Alice',
|
|
21
|
-
lastName: 'Johnson',
|
|
22
|
-
age: 28,
|
|
23
|
-
email: 'alice.johnson@example.com',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: 4,
|
|
27
|
-
firstName: 'Bob',
|
|
28
|
-
lastName: 'Brown',
|
|
29
|
-
age: 35,
|
|
30
|
-
email: 'bob.brown@example.com',
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
export const fetchDemoApi = async (): Promise<Users> => {
|
|
35
|
-
return new Promise<Users>((resolve, reject) => {
|
|
36
|
-
try {
|
|
37
|
-
setTimeout(() => {
|
|
38
|
-
resolve(data);
|
|
39
|
-
}, 3000);
|
|
40
|
-
} catch (error) {
|
|
41
|
-
reject(error);
|
|
42
|
-
}
|
|
4
|
+
export const fetchDemoApi = async () => {
|
|
5
|
+
const users = await fetchApi<Users>({
|
|
6
|
+
path: 'https://jsonplaceholder.typicode.com/users',
|
|
43
7
|
});
|
|
8
|
+
|
|
9
|
+
return users.map(user => ({ ...user, username: user.username.toUpperCase(), email: user.email.toLowerCase() }));
|
|
44
10
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Json } from '@Front/types/api';
|
|
2
|
+
import { HEADERS, METHODS, MINE_TYPES } from './constant';
|
|
3
|
+
|
|
4
|
+
export type FetchApiError = Error & {
|
|
5
|
+
code?: number;
|
|
6
|
+
contentType?: MINE_TYPES;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type FetchApiProps = {
|
|
10
|
+
path: string;
|
|
11
|
+
method?: METHODS;
|
|
12
|
+
data?: Json;
|
|
13
|
+
headers?: HeadersInit;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const fetchApi = async <T extends Json | string>({
|
|
17
|
+
path,
|
|
18
|
+
method = METHODS.get,
|
|
19
|
+
data,
|
|
20
|
+
headers = [],
|
|
21
|
+
}: FetchApiProps): Promise<T> => {
|
|
22
|
+
const mergeHeaders = new Headers(headers);
|
|
23
|
+
|
|
24
|
+
if (data) {
|
|
25
|
+
mergeHeaders.append(HEADERS.contentType, MINE_TYPES.json);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const response = await fetch(path, {
|
|
29
|
+
method,
|
|
30
|
+
...(data && { body: JSON.stringify(data) }),
|
|
31
|
+
headers: mergeHeaders,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const content = await response.text();
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const error: FetchApiError = new Error(content);
|
|
38
|
+
error.code = response.status;
|
|
39
|
+
error.contentType = (response.headers.get(HEADERS.contentType) as MINE_TYPES) ?? MINE_TYPES.text;
|
|
40
|
+
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if ((response.headers.get(HEADERS.contentType) ?? '').includes('json')) {
|
|
45
|
+
return JSON.parse(content) as T;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return content as T;
|
|
49
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fetchDemoApi } from '@Front/api/demoApi';
|
|
2
2
|
import type { Users } from '@Front/types/demoApi';
|
|
3
|
-
import { useQuery } from 'react-query';
|
|
3
|
+
import { useQuery } from '@tanstack/react-query';
|
|
4
4
|
import { demoQuery } from './queryKey';
|
|
5
5
|
|
|
6
6
|
type UseDemoApiReturn = {
|
|
@@ -10,7 +10,7 @@ type UseDemoApiReturn = {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export const useDemoApi = (): UseDemoApiReturn => {
|
|
13
|
-
const { data = [], isLoading, isError } = useQuery<Users>(demoQuery(), fetchDemoApi);
|
|
13
|
+
const { data = [], isLoading, isError } = useQuery<Users>({ queryKey: demoQuery(), queryFn: fetchDemoApi });
|
|
14
14
|
|
|
15
15
|
return { result: data, isLoading, isError };
|
|
16
16
|
};
|
package/src/pages/Demo/Demo.tsx
CHANGED
|
@@ -7,8 +7,7 @@ export const Demo = () => {
|
|
|
7
7
|
return (
|
|
8
8
|
<div className={classes.container}>
|
|
9
9
|
<div className={classes.bigTitleStyle}>
|
|
10
|
-
Welcome to Your React Starter Kit
|
|
11
|
-
<span>Welcome</span>
|
|
10
|
+
Welcome to Your React Starter Kit <span>Welcome</span>
|
|
12
11
|
</div>
|
|
13
12
|
|
|
14
13
|
<div className={classes.foo}>FlexBlug</div>
|
|
@@ -28,20 +28,20 @@ export const ReactQueryDemo = () => {
|
|
|
28
28
|
<thead>
|
|
29
29
|
<tr>
|
|
30
30
|
<th>ID</th>
|
|
31
|
-
<th>
|
|
32
|
-
<th>
|
|
33
|
-
<th>Age</th>
|
|
31
|
+
<th>Name</th>
|
|
32
|
+
<th>Username</th>
|
|
34
33
|
<th>Email</th>
|
|
34
|
+
<th>Website</th>
|
|
35
35
|
</tr>
|
|
36
36
|
</thead>
|
|
37
37
|
<tbody>
|
|
38
38
|
{result?.map(eachResult => (
|
|
39
39
|
<tr key={eachResult.id}>
|
|
40
40
|
<td>{eachResult.id}</td>
|
|
41
|
-
<td>{eachResult.
|
|
42
|
-
<td>{eachResult.
|
|
43
|
-
<td>{eachResult.age}</td>
|
|
41
|
+
<td>{eachResult.name}</td>
|
|
42
|
+
<td>{eachResult.username}</td>
|
|
44
43
|
<td>{eachResult.email}</td>
|
|
44
|
+
<td>{eachResult.website}</td>
|
|
45
45
|
</tr>
|
|
46
46
|
))}
|
|
47
47
|
</tbody>
|
|
@@ -2,13 +2,13 @@ import logo from '@Front/assets/images/logo.png';
|
|
|
2
2
|
import { Logo } from '@Front/ui/atoms/Logo';
|
|
3
3
|
import { Link, useRouteError } from 'react-router-dom';
|
|
4
4
|
|
|
5
|
-
import classes from './
|
|
5
|
+
import classes from './UnexpectedError.module.css';
|
|
6
6
|
|
|
7
|
-
export const
|
|
7
|
+
export const UnexpectedError = () => {
|
|
8
8
|
const error: unknown = useRouteError();
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
|
-
<div className={classes.
|
|
11
|
+
<div className={classes.root}>
|
|
12
12
|
<Link to="/">
|
|
13
13
|
<Logo src={logo} alt="logo" />
|
|
14
14
|
</Link>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { UnexpectedError } from './UnexpectedError';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { type QueryClient, QueryClientProvider as ReactQueryClientProvider } from '@tanstack/react-query';
|
|
2
|
+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|
1
3
|
import type { PropsWithChildren } from 'react';
|
|
2
|
-
import { type QueryClient, QueryClientProvider as ReactQueryClientProvider } from 'react-query';
|
|
3
|
-
import { ReactQueryDevtools } from 'react-query/devtools';
|
|
4
4
|
|
|
5
5
|
type ClientProviderProps = {
|
|
6
6
|
client: QueryClient;
|
package/src/routing/routes.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { demoRoutes } from '@Front/pages/Demo';
|
|
2
|
-
import { Error } from '@Front/pages/Error';
|
|
3
2
|
import { Layout } from '@Front/pages/Layout';
|
|
4
3
|
import type { RouteObject } from 'react-router-dom';
|
|
4
|
+
import { UnexpectedError } from 'src/pages/UnexpectedError';
|
|
5
5
|
|
|
6
6
|
export const routeObject: RouteObject[] = [
|
|
7
7
|
{
|
|
8
8
|
path: '/',
|
|
9
9
|
element: <Layout />,
|
|
10
10
|
children: [demoRoutes],
|
|
11
|
-
errorElement: <
|
|
11
|
+
errorElement: <UnexpectedError />,
|
|
12
12
|
},
|
|
13
13
|
];
|
package/src/types/api.ts
ADDED
package/src/types/demoApi.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
+
export type Address = {
|
|
2
|
+
street: string;
|
|
3
|
+
suite: string;
|
|
4
|
+
city: string;
|
|
5
|
+
zipcode: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type Company = {
|
|
9
|
+
name: string;
|
|
10
|
+
catchPhrase: string;
|
|
11
|
+
bs: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
1
14
|
export type User = {
|
|
2
15
|
id: number;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
age: number;
|
|
16
|
+
name: string;
|
|
17
|
+
username: string;
|
|
6
18
|
email: string;
|
|
19
|
+
address: Address;
|
|
20
|
+
phone: string;
|
|
21
|
+
website: string;
|
|
22
|
+
company: Company;
|
|
7
23
|
};
|
|
8
24
|
|
|
9
25
|
export type Users = User[];
|
package/src/pages/Error/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { Error } from './Error';
|