@movalib/movalib-commons 1.0.67 → 1.0.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.env.development +6 -0
  2. package/.env.production +4 -0
  3. package/devIndex.tsx +119 -10
  4. package/dist/devIndex.js +101 -8
  5. package/dist/index.d.ts +3 -1
  6. package/dist/index.js +5 -1
  7. package/dist/src/MovaLogin.js +2 -2
  8. package/dist/src/MovaSignUp.js +1 -1
  9. package/dist/src/QRCode.d.ts +1 -0
  10. package/dist/src/QRCode.js +13 -12
  11. package/dist/src/ScheduleFields.d.ts +24 -0
  12. package/dist/src/ScheduleFields.js +202 -0
  13. package/dist/src/helpers/ApiHelper.d.ts +20 -0
  14. package/dist/src/helpers/ApiHelper.js +74 -0
  15. package/dist/src/helpers/CookieUtils.d.ts +6 -0
  16. package/dist/src/helpers/CookieUtils.js +28 -0
  17. package/dist/src/helpers/Enums.d.ts +8 -1
  18. package/dist/src/helpers/Enums.js +10 -2
  19. package/dist/src/helpers/Tools.d.ts +6 -2
  20. package/dist/src/helpers/Tools.js +20 -7
  21. package/dist/src/services/AuthenticationService.d.ts +6 -0
  22. package/dist/src/services/AuthenticationService.js +106 -0
  23. package/dist/src/services/GarageService.d.ts +5 -0
  24. package/dist/src/services/GarageService.js +16 -0
  25. package/dist/src/services/UserService.d.ts +10 -0
  26. package/dist/src/services/UserService.js +76 -0
  27. package/index.ts +3 -1
  28. package/package.json +9 -8
  29. package/src/MovaLogin.tsx +2 -2
  30. package/src/MovaSignUp.tsx +1 -1
  31. package/src/QRCode.css +4 -0
  32. package/src/QRCode.tsx +12 -13
  33. package/src/ScheduleFields.tsx +301 -0
  34. package/src/helpers/ApiHelper.ts +95 -0
  35. package/src/helpers/CookieUtils.ts +23 -0
  36. package/src/helpers/Enums.ts +8 -1
  37. package/src/helpers/Tools.ts +20 -2
  38. package/src/services/AuthenticationService.ts +68 -0
  39. package/src/services/GarageService.ts +14 -0
  40. package/src/services/UserService.ts +25 -0
  41. package/tsconfig.json +1 -1
  42. package/webpack.config.js +9 -0
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var ApiHelper_1 = require("../helpers/ApiHelper");
43
+ var Enums_1 = require("../helpers/Enums");
44
+ var Logger_1 = __importDefault(require("../helpers/Logger"));
45
+ var UserService = /** @class */ (function () {
46
+ function UserService() {
47
+ }
48
+ /**
49
+ * @param email
50
+ * @param password
51
+ * @returns
52
+ */
53
+ UserService.getCurrentUser = function () {
54
+ return __awaiter(this, void 0, void 0, function () {
55
+ var error_1;
56
+ return __generator(this, function (_a) {
57
+ switch (_a.label) {
58
+ case 0:
59
+ _a.trys.push([0, 2, , 3]);
60
+ return [4 /*yield*/, (0, ApiHelper_1.request)({
61
+ url: "".concat(ApiHelper_1.API_BASE_URL, "/user/me"),
62
+ method: Enums_1.APIMethod.GET
63
+ })];
64
+ case 1: return [2 /*return*/, _a.sent()];
65
+ case 2:
66
+ error_1 = _a.sent();
67
+ Logger_1.default.error('Error occurred during getting user:', error_1);
68
+ return [2 /*return*/, Promise.reject(error_1)];
69
+ case 3: return [2 /*return*/];
70
+ }
71
+ });
72
+ });
73
+ };
74
+ return UserService;
75
+ }());
76
+ exports.default = UserService;
package/index.ts CHANGED
@@ -30,11 +30,13 @@ export { default as Event } from './src/models/Event';
30
30
  export { default as VehicleTire } from './src/models/VehicleTire';
31
31
 
32
32
  // Export des types
33
+ export type { APIRequest, APIResponse } from './src/helpers/ApiHelper';
33
34
  export type { MovaFormField, MovaLoginForm, MovaUserSignUpForm, MovaInterval, OriginReference,
34
35
  MovaVehicleForm } from './src/helpers/Types';
