@yoann-86/react_test_lab 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/.github/actions/install-node/action.yml +25 -0
- package/.github/workflows/build_test_deploy_react.yml +114 -0
- package/README.md +105 -0
- package/TEST_PLAN.md +94 -0
- package/babel.config.js +6 -0
- package/cypress/e2e/app-spec.cy.js +182 -0
- package/cypress/e2e/navigation.cy.js +101 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/e2e.js +17 -0
- package/cypress.config.js +12 -0
- package/dist/App.css +108 -0
- package/dist/App.js +59 -0
- package/dist/App.test.js +225 -0
- package/dist/components/UserForm.js +188 -0
- package/dist/components/UserForm.test.js +291 -0
- package/dist/components/UsersList.js +38 -0
- package/dist/components/UsersList.test.js +134 -0
- package/dist/components/atomic/TextInput.js +42 -0
- package/dist/index.css +13 -0
- package/dist/index.js +23 -0
- package/dist/infra/api.js +27 -0
- package/dist/infra/api.test.js +54 -0
- package/dist/logo.svg +1 -0
- package/dist/reportWebVitals.js +26 -0
- package/dist/setupTests.js +3 -0
- package/dist/utils/module.js +25 -0
- package/dist/utils/module.test.js +78 -0
- package/dist/utils/validator.js +116 -0
- package/dist/utils/validator.test.js +324 -0
- package/jsdoc.config.json +10 -0
- package/package.json +62 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +43 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/repo.md +13 -0
package/dist/App.css
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
.App {
|
|
2
|
+
text-align: center;
|
|
3
|
+
background-color: #282c34;
|
|
4
|
+
min-height: 100vh;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
align-items: center;
|
|
8
|
+
font-size: calc(10px + 2vmin);
|
|
9
|
+
color: white;
|
|
10
|
+
|
|
11
|
+
ul,
|
|
12
|
+
ol {
|
|
13
|
+
justify-self: center;
|
|
14
|
+
list-style: none;
|
|
15
|
+
padding-left: 0;
|
|
16
|
+
margin: 0;
|
|
17
|
+
|
|
18
|
+
li {
|
|
19
|
+
justify-self: start;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.button {
|
|
25
|
+
margin: 20px;
|
|
26
|
+
padding: 10px 20px;
|
|
27
|
+
font-size: 1.5rem;
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
background-color: #61dafb;
|
|
30
|
+
border: none;
|
|
31
|
+
border-radius: 5px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.submit-button {
|
|
35
|
+
margin-top: 40px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.disabled {
|
|
39
|
+
background-color: #ccc;
|
|
40
|
+
cursor: not-allowed;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.counter {
|
|
44
|
+
font-size: 5rem;
|
|
45
|
+
font-weight: bold;
|
|
46
|
+
color: #fff;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.counter-container {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
align-items: center;
|
|
53
|
+
padding: 20px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.form-container {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
align-items: center;
|
|
60
|
+
padding: 20px;
|
|
61
|
+
width: max-content;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.input-container {
|
|
65
|
+
display: grid;
|
|
66
|
+
grid-template-areas:
|
|
67
|
+
"label input"
|
|
68
|
+
"error error";
|
|
69
|
+
grid-template-columns: 1fr 2fr;
|
|
70
|
+
row-gap: 4px;
|
|
71
|
+
column-gap: 16px;
|
|
72
|
+
margin: 10px 0;
|
|
73
|
+
width: 100%;
|
|
74
|
+
justify-content: space-between;
|
|
75
|
+
align-items: center;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.label {
|
|
79
|
+
grid-area: label;
|
|
80
|
+
justify-self: start;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-wrap: wrap;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.input {
|
|
86
|
+
grid-area: input;
|
|
87
|
+
width: 100%;
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
box-sizing: border-box;
|
|
90
|
+
padding: 10px;
|
|
91
|
+
font-size: 1rem;
|
|
92
|
+
border-radius: 5px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.error-text {
|
|
96
|
+
grid-area: error;
|
|
97
|
+
display: flex;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
color: red;
|
|
100
|
+
font-size: 1rem;
|
|
101
|
+
margin: 0;
|
|
102
|
+
height: fit-content;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.required-indicator {
|
|
106
|
+
font-size: 0.7rem;
|
|
107
|
+
margin-left: 4px;
|
|
108
|
+
}
|
package/dist/App.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _wouter = require("wouter");
|
|
9
|
+
var _UserForm = require("./components/UserForm");
|
|
10
|
+
var _UsersList = require("./components/UsersList");
|
|
11
|
+
var _api = require("./infra/api");
|
|
12
|
+
require("./App.css");
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function App() {
|
|
15
|
+
const [users, setUsers] = (0, _react.useState)([]);
|
|
16
|
+
(0, _react.useEffect)(() => {
|
|
17
|
+
let isMounted = true;
|
|
18
|
+
const loadUsers = async () => {
|
|
19
|
+
try {
|
|
20
|
+
const fetchedUsers = await (0, _api.getAllUsers)();
|
|
21
|
+
if (!isMounted) return;
|
|
22
|
+
setUsers(Array.isArray(fetchedUsers) ? fetchedUsers : []);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (!isMounted) return;
|
|
25
|
+
console.error("Failed to fetch users:", error);
|
|
26
|
+
alert("Une erreur est survenue lors du chargement des utilisateurs. Veuillez réessayer.");
|
|
27
|
+
setUsers([]);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
loadUsers();
|
|
31
|
+
return () => {
|
|
32
|
+
isMounted = false;
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_wouter.Router, {
|
|
36
|
+
base: "/react-test-lab",
|
|
37
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
38
|
+
className: "App",
|
|
39
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("h1", {
|
|
40
|
+
children: "Bienvenue sur le React Test Lab"
|
|
41
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_wouter.Switch, {
|
|
42
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_wouter.Route, {
|
|
43
|
+
path: "/",
|
|
44
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_UsersList.UsersList, {
|
|
45
|
+
users: users
|
|
46
|
+
})
|
|
47
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_wouter.Route, {
|
|
48
|
+
path: "/register",
|
|
49
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_UserForm.UserForm, {
|
|
50
|
+
setUsers: setUsers
|
|
51
|
+
})
|
|
52
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_wouter.Route, {
|
|
53
|
+
children: "404: No such page!"
|
|
54
|
+
})]
|
|
55
|
+
})]
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
var _default = exports.default = App;
|
package/dist/App.test.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _react = require("@testing-library/react");
|
|
4
|
+
var _userEvent = _interopRequireDefault(require("@testing-library/user-event"));
|
|
5
|
+
var _App = _interopRequireDefault(require("./App"));
|
|
6
|
+
var _axios = _interopRequireDefault(require("axios"));
|
|
7
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
jest.mock("axios");
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
jest.spyOn(window, "alert").mockImplementation(() => {});
|
|
12
|
+
});
|
|
13
|
+
const renderAndWait = async () => {
|
|
14
|
+
const utils = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_App.default, {}));
|
|
15
|
+
await (0, _react.waitFor)(() => expect(_axios.default.get).toHaveBeenCalled());
|
|
16
|
+
return utils;
|
|
17
|
+
};
|
|
18
|
+
describe("App component", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
_axios.default.get.mockResolvedValue({
|
|
22
|
+
data: []
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
afterAll(() => {
|
|
26
|
+
window.alert.mockRestore();
|
|
27
|
+
});
|
|
28
|
+
test("renders welcome title on App", async () => {
|
|
29
|
+
await renderAndWait();
|
|
30
|
+
const title = _react.screen.getByText("Bienvenue sur le React Test Lab");
|
|
31
|
+
expect(title).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
test("renders an empty counter when no users are registered", async () => {
|
|
34
|
+
Object.defineProperty(window, "location", {
|
|
35
|
+
value: {
|
|
36
|
+
pathname: "/react-test-lab"
|
|
37
|
+
},
|
|
38
|
+
writable: true
|
|
39
|
+
});
|
|
40
|
+
_axios.default.get.mockImplementation(() => Promise.resolve({
|
|
41
|
+
data: []
|
|
42
|
+
}));
|
|
43
|
+
await renderAndWait();
|
|
44
|
+
const counter = _react.screen.getByTestId("users-counter");
|
|
45
|
+
expect(counter).toBeInTheDocument();
|
|
46
|
+
expect(counter).toHaveTextContent("Il n'y a aucun utilisateur enregistré");
|
|
47
|
+
});
|
|
48
|
+
test("renders a counter when a user is registered", async () => {
|
|
49
|
+
Object.defineProperty(window, "location", {
|
|
50
|
+
value: {
|
|
51
|
+
pathname: "/react-test-lab"
|
|
52
|
+
},
|
|
53
|
+
writable: true
|
|
54
|
+
});
|
|
55
|
+
const data = {
|
|
56
|
+
data: [{
|
|
57
|
+
name: "John Doe",
|
|
58
|
+
firstname: "John",
|
|
59
|
+
lastname: "Doe",
|
|
60
|
+
email: "john@example.com",
|
|
61
|
+
birth: "1990-01-01",
|
|
62
|
+
zipCode: "75010",
|
|
63
|
+
city: ""
|
|
64
|
+
}]
|
|
65
|
+
};
|
|
66
|
+
_axios.default.get.mockImplementation(() => Promise.resolve(data));
|
|
67
|
+
await renderAndWait();
|
|
68
|
+
const counter = _react.screen.getByTestId("users-counter");
|
|
69
|
+
expect(counter).toBeInTheDocument();
|
|
70
|
+
await (0, _react.waitFor)(() => expect(counter).toHaveTextContent("Il y a 1 utilisateur enregistré"));
|
|
71
|
+
});
|
|
72
|
+
test("renders a counter when several users are registered", async () => {
|
|
73
|
+
Object.defineProperty(window, "location", {
|
|
74
|
+
value: {
|
|
75
|
+
pathname: "/react-test-lab"
|
|
76
|
+
},
|
|
77
|
+
writable: true
|
|
78
|
+
});
|
|
79
|
+
const data = {
|
|
80
|
+
data: [{
|
|
81
|
+
name: "John Doe",
|
|
82
|
+
firstname: "John",
|
|
83
|
+
lastname: "Doe",
|
|
84
|
+
email: "john@example.com",
|
|
85
|
+
birth: "1990-01-01",
|
|
86
|
+
zipCode: "75010",
|
|
87
|
+
city: ""
|
|
88
|
+
}, {
|
|
89
|
+
name: "Jane Smith",
|
|
90
|
+
firstname: "Jane",
|
|
91
|
+
lastname: "Smith",
|
|
92
|
+
email: "jane@example.com",
|
|
93
|
+
birth: "1992-05-05",
|
|
94
|
+
zipCode: "75001",
|
|
95
|
+
city: ""
|
|
96
|
+
}]
|
|
97
|
+
};
|
|
98
|
+
_axios.default.get.mockImplementationOnce(() => Promise.resolve(data));
|
|
99
|
+
await renderAndWait();
|
|
100
|
+
const counter = _react.screen.getByTestId("users-counter");
|
|
101
|
+
expect(counter).toBeInTheDocument();
|
|
102
|
+
await (0, _react.waitFor)(() => expect(counter).toHaveTextContent("Il y a 2 utilisateurs enregistrés"));
|
|
103
|
+
});
|
|
104
|
+
test("displays UsersList component by default", async () => {
|
|
105
|
+
Object.defineProperty(window, "location", {
|
|
106
|
+
value: {
|
|
107
|
+
pathname: "/react-test-lab"
|
|
108
|
+
},
|
|
109
|
+
writable: true
|
|
110
|
+
});
|
|
111
|
+
await renderAndWait();
|
|
112
|
+
const userListTitle = _react.screen.getByText("Créez en un pour voir la liste des utilisateurs enregistrés !");
|
|
113
|
+
expect(userListTitle).toBeInTheDocument();
|
|
114
|
+
});
|
|
115
|
+
test("displays UserForm component when location is set to /register", async () => {
|
|
116
|
+
Object.defineProperty(window, "location", {
|
|
117
|
+
value: {
|
|
118
|
+
pathname: "/react-test-lab/register"
|
|
119
|
+
},
|
|
120
|
+
writable: true
|
|
121
|
+
});
|
|
122
|
+
await renderAndWait();
|
|
123
|
+
const userFormTitle = _react.screen.getByText("Enregistrer un nouvel utilisateur");
|
|
124
|
+
expect(userFormTitle).toBeInTheDocument();
|
|
125
|
+
});
|
|
126
|
+
test("displays 404 message for unknown routes", async () => {
|
|
127
|
+
Object.defineProperty(window, "location", {
|
|
128
|
+
value: {
|
|
129
|
+
pathname: "/react-test-lab/unknown"
|
|
130
|
+
},
|
|
131
|
+
writable: true
|
|
132
|
+
});
|
|
133
|
+
await renderAndWait();
|
|
134
|
+
const notFoundMessage = _react.screen.getByText("404: No such page!");
|
|
135
|
+
expect(notFoundMessage).toBeInTheDocument();
|
|
136
|
+
});
|
|
137
|
+
test("handles fetch errors by alerting and clearing users", async () => {
|
|
138
|
+
Object.defineProperty(window, "location", {
|
|
139
|
+
value: {
|
|
140
|
+
pathname: "/react-test-lab"
|
|
141
|
+
},
|
|
142
|
+
writable: true
|
|
143
|
+
});
|
|
144
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
|
|
145
|
+
_axios.default.get.mockRejectedValueOnce(new Error("Network Error"));
|
|
146
|
+
await renderAndWait();
|
|
147
|
+
await (0, _react.waitFor)(() => expect(window.alert).toHaveBeenCalledWith("Une erreur est survenue lors du chargement des utilisateurs. Veuillez réessayer."));
|
|
148
|
+
const counter = _react.screen.getByTestId("users-counter");
|
|
149
|
+
expect(counter).toHaveTextContent("Il n'y a aucun utilisateur enregistré");
|
|
150
|
+
consoleSpy.mockRestore();
|
|
151
|
+
});
|
|
152
|
+
test("shows alert when API returns 500", async () => {
|
|
153
|
+
Object.defineProperty(window, "location", {
|
|
154
|
+
value: {
|
|
155
|
+
pathname: "/react-test-lab"
|
|
156
|
+
},
|
|
157
|
+
writable: true
|
|
158
|
+
});
|
|
159
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
|
|
160
|
+
const error = new Error("Server error");
|
|
161
|
+
error.response = {
|
|
162
|
+
status: 500
|
|
163
|
+
};
|
|
164
|
+
_axios.default.get.mockRejectedValueOnce(error);
|
|
165
|
+
await renderAndWait();
|
|
166
|
+
await (0, _react.waitFor)(() => expect(window.alert).toHaveBeenCalledWith("Une erreur est survenue lors du chargement des utilisateurs. Veuillez réessayer."));
|
|
167
|
+
consoleSpy.mockRestore();
|
|
168
|
+
});
|
|
169
|
+
test("shows alert when API returns 400", async () => {
|
|
170
|
+
Object.defineProperty(window, "location", {
|
|
171
|
+
value: {
|
|
172
|
+
pathname: "/react-test-lab"
|
|
173
|
+
},
|
|
174
|
+
writable: true
|
|
175
|
+
});
|
|
176
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
|
|
177
|
+
const error = new Error("Bad request");
|
|
178
|
+
error.response = {
|
|
179
|
+
status: 400
|
|
180
|
+
};
|
|
181
|
+
_axios.default.get.mockRejectedValueOnce(error);
|
|
182
|
+
await renderAndWait();
|
|
183
|
+
await (0, _react.waitFor)(() => expect(window.alert).toHaveBeenCalledWith("Une erreur est survenue lors du chargement des utilisateurs. Veuillez réessayer."));
|
|
184
|
+
consoleSpy.mockRestore();
|
|
185
|
+
});
|
|
186
|
+
test("handles non-array API response by clearing users", async () => {
|
|
187
|
+
Object.defineProperty(window, "location", {
|
|
188
|
+
value: {
|
|
189
|
+
pathname: "/react-test-lab"
|
|
190
|
+
},
|
|
191
|
+
writable: true
|
|
192
|
+
});
|
|
193
|
+
_axios.default.get.mockResolvedValueOnce({
|
|
194
|
+
data: {
|
|
195
|
+
unexpected: true
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
await renderAndWait();
|
|
199
|
+
const counter = _react.screen.getByTestId("users-counter");
|
|
200
|
+
expect(counter).toHaveTextContent("Il n'y a aucun utilisateur enregistré");
|
|
201
|
+
});
|
|
202
|
+
test("ignores late rejection after unmount", async () => {
|
|
203
|
+
Object.defineProperty(window, "location", {
|
|
204
|
+
value: {
|
|
205
|
+
pathname: "/react-test-lab"
|
|
206
|
+
},
|
|
207
|
+
writable: true
|
|
208
|
+
});
|
|
209
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
|
|
210
|
+
let rejectFn;
|
|
211
|
+
_axios.default.get.mockReturnValueOnce(new Promise((_, reject) => {
|
|
212
|
+
rejectFn = reject;
|
|
213
|
+
}));
|
|
214
|
+
const {
|
|
215
|
+
unmount
|
|
216
|
+
} = await renderAndWait();
|
|
217
|
+
unmount();
|
|
218
|
+
rejectFn(new Error("Late failure"));
|
|
219
|
+
await (0, _react.waitFor)(() => {
|
|
220
|
+
expect(window.alert).not.toHaveBeenCalled();
|
|
221
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
consoleSpy.mockRestore();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.UserForm = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _wouter = require("wouter");
|
|
9
|
+
var _validator = require("../utils/validator");
|
|
10
|
+
var _TextInput = require("./atomic/TextInput");
|
|
11
|
+
var _api = require("../infra/api");
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
16
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
17
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
18
|
+
const INITIAL_PERSON = {
|
|
19
|
+
firstname: "",
|
|
20
|
+
lastname: "",
|
|
21
|
+
email: "",
|
|
22
|
+
birth: "",
|
|
23
|
+
city: "",
|
|
24
|
+
zipCode: ""
|
|
25
|
+
};
|
|
26
|
+
const UserForm = _ref => {
|
|
27
|
+
let {
|
|
28
|
+
setUsers
|
|
29
|
+
} = _ref;
|
|
30
|
+
const [disabled, setDisabled] = (0, _react.useState)(true);
|
|
31
|
+
const [person, setPerson] = (0, _react.useState)(INITIAL_PERSON);
|
|
32
|
+
const [personError, setPersonError] = (0, _react.useState)(INITIAL_PERSON);
|
|
33
|
+
const [, navigate] = (0, _wouter.useLocation)();
|
|
34
|
+
const handleChange = function (field, value) {
|
|
35
|
+
let validator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
36
|
+
setPerson(_objectSpread(_objectSpread({}, person), {}, {
|
|
37
|
+
[field]: value
|
|
38
|
+
}));
|
|
39
|
+
if (!validator) {
|
|
40
|
+
setPersonError(_objectSpread(_objectSpread({}, personError), {}, {
|
|
41
|
+
[field]: ""
|
|
42
|
+
}));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const validateData = field === "birth" ? {
|
|
47
|
+
birth: new Date(value)
|
|
48
|
+
} : field === "email" ? {
|
|
49
|
+
email: value
|
|
50
|
+
} : field === "zipCode" ? {
|
|
51
|
+
zipCode: value
|
|
52
|
+
} : value;
|
|
53
|
+
validator(validateData);
|
|
54
|
+
setPersonError(_objectSpread(_objectSpread({}, personError), {}, {
|
|
55
|
+
[field]: ""
|
|
56
|
+
}));
|
|
57
|
+
} catch (error) {
|
|
58
|
+
setPersonError(_objectSpread(_objectSpread({}, personError), {}, {
|
|
59
|
+
[field]: error.message
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
(0, _react.useEffect)(() => {
|
|
64
|
+
if (!person.firstname || !person.lastname || !person.birth || !new Date(person.birth) instanceof Date || !person.email || !person.zipCode) return;
|
|
65
|
+
try {
|
|
66
|
+
if ((0, _validator.validateIndentity)(person) && (0, _validator.validateAge)(_objectSpread(_objectSpread({}, person), {}, {
|
|
67
|
+
birth: new Date(person.birth)
|
|
68
|
+
})) && (0, _validator.validateZipCode)(person) && (0, _validator.validateEmail)(person)) return setDisabled(false);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return setDisabled(true);
|
|
71
|
+
}
|
|
72
|
+
}, [person]);
|
|
73
|
+
const onSubmit = async e => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
const user = _objectSpread({
|
|
76
|
+
name: "".concat(person.firstname, " ").concat(person.lastname)
|
|
77
|
+
}, person);
|
|
78
|
+
try {
|
|
79
|
+
const createdUser = await (0, _api.createOneUser)(user);
|
|
80
|
+
setUsers(prev => {
|
|
81
|
+
const updatedUsers = [...prev, createdUser];
|
|
82
|
+
return updatedUsers;
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error("Failed to create user:", error);
|
|
86
|
+
alert("Une erreur est survenue lors de la création de l'utilisateur. Veuillez réessayer.");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
setPerson(INITIAL_PERSON);
|
|
90
|
+
setDisabled(true);
|
|
91
|
+
navigate("/");
|
|
92
|
+
};
|
|
93
|
+
const requiredIndicator = /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
94
|
+
className: "required-indicator",
|
|
95
|
+
"aria-hidden": "true",
|
|
96
|
+
children: "*"
|
|
97
|
+
});
|
|
98
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
99
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("h3", {
|
|
100
|
+
className: "form-title",
|
|
101
|
+
children: "Enregistrer un nouvel utilisateur"
|
|
102
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("form", {
|
|
103
|
+
action: "/",
|
|
104
|
+
method: "get",
|
|
105
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
106
|
+
className: "form-container",
|
|
107
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_TextInput.TextInput, {
|
|
108
|
+
label: "Pr\xE9nom",
|
|
109
|
+
id: "firstname",
|
|
110
|
+
value: person.firstname,
|
|
111
|
+
onChange: e => handleChange("firstname", e.target.value, _validator.validateName),
|
|
112
|
+
testId: "firstname-input",
|
|
113
|
+
errorTestId: "firstname-error-text",
|
|
114
|
+
errorText: personError.firstname,
|
|
115
|
+
required: true,
|
|
116
|
+
requiredIndicator: requiredIndicator
|
|
117
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextInput.TextInput, {
|
|
118
|
+
label: "Nom",
|
|
119
|
+
id: "lastname",
|
|
120
|
+
type: "text",
|
|
121
|
+
value: person.lastname,
|
|
122
|
+
onChange: e => handleChange("lastname", e.target.value, _validator.validateName),
|
|
123
|
+
testId: "lastname-input",
|
|
124
|
+
errorTestId: "lastname-error-text",
|
|
125
|
+
errorText: personError.lastname,
|
|
126
|
+
required: true,
|
|
127
|
+
requiredIndicator: requiredIndicator
|
|
128
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextInput.TextInput, {
|
|
129
|
+
label: "Email",
|
|
130
|
+
id: "email",
|
|
131
|
+
type: "email",
|
|
132
|
+
value: person.email,
|
|
133
|
+
onChange: e => handleChange("email", e.target.value, _validator.validateEmail),
|
|
134
|
+
testId: "email-input",
|
|
135
|
+
errorTestId: "email-error-text",
|
|
136
|
+
errorText: personError.email,
|
|
137
|
+
required: true,
|
|
138
|
+
requiredIndicator: requiredIndicator
|
|
139
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextInput.TextInput, {
|
|
140
|
+
label: "Date de naissance",
|
|
141
|
+
id: "birthdate",
|
|
142
|
+
type: "date",
|
|
143
|
+
value: person.birth,
|
|
144
|
+
onChange: e => handleChange("birth", e.target.value, _validator.validateAge),
|
|
145
|
+
testId: "birth-input",
|
|
146
|
+
errorTestId: "birth-error-text",
|
|
147
|
+
errorText: personError.birth,
|
|
148
|
+
required: true,
|
|
149
|
+
requiredIndicator: requiredIndicator
|
|
150
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextInput.TextInput, {
|
|
151
|
+
label: "Ville",
|
|
152
|
+
id: "city",
|
|
153
|
+
value: person.city,
|
|
154
|
+
onChange: e => handleChange("city", e.target.value),
|
|
155
|
+
testId: "city-input",
|
|
156
|
+
errorTestId: "city-error-text",
|
|
157
|
+
errorText: personError.city
|
|
158
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextInput.TextInput, {
|
|
159
|
+
label: "Code postal",
|
|
160
|
+
id: "zipcode",
|
|
161
|
+
value: person.zipCode,
|
|
162
|
+
onChange: e => handleChange("zipCode", e.target.value, _validator.validateZipCode),
|
|
163
|
+
testId: "zip-input",
|
|
164
|
+
errorTestId: "zip-error-text",
|
|
165
|
+
errorText: personError.zipCode,
|
|
166
|
+
required: true,
|
|
167
|
+
requiredIndicator: requiredIndicator
|
|
168
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
169
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
170
|
+
"data-testid": "back-button",
|
|
171
|
+
className: "button button-secondary",
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: () => navigate("/"),
|
|
174
|
+
children: "Retour"
|
|
175
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
176
|
+
"data-testid": "submit-button",
|
|
177
|
+
className: "button submit-button ".concat(disabled && "disabled"),
|
|
178
|
+
type: "submit",
|
|
179
|
+
onClick: onSubmit,
|
|
180
|
+
disabled: disabled,
|
|
181
|
+
children: "Enregistrer l'utilisateur"
|
|
182
|
+
})]
|
|
183
|
+
})]
|
|
184
|
+
})
|
|
185
|
+
})]
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
exports.UserForm = UserForm;
|