35
36
 
36
37
  // Export des méthodes utilitaires
37
- export { validateField, formatVehicleTire, formatFrenchVehiclePlate } from './src/helpers/Tools';
38
+ export { readCookie, deleteCookie } from './src/helpers/CookieUtils';
39
+ export { validateField, formatVehicleTire, formatFrenchVehiclePlate, isEmpty } from './src/helpers/Tools';
38
40
  export { validatePhoneNumber, validateText, validateEmail } from './src/helpers/Validator';
39
41
  export { formatDateByCountryCode, getLongFormattedDateTime, capitalizeFirstLetter } from './src/helpers/DateUtils';
40
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@movalib/movalib-commons",
3
- "version": "1.0.67",
3
+ "version": "1.0.68",
4
4
  "description": "Bibliothèque d'objets communs à l'ensemble des projets React de Movalib",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,21 +20,21 @@
20
20
  "@emotion/styled": "^11.11.0",
21
21
  "@mui/icons-material": "^5.11.16",
22
22
  "@mui/lab": "^5.0.0-alpha.134",
23
- "@mui/x-date-pickers": "^6.9.1",
24
23
  "@mui/material": "^5.13.5",
24
+ "@mui/x-date-pickers": "^6.9.1",
25
25
  "@types/react": "^18.2.22",
26
26
  "@types/react-dom": "^18.2.7",
27
- "file-loader": "^6.2.0",
28
- "@types/node": "^16.18.53",
29
27
  "@types/react-router-dom": "^5.3.0",
28
+ "date-fns": "^2.29.3",
29
+ "date-fns-tz": "^2.0.0",
30
+ "file-loader": "^6.2.0",
31
+ "js-cookie": "^3.0.5",
32
+ "qr-code-styling": "^1.4.4",
30
33
  "react": "^18.2.0",
31
34
  "react-dom": "^18.2.0",
32
35
  "react-router-dom": "^5.1.2",
33
36
  "react-scripts": "5.0.1",
34
- "qr-code-styling": "^1.4.4",
35
- "typescript": "^4.9.5",
36
- "date-fns": "^2.29.3",
37
- "date-fns-tz": "^2.0.0"
37
+ "typescript": "^4.9.5"
38
38
  },
39
39
  "eslintConfig": {
40
40
  "extends": [
@@ -42,6 +42,7 @@
42
42
  ]
43
43
  },
44
44
  "devDependencies": {
45
+ "@types/js-cookie": "^3.0.4",
45
46
  "html-webpack-plugin": "^5.5.3",
46
47
  "ts-loader": "^9.4.4",
47
48
  "webpack-cli": "^5.1.4"
package/src/MovaLogin.tsx CHANGED
@@ -103,7 +103,7 @@ const MovaLogin: FunctionComponent<MovaLoginProps> = ({ loading, movaAppType, on
103
103
  const getMovaLogo = () => {
104
104
 
105
105
  return movaAppType === MovaAppType.GARAGE ? LogoProLarge :
106
- movaAppType === MovaAppType.USER ? LogoLarge :
106
+ movaAppType === MovaAppType.INDIVIDUAL ? LogoLarge :
107
107
  movaAppType === MovaAppType.ADMIN ? LogoLarge : LogoLarge;
108
108
  }
109
109
 
@@ -267,7 +267,7 @@ const MovaLogin: FunctionComponent<MovaLoginProps> = ({ loading, movaAppType, on
267
267
  Mot de passe oublié ?
268
268
  </Link>
269
269
  </Grid>
270
- {movaAppType === MovaAppType.USER && <Grid item>
270
+ {movaAppType === MovaAppType.INDIVIDUAL && <Grid item>
271
271
  <Link variant="body2" color="text.secondary" onClick={(e) => handleOnClickSignUp(e)}>
272
272
  Créer mon compte
273
273
  </Link>
@@ -145,7 +145,7 @@ const MovaLogin: FunctionComponent<MovaSignUpProps> = ({ loading, movaAppType, o
145
145
  const getMovaLogo = () => {
146
146
 
147
147
  return movaAppType === MovaAppType.GARAGE ? LogoProLarge :
148
- movaAppType === MovaAppType.USER ? LogoLarge :
148
+ movaAppType === MovaAppType.INDIVIDUAL ? LogoLarge :
149
149
  movaAppType === MovaAppType.ADMIN ? LogoLarge : LogoLarge;
150
150
  }
151
151
 
package/src/QRCode.css ADDED
@@ -0,0 +1,4 @@
1
+ .qr-code canvas {
2
+ max-width: 300px !important;
3
+ width: 100%;
4
+ }
package/src/QRCode.tsx CHANGED
@@ -12,6 +12,7 @@ import QRCodeStyling, {
12
12
  Extension,
13
13
  Options
14
14
  } from "qr-code-styling";
15
+ import './QRCode.css';
15
16
 
16
17
 
17
18
  interface QRCodeProps {
@@ -19,18 +20,14 @@ interface QRCodeProps {
19
20
  showDownload?:boolean
20
21
  }
21
22
 
22
-
23
23
  const styles = {
24
24
  inputWrapper: {
25
25
  margin: "20px 0",
26
26
  display: "flex",
27
27
  alignItems: 'center',
28
28
  justifyContent: "center",
29
- width: '100%'
30
- },
31
- inputBox: {
32
- flexGrow: 1,
33
- marginRight: 20
29
+ width: '300px',
30
+ height: '300px'
34
31
  }
35
32
  };
36
33
 
@@ -42,9 +39,9 @@ const QRCode: FC<QRCodeProps> = ({ data, showDownload=false }) => {
42
39
  const defaultData:string = 'https://movalib.com';
43
40
 
44
41
  const [options, setOptions] = useState<Options>({
45
- width: 300,
46
- height: 300,
47
- type: 'svg' as DrawType,
42
+ width: 600,
43
+ height: 600,
44
+ type: 'canvas' as DrawType,
48
45
  data: data ?? defaultData,
49
46
  image: QRCodeImage,
50
47
  margin: 10,
@@ -56,8 +53,7 @@ const QRCode: FC<QRCodeProps> = ({ data, showDownload=false }) => {
56
53
  imageOptions: {
57
54
  hideBackgroundDots: true,
58
55
  imageSize: 0.4,
59
- margin: 15,
60
- crossOrigin: 'anonymous',
56
+ margin: 15
61
57
  },
62
58
  dotsOptions: {
63
59
  color: darken(theme.palette.primary.main, 0.3),
@@ -126,8 +122,11 @@ const QRCode: FC<QRCodeProps> = ({ data, showDownload=false }) => {
126
122
  };
127
123
 
128
124
  return (
129
- <Box>
130
- <div ref={ref} style={styles.inputWrapper} />
125
+ <Box sx={{
126
+ display: 'flex',
127
+ flexDirection: 'column',
128
+ alignItems: 'center' }} >
129
+ <div ref={ref} style={styles.inputWrapper} className='qr-code' />
131
130
  {/* <select onChange={onExtensionChange} value={fileExt}>
132
131
  <option value="svg">SVG</option>
133
132
  <option value="png">PNG</option>
@@ -0,0 +1,301 @@
1
+ import * as React from 'react';
2
+ import { FunctionComponent, useEffect, useState } from 'react';
3
+ import { Checkbox, FormControlLabel, Grid, IconButton, FormHelperText, FormControl, RadioGroup, Radio } from '@mui/material';
4
+ import AddIcon from '@mui/icons-material/AddRounded';
5
+ import DeleteIcon from '@mui/icons-material/DeleteRounded';
6
+ import { TimePicker } from '@mui/x-date-pickers';
7
+ import theme from '../theme'; // Import du thème personnalisé
8
+ import Schedule from './models/Schedule';
9
+ import { DayOfWeek } from './helpers/Enums';
10
+ import Logger from './helpers/Logger';
11
+ import { flexCenter } from './helpers/Tools';
12
+
13
+ export const FR_WEEK_DAYS: string[] = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'];
14
+
15
+ type DayInterval = {
16
+ startTime: Date | '' | null,
17
+ endTime: Date | '' | null,
18
+ countryCode: string,
19
+ error: string | null
20
+ }
21
+
22
+ export type DaySchedule = {
23
+ day: DayOfWeek,
24
+ checked: boolean,
25
+ allDay: boolean,
26
+ intervals: DayInterval[],
27
+ };
28
+
29
+ const initialSchedules: DaySchedule[] = [
30
+ {day: DayOfWeek.MONDAY, checked: false, allDay: false, intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
31
+ {day: DayOfWeek.TUESDAY, checked: false, allDay: false,intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
32
+ {day: DayOfWeek.WEDNESDAY, checked: false, allDay: false,intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
33
+ {day: DayOfWeek.THURSDAY, checked: false, allDay: false,intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
34
+ {day: DayOfWeek.FRIDAY, checked: false, allDay: false,intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
35
+ {day: DayOfWeek.SATURDAY, checked: false, allDay: false,intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
36
+ {day: DayOfWeek.SUNDAY, checked: false, allDay: false,intervals: [{startTime: null, endTime: null, countryCode: 'fr', error: null}]},
37
+ ];
38
+
39
+ interface ScheduleFieldsProps {
40
+ timePickerStep: number,
41
+ size: 'small' | 'medium',
42
+ onChange: (schedules: DaySchedule[]) => void,
43
+ schedules?: Schedule[] | undefined;
44
+ }
45
+
46
+ const ScheduleFields: FunctionComponent<ScheduleFieldsProps> = ({ timePickerStep, schedules, size, onChange }) => {
47
+
48
+ const mergeSchedules = (apiSchedules: Schedule[]): DaySchedule[] => {
49
+ // Créez une copie profonde du tableau initialSchedules pour éviter toute mutation accidentelle
50
+ const mergedSchedules = JSON.parse(JSON.stringify(initialSchedules));
51
+
52
+ // Parcourez chaque élément de apiSchedules et mettez à jour updatedSchedules
53
+ const today = new Date().toISOString().substring(0, 10);
54
+
55
+ for (const apiSchedule of apiSchedules) {
56
+ const dayIndex = mergedSchedules.findIndex((schedule: { day: DayOfWeek; }) => schedule.day === apiSchedule.dayOfWeek);
57
+ if (dayIndex !== -1) {
58
+ mergedSchedules[dayIndex].checked = apiSchedule.intervals.length > 0;
59
+ mergedSchedules[dayIndex].intervals = apiSchedule.intervals.map(interval => ({
60
+ ...interval,
61
+ // On injecte une date fictive même si nous n'utilisons que l'heure, le Timepicker attend une Date JS Valide
62
+ startTime: new Date(`${today}T${interval.startTime!}`),
63
+ endTime: new Date(`${today}T${interval.endTime!}`),
64
+ error: null, // Réinitialisez l'erreur car les données sont maintenant mises à jour
65
+ }));
66
+ mergedSchedules[dayIndex].allDay = apiSchedule.isAllWorkingDay;
67
+ }
68
+ }
69
+
70
+ Logger.info(mergedSchedules);
71
+
72
+ return mergedSchedules;
73
+ }
74
+
75
+ /**
76
+ * Cette fonction renvoie une liste de DaySchedule depuis une liste de Schedule
77
+ * @returns
78
+ */
79
+ const initializeFromSchedules = () => {
80
+
81
+ if (schedules && schedules.length > 0 ){
82
+
83
+ return schedules.map(s => {
84
+ const iList: DayInterval[] = s.intervals.map(i => ({
85
+ startTime: new Date(i.startTime!),
86
+ endTime: new Date(i.endTime!),
87
+ countryCode: i.countryCode,
88
+ error: null,
89
+ }));
90
+
91
+ return {
92
+ day: s.dayOfWeek,
93
+ checked: iList.length > 0, // le checked est à true si il y a au moins un interval de saisi
94
+ intervals: iList,
95
+ };
96
+ });
97
+ }
98
+ return [];
99
+ }
100
+
101
+ // Si des schedules sont transmises on initialise un nouveau tableau de DaySchedule[]
102
+ const [schedule, setSchedule] = useState<DaySchedule[]>(initialSchedules);
103
+
104
+ // Chargement initial des données du garage
105
+ useEffect(() => {
106
+
107
+ if(schedules && schedules.length > 0){
108
+ setSchedule(mergeSchedules(schedules));
109
+ }
110
+
111
+ }, [schedules]); // Exécute le hook à chaque fois que les schedules transmis en props changent
112
+
113
+ const handleDayChecked = (dayIndex: number) => (event: React.ChangeEvent<HTMLInputElement>) => {
114
+ const newSchedule = [...schedule];
115
+ newSchedule[dayIndex].checked = event.target.checked;
116
+ // On réinitialise aussi les intervals
117
+ newSchedule[dayIndex].intervals = [{startTime: null, endTime: null, countryCode: 'fr', error: null}];
118
+ onChange(newSchedule);
119
+ setSchedule(newSchedule);
120
+ };
121
+
122
+ const handleAllDayChecked = (e: React.ChangeEvent<HTMLInputElement>, value: string, dayIndex: number) => (event: React.ChangeEvent<HTMLInputElement>) => {
123
+ e.preventDefault();
124
+
125
+ const newSchedule = [...schedule];
126
+ newSchedule[dayIndex].allDay = event.target.checked;
127
+ onChange(newSchedule);
128
+ setSchedule(newSchedule);
129
+ };
130
+
131
+ const validateInterval = (start: Date | '', end: Date | ''): string | null => {
132
+ if (start instanceof Date && end instanceof Date && start.getTime() > end.getTime()) {
133
+ return "L'heure de début doit être antérieure à l'heure de fin";
134
+ }
135
+ return null;
136
+ };
137
+
138
+ const validateIntervals = (intervals: DayInterval[]): string | null => {
139
+
140
+ // On tri les intervals par date de début
141
+ const sortedIntervals = [...intervals].sort((a, b) =>
142
+ a.startTime && b.startTime ? a.startTime.toISOString().localeCompare(b.startTime.toISOString()) : 0);
143
+
144
+ console.log(sortedIntervals)
145
+ for (let i = 0; i < sortedIntervals.length; i++) {
146
+ if (
147
+ sortedIntervals[i].startTime?.toString().localeCompare(sortedIntervals[i].endTime?.toString()!)! >= 0 ||
148
+ (i < sortedIntervals.length - 1 &&
149
+ sortedIntervals[i].endTime?.toString().localeCompare(sortedIntervals[i + 1].startTime?.toString()!)! > 0)
150
+ ) {
151
+ return 'Attention vos intervales se chevauchent'; // start time is after or equal to end time, or end time of current interval is after start time of next interval
152
+ }
153
+ }
154
+ return null; // all intervals are valid and there are no overlapping intervals
155
+ }
156
+
157
+
158
+ const handleIntervalChange = (dayIndex: number, intervalIndex: number, type: 'startTime' | 'endTime') =>
159
+ (newValue: "" | Date | null) => {
160
+
161
+ const newSchedule = [...schedule];
162
+
163
+ newSchedule[dayIndex].intervals[intervalIndex][type] = newValue!;
164
+
165
+ newSchedule[dayIndex].intervals[intervalIndex].error = validateInterval(
166
+ newSchedule[dayIndex].intervals[intervalIndex].startTime!,
167
+ newSchedule[dayIndex].intervals[intervalIndex].endTime!
168
+ );
169
+
170
+ //Contrôle le chevauchement d'intervales
171
+ if(type === 'endTime' && intervalIndex > 0 && !newSchedule[dayIndex].intervals[intervalIndex].error){
172
+ console.log(dayIndex);
173
+ console.log(newSchedule[dayIndex].intervals);
174
+ newSchedule[dayIndex].intervals[intervalIndex].error = validateIntervals(
175
+ newSchedule[dayIndex].intervals);
176
+ }
177
+
178
+ setSchedule(newSchedule);
179
+ onChange(newSchedule);
180
+ };
181
+
182
+ const addInterval = (dayIndex: number) => () => {
183
+ Logger.info(dayIndex);
184
+
185
+ const newSchedule = [...schedule];
186
+ newSchedule[dayIndex].intervals.push({startTime: null, endTime: null, countryCode: 'fr', error: null});
187
+ Logger.info(newSchedule);
188
+ setSchedule(newSchedule);
189
+ };
190
+
191
+ const deleteInterval = (dayIndex: number, intervalIndex: number) => () => {
192
+ const newSchedule = [...schedule];
193
+ newSchedule[dayIndex].intervals.splice(intervalIndex, 1);
194
+ onChange(newSchedule);
195
+ setSchedule(newSchedule);
196
+ }
197
+
198
+ const getDayOfWeekIndex = (day: DayOfWeek) => {
199
+ if(day){
200
+ // Convertir les valeurs de l'énumération en un tableau
201
+ const daysArray = Object.values(DayOfWeek);
202
+ return daysArray.indexOf(day);
203
+ }
204
+ return -1;
205
+ }
206
+
207
+ return (
208
+ <Grid container spacing={1} sx={{ minWidth: '650px', maxWidth: '800px'}} >
209
+ {schedule.map((daySchedule, dayIndex) => (
210
+ <>
211
+ <Grid item xs={1} />
212
+
213
+ <Grid item xs={2} key={dayIndex+1}>
214
+ <FormControlLabel
215
+ control={
216
+ <Checkbox
217
+ checked={daySchedule.checked}
218
+ onChange={handleDayChecked(dayIndex)}
219
+ />
220
+ }
221
+ label={FR_WEEK_DAYS[getDayOfWeekIndex(daySchedule.day)]}
222
+ />
223
+ </Grid>
224
+
225
+ <Grid item xs={7} key={(dayIndex+1)*Math.random()}>
226
+ {daySchedule.intervals.map((interval, intervalIndex) => (
227
+ <Grid container key={intervalIndex+1}
228
+ style={{ paddingTop: intervalIndex > 0 ? theme.spacing(1) : 0 }}>
229
+ <Grid item xs={6} key={(intervalIndex+1)*Math.random()} sx={{ textAlign: 'center' }}>
230
+ <TimePicker
231
+ views={['hours', 'minutes']}
232
+ minutesStep={timePickerStep ?? 30} // Valeur par défaut de 30 minutes au cas ou
233
+ disabled={!daySchedule.checked}
234
+ value={interval.startTime}
235
+ formatDensity='dense'
236
+ onChange={handleIntervalChange(dayIndex, intervalIndex, 'startTime')}
237
+ slotProps={{
238
+ textField: {
239
+ size: 'small',
240
+ sx: {
241
+ width:'150px',
242
+ minWidth:'100px',
243
+ padding: 0,
244
+ },
245
+ },
246
+ actionBar: {
247
+ sx: { display: 'none' },
248
+ },
249
+ }}
250
+ />
251
+ </Grid>
252
+ <Grid item xs={6} key={(intervalIndex+1)*Math.random()} sx={{ display: 'contents'}}>
253
+ <TimePicker
254
+ minutesStep={timePickerStep ?? 30} // Valeur par défaut de 30 minutes au cas ou
255
+ disabled={!(interval.startTime instanceof Date)}
256
+ value={interval.endTime}
257
+ formatDensity='dense'
258
+ onChange={handleIntervalChange(dayIndex, intervalIndex, 'endTime')}
259
+ //onClose={handleIntervalClose}
260
+ closeOnSelect={false}
261
+ slotProps={{
262
+ textField: {
263
+ size: 'small',
264
+ sx: {
265
+ width:'150px',
266
+ minWidth:'100px',
267
+ padding: 0,
268
+ },
269
+ },
270
+ actionBar: {
271
+ sx: { display: 'none' },
272
+ },
273
+ }}
274
+ />
275
+ {intervalIndex > 0 &&
276
+ <IconButton onClick={deleteInterval(dayIndex, intervalIndex)} disabled={!daySchedule.checked} sx={{ ml: 2 }}>
277
+ <DeleteIcon />
278
+ </IconButton>
279
+ }
280
+ </Grid>
281
+ <Grid item xs={12} key={(intervalIndex+1)*Math.random()} >
282
+ {interval.error && <FormHelperText error sx={{mb:1}}>{interval.error}</FormHelperText>}
283
+ </Grid>
284
+ </Grid>
285
+ ))}
286
+ </Grid>
287
+
288
+ <Grid item xs={1} key={(dayIndex+1)*Math.random()} sx={{ pl: 0 }}>
289
+ <IconButton onClick={addInterval(dayIndex)} disabled={!daySchedule.checked}>
290
+ <AddIcon />
291
+ </IconButton>
292
+ </Grid>
293
+
294
+ <Grid item xs={1} />
295
+ </>
296
+ ))}
297
+ </Grid>
298
+ );
299
+ }
300
+
301
+ export default ScheduleFields;
@@ -0,0 +1,95 @@
1
+ import { APIMethod } from "./Enums";
2
+ import { COOKIE_GARAGE_TOKEN, readCookie } from "./CookieUtils";
3
+ import Logger from "./Logger";
4
+
5
+ export const API_BASE_URL = process.env.REACT_APP_API_URL ?? 'https://localhost:8443/api';
6
+
7
+ export type APIResponse<T> = {
8
+ success: boolean;
9
+ data?: T;
10
+ error?: string;
11
+ };
12
+
13
+ export type APIRequest = {
14
+ url: string,
15
+ method: APIMethod,
16
+ body?: string | FormData
17
+ }
18
+
19
+ function handleError(error: Error): APIResponse<null> {
20
+ let msg = error.message.includes('fetch') ? "Connexion impossible" : error.message;
21
+ return {
22
+ success: false,
23
+ error: msg
24
+ };
25
+ }
26
+
27
+ function handleResponse(response: Response): Promise<APIResponse<any>> {
28
+
29
+ const contentType = response.headers.get("content-type");
30
+ const isJson = contentType && contentType.includes("application/json");
31
+
32
+ const dataPromise = isJson ? response.json() : response.text();
33
+
34
+ return dataPromise.then(data => {
35
+
36
+ if (!response.ok) {
37
+
38
+ Logger.error(data);
39
+
40
+ let errorMsg: string;
41
+
42
+ switch(response.status){
43
+ case 403:
44
+ errorMsg = 'Accès non autorisé (403)'; break;
45
+ case 404:
46
+ errorMsg = 'La ressource demandée est introuvable (404)'; break;
47
+ case 500:
48
+ errorMsg = 'Une erreur interne du serveur est survenue (500)'; break;
49
+ default:
50
+ errorMsg = (typeof data === 'string') ? data : `Une erreur est survenue (${response.status})`; break;
51
+ }
52
+
53
+ return { success: false, error: errorMsg };
54
+ }
55
+
56
+ return { success: true, data };
57
+ });
58
+ }
59
+
60
+
61
+ /**
62
+ * ATTENTION : cela signifie que toutes les requêtes du Front seront de type "preflight"
63
+ * Il faut penser à configurer l'API pour autoriser les headers
64
+ * @param options
65
+ * @param dispatch
66
+ * @returns
67
+ */
68
+ export const request = (options:APIRequest): Promise<APIResponse<any>> => {
69
+
70
+ const headers = new Headers();
71
+
72
+ // Si le corps est de type FormData, il ne faut pas définir le content-type manuellement (le navigateur prend le relai est injecte un boundary (délimiteur)
73
+ if (!(options.body instanceof FormData)) {
74
+ headers.append('Content-Type', 'application/json');
75
+ }
76
+
77
+ // Récupération du TOKEN depuis les cookies et intégration au header
78
+ if(readCookie(COOKIE_GARAGE_TOKEN)) {
79
+ headers.append('Authorization', 'Bearer ' + readCookie(COOKIE_GARAGE_TOKEN))
80
+ }
81
+
82
+ // On intègre les headers aux options
83
+ const defaults = {headers: headers};
84
+ options = Object.assign({}, defaults, options);
85
+
86
+ Logger.info(options);
87
+
88
+ // Appel API
89
+ return fetch(options.url, options)
90
+ .then(handleResponse)
91
+ .catch(error => {
92
+ Logger.error('There has been a problem with your fetch operation : ', error.message);
93
+ return handleError(error);
94
+ });
95
+ }
@@ -0,0 +1,23 @@
1
+ import Cookies from "js-cookie";
2
+
3
+ export const COOKIE_GARAGE_TOKEN: string = 'movalibGarageToken';
4
+ export const COOKIE_INDIVIDUAL_TOKEN: string = 'movalibIndividualToken';
5
+ export const COOKIE_DEFAULT_EXPIRES: number = 7;
6
+
7
+ export const createCookie = (name:string, value:string) => {
8
+ if(name && value){
9
+ Cookies.set(name, value, { expires: COOKIE_DEFAULT_EXPIRES, sameSite: 'None', secure: true });
10
+ }
11
+ }
12
+
13
+ export const deleteCookie = (name:string) => {
14
+ if(name){
15
+ Cookies.remove(name);
16
+ }
17
+ }
18
+
19
+ export const readCookie = (name:string) => {
20
+ if(name){
21
+ return Cookies.get(name);
22
+ }
23
+ }
@@ -1,3 +1,10 @@
1
+ export enum APIMethod {
2
+ GET = 'GET',
3
+ PUT = 'PUT',
4
+ PATCH = 'PATCH',
5
+ POST = 'POST',
6
+ DELETE = 'DELETE'
7
+ }
1
8
 
2
9
  export enum DateFormatTypes {
3
10
  SHORT_FORMAT_DATE = 'dd/MM/yyyy',
@@ -81,7 +88,7 @@ export enum MovaAppType {
81
88
  /**
82
89
  * Application Utilisateur (automobiliste)
83
90
  */
84
- USER = "USER",
91
+ INDIVIDUAL = "INDIVIDUAL",
85
92
  /**
86
93
  * Application Administrateur (MovaTeam)
87
94
  */