@wasto/sdk 1.0.3 → 1.0.5
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/dist/index.d.mts +60 -3
- package/dist/index.d.ts +60 -3
- package/dist/index.js +79 -0
- package/dist/index.mjs +79 -0
- package/package.json +1 -1
- package/src/client.ts +791 -692
- package/src/dashboard-apps.ts +367 -350
- package/src/dto/cms-app-settings.ts +91 -91
- package/src/enums.ts +298 -298
- package/src/free-tier.ts +56 -56
- package/src/index.ts +35 -35
- package/src/opening-hours/constants.ts +48 -48
- package/src/opening-hours/convert.ts +276 -276
- package/src/opening-hours/format.ts +99 -99
- package/src/opening-hours/index.ts +9 -9
- package/src/opening-hours/is-open-now.ts +38 -38
- package/src/opening-hours/resolve.ts +71 -71
- package/src/opening-hours/time.ts +74 -74
- package/src/opening-hours/timezone.ts +34 -34
- package/src/opening-hours/types.ts +53 -53
- package/src/opening-hours/validate.ts +182 -182
- package/src/roi/calculations.ts +732 -732
- package/src/roi/index.ts +1 -1
- package/src/types/app-preload.types.ts +10 -0
- package/src/types/asset-bookings.types.ts +19 -19
- package/src/types/asset-interventions.types.ts +24 -24
- package/src/types/assets.types.ts +68 -68
- package/src/types/auth.types.ts +37 -37
- package/src/types/blog-posts.types.ts +19 -19
- package/src/types/calendar-pdf.types.ts +24 -24
- package/src/types/calendo-events.types.ts +64 -64
- package/src/types/challenges.types.ts +30 -30
- package/src/types/chatbot.types.ts +82 -82
- package/src/types/cities.types.ts +27 -27
- package/src/types/city-groups.types.ts +46 -46
- package/src/types/cms.types.ts +87 -87
- package/src/types/field-reports.types.ts +66 -66
- package/src/types/free-tier.types.ts +54 -54
- package/src/types/index.ts +33 -32
- package/src/types/iot.types.ts +31 -31
- package/src/types/maintenance.types.ts +44 -44
- package/src/types/mapso.types.ts +146 -146
- package/src/types/notifications.types.ts +68 -68
- package/src/types/onboarding.types.ts +117 -117
- package/src/types/operational-zones.types.ts +70 -70
- package/src/types/pagination.types.ts +33 -33
- package/src/types/providers.types.ts +29 -29
- package/src/types/quiz-questions.types.ts +59 -59
- package/src/types/recycling-points.types.ts +47 -47
- package/src/types/roi.types.ts +60 -60
- package/src/types/surveys.types.ts +103 -103
- package/src/types/tickets.types.ts +65 -65
- package/src/types/users.types.ts +30 -30
- package/src/types/waste-collection-places-query.types.ts +4 -4
- package/src/types/waste-collection-places.types.ts +34 -33
- package/src/types/waste-types.types.ts +21 -21
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
export const WEEKDAYS = [
|
|
2
|
-
'monday',
|
|
3
|
-
'tuesday',
|
|
4
|
-
'wednesday',
|
|
5
|
-
'thursday',
|
|
6
|
-
'friday',
|
|
7
|
-
'saturday',
|
|
8
|
-
'sunday',
|
|
9
|
-
] as const;
|
|
10
|
-
|
|
11
|
-
export type Weekday = (typeof WEEKDAYS)[number];
|
|
12
|
-
|
|
13
|
-
export type TimeSlot = { open: string; close: string };
|
|
14
|
-
|
|
15
|
-
export type OpeningHoursRule = { days: Weekday[]; slots: TimeSlot[] };
|
|
16
|
-
|
|
17
|
-
export type OpeningHoursExceptionClosed = {
|
|
18
|
-
date: string;
|
|
19
|
-
closed: true;
|
|
20
|
-
label?: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type OpeningHoursExceptionOpen = {
|
|
24
|
-
date: string;
|
|
25
|
-
closed: false;
|
|
26
|
-
slots: TimeSlot[];
|
|
27
|
-
label?: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type OpeningHoursException =
|
|
31
|
-
| OpeningHoursExceptionClosed
|
|
32
|
-
| OpeningHoursExceptionOpen;
|
|
33
|
-
|
|
34
|
-
/** Horaires structurés persistés en UTC+0 */
|
|
35
|
-
export type OpeningHours = {
|
|
36
|
-
version: 1;
|
|
37
|
-
storage: 'utc';
|
|
38
|
-
rules: OpeningHoursRule[];
|
|
39
|
-
exceptions?: OpeningHoursException[];
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export type HoursInputMode = 'structured' | 'text';
|
|
43
|
-
|
|
44
|
-
export type WeeklyHoursRow = {
|
|
45
|
-
day: Weekday;
|
|
46
|
-
label: string;
|
|
47
|
-
closed: boolean;
|
|
48
|
-
slots: TimeSlot[];
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export type OpeningHoursValidationResult =
|
|
52
|
-
| { ok: true; value: OpeningHours }
|
|
53
|
-
| { ok: false; errors: string[] };
|
|
1
|
+
export const WEEKDAYS = [
|
|
2
|
+
'monday',
|
|
3
|
+
'tuesday',
|
|
4
|
+
'wednesday',
|
|
5
|
+
'thursday',
|
|
6
|
+
'friday',
|
|
7
|
+
'saturday',
|
|
8
|
+
'sunday',
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
export type Weekday = (typeof WEEKDAYS)[number];
|
|
12
|
+
|
|
13
|
+
export type TimeSlot = { open: string; close: string };
|
|
14
|
+
|
|
15
|
+
export type OpeningHoursRule = { days: Weekday[]; slots: TimeSlot[] };
|
|
16
|
+
|
|
17
|
+
export type OpeningHoursExceptionClosed = {
|
|
18
|
+
date: string;
|
|
19
|
+
closed: true;
|
|
20
|
+
label?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type OpeningHoursExceptionOpen = {
|
|
24
|
+
date: string;
|
|
25
|
+
closed: false;
|
|
26
|
+
slots: TimeSlot[];
|
|
27
|
+
label?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type OpeningHoursException =
|
|
31
|
+
| OpeningHoursExceptionClosed
|
|
32
|
+
| OpeningHoursExceptionOpen;
|
|
33
|
+
|
|
34
|
+
/** Horaires structurés persistés en UTC+0 */
|
|
35
|
+
export type OpeningHours = {
|
|
36
|
+
version: 1;
|
|
37
|
+
storage: 'utc';
|
|
38
|
+
rules: OpeningHoursRule[];
|
|
39
|
+
exceptions?: OpeningHoursException[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type HoursInputMode = 'structured' | 'text';
|
|
43
|
+
|
|
44
|
+
export type WeeklyHoursRow = {
|
|
45
|
+
day: Weekday;
|
|
46
|
+
label: string;
|
|
47
|
+
closed: boolean;
|
|
48
|
+
slots: TimeSlot[];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type OpeningHoursValidationResult =
|
|
52
|
+
| { ok: true; value: OpeningHours }
|
|
53
|
+
| { ok: false; errors: string[] };
|
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
import {
|
|
2
|
-
WEEKDAYS,
|
|
3
|
-
type OpeningHours,
|
|
4
|
-
type OpeningHoursException,
|
|
5
|
-
type OpeningHoursRule,
|
|
6
|
-
type OpeningHoursValidationResult,
|
|
7
|
-
type TimeSlot,
|
|
8
|
-
type Weekday,
|
|
9
|
-
} from './types';
|
|
10
|
-
import { parseTimeMinutes, slotsOverlap, sortSlots } from './time';
|
|
11
|
-
|
|
12
|
-
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
13
|
-
|
|
14
|
-
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
15
|
-
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function validateTimeSlot(slot: unknown, path: string, errors: string[]): slot is TimeSlot {
|
|
19
|
-
if (!isPlainObject(slot)) {
|
|
20
|
-
errors.push(`${path} : créneau invalide`);
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
const open = slot.open;
|
|
24
|
-
const close = slot.close;
|
|
25
|
-
if (typeof open !== 'string' || parseTimeMinutes(open) == null) {
|
|
26
|
-
errors.push(`${path}.open : heure invalide (HH:mm)`);
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
if (typeof close !== 'string' || parseTimeMinutes(close) == null) {
|
|
30
|
-
errors.push(`${path}.close : heure invalide (HH:mm)`);
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
const openMin = parseTimeMinutes(open)!;
|
|
34
|
-
const closeMin = parseTimeMinutes(close)!;
|
|
35
|
-
if (closeMin <= openMin) {
|
|
36
|
-
errors.push(`${path} : l'heure de fermeture doit être après l'ouverture (UTC)`);
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function validateRule(rule: unknown, index: number, errors: string[]): rule is OpeningHoursRule {
|
|
43
|
-
if (!isPlainObject(rule)) {
|
|
44
|
-
errors.push(`rules[${index}] : règle invalide`);
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
if (!Array.isArray(rule.days) || rule.days.length === 0) {
|
|
48
|
-
errors.push(`rules[${index}].days : au moins un jour requis`);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
for (const day of rule.days) {
|
|
52
|
-
if (typeof day !== 'string' || !WEEKDAYS.includes(day as Weekday)) {
|
|
53
|
-
errors.push(`rules[${index}].days : jour invalide (${String(day)})`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (!Array.isArray(rule.slots) || rule.slots.length === 0) {
|
|
57
|
-
errors.push(`rules[${index}].slots : au moins un créneau requis`);
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
const slots: TimeSlot[] = [];
|
|
61
|
-
rule.slots.forEach((slot, slotIndex) => {
|
|
62
|
-
if (validateTimeSlot(slot, `rules[${index}].slots[${slotIndex}]`, errors)) {
|
|
63
|
-
slots.push(slot);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
const sorted = sortSlots(slots);
|
|
67
|
-
for (let i = 0; i < sorted.length - 1; i++) {
|
|
68
|
-
if (slotsOverlap(sorted[i], sorted[i + 1])) {
|
|
69
|
-
errors.push(`rules[${index}].slots : créneaux qui se chevauchent`);
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return errors.length === 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function validateException(
|
|
77
|
-
exc: unknown,
|
|
78
|
-
index: number,
|
|
79
|
-
errors: string[],
|
|
80
|
-
): exc is OpeningHoursException {
|
|
81
|
-
if (!isPlainObject(exc)) {
|
|
82
|
-
errors.push(`exceptions[${index}] : exception invalide`);
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
if (typeof exc.date !== 'string' || !DATE_RE.test(exc.date)) {
|
|
86
|
-
errors.push(`exceptions[${index}].date : date invalide (YYYY-MM-DD)`);
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
if (exc.closed === true) return true;
|
|
90
|
-
if (exc.closed === false) {
|
|
91
|
-
if (!Array.isArray(exc.slots) || exc.slots.length === 0) {
|
|
92
|
-
errors.push(`exceptions[${index}].slots : requis si non fermé`);
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
exc.slots.forEach((slot, slotIndex) => {
|
|
96
|
-
validateTimeSlot(slot, `exceptions[${index}].slots[${slotIndex}]`, errors);
|
|
97
|
-
});
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
errors.push(`exceptions[${index}].closed : booléen requis`);
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function validateOpeningHours(input: unknown): OpeningHoursValidationResult {
|
|
105
|
-
const errors: string[] = [];
|
|
106
|
-
if (!isPlainObject(input)) {
|
|
107
|
-
return { ok: false, errors: ['Horaires : objet attendu'] };
|
|
108
|
-
}
|
|
109
|
-
if (input.version !== 1) {
|
|
110
|
-
errors.push('version : doit être 1');
|
|
111
|
-
}
|
|
112
|
-
if (input.storage !== 'utc') {
|
|
113
|
-
errors.push('storage : doit être "utc"');
|
|
114
|
-
}
|
|
115
|
-
if (!Array.isArray(input.rules)) {
|
|
116
|
-
errors.push('rules : tableau attendu');
|
|
117
|
-
return { ok: false, errors };
|
|
118
|
-
}
|
|
119
|
-
input.rules.forEach((rule, index) => validateRule(rule, index, errors));
|
|
120
|
-
|
|
121
|
-
const daySet = new Set<Weekday>();
|
|
122
|
-
if (Array.isArray(input.rules)) {
|
|
123
|
-
for (const rule of input.rules) {
|
|
124
|
-
if (!isPlainObject(rule) || !Array.isArray(rule.days)) continue;
|
|
125
|
-
for (const day of rule.days) {
|
|
126
|
-
if (typeof day === 'string' && WEEKDAYS.includes(day as Weekday)) {
|
|
127
|
-
const wd = day as Weekday;
|
|
128
|
-
if (daySet.has(wd)) {
|
|
129
|
-
errors.push(`rules : le jour ${wd} apparaît dans plusieurs règles`);
|
|
130
|
-
}
|
|
131
|
-
daySet.add(wd);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (input.exceptions != null) {
|
|
138
|
-
if (!Array.isArray(input.exceptions)) {
|
|
139
|
-
errors.push('exceptions : tableau attendu');
|
|
140
|
-
} else {
|
|
141
|
-
input.exceptions.forEach((exc, index) => validateException(exc, index, errors));
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (errors.length > 0) {
|
|
146
|
-
return { ok: false, errors };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
ok: true,
|
|
151
|
-
value: {
|
|
152
|
-
version: 1,
|
|
153
|
-
storage: 'utc',
|
|
154
|
-
rules: (input.rules as OpeningHoursRule[]).map((r) => ({
|
|
155
|
-
days: [...r.days],
|
|
156
|
-
slots: sortSlots([...r.slots]),
|
|
157
|
-
})),
|
|
158
|
-
exceptions: input.exceptions as OpeningHoursException[] | undefined,
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** Valide des horaires saisis en heure locale (même structure, sans storage/version). */
|
|
164
|
-
export function validateLocalOpeningHours(input: {
|
|
165
|
-
rules: OpeningHoursRule[];
|
|
166
|
-
exceptions?: OpeningHoursException[];
|
|
167
|
-
}): OpeningHoursValidationResult {
|
|
168
|
-
return validateOpeningHours({
|
|
169
|
-
version: 1,
|
|
170
|
-
storage: 'utc',
|
|
171
|
-
rules: input.rules,
|
|
172
|
-
exceptions: input.exceptions,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function createEmptyOpeningHours(): OpeningHours {
|
|
177
|
-
return { version: 1, storage: 'utc', rules: [] };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function createEmptyLocalRules(): OpeningHoursRule[] {
|
|
181
|
-
return [];
|
|
182
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
WEEKDAYS,
|
|
3
|
+
type OpeningHours,
|
|
4
|
+
type OpeningHoursException,
|
|
5
|
+
type OpeningHoursRule,
|
|
6
|
+
type OpeningHoursValidationResult,
|
|
7
|
+
type TimeSlot,
|
|
8
|
+
type Weekday,
|
|
9
|
+
} from './types';
|
|
10
|
+
import { parseTimeMinutes, slotsOverlap, sortSlots } from './time';
|
|
11
|
+
|
|
12
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
13
|
+
|
|
14
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
15
|
+
return v != null && typeof v === 'object' && !Array.isArray(v);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function validateTimeSlot(slot: unknown, path: string, errors: string[]): slot is TimeSlot {
|
|
19
|
+
if (!isPlainObject(slot)) {
|
|
20
|
+
errors.push(`${path} : créneau invalide`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const open = slot.open;
|
|
24
|
+
const close = slot.close;
|
|
25
|
+
if (typeof open !== 'string' || parseTimeMinutes(open) == null) {
|
|
26
|
+
errors.push(`${path}.open : heure invalide (HH:mm)`);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (typeof close !== 'string' || parseTimeMinutes(close) == null) {
|
|
30
|
+
errors.push(`${path}.close : heure invalide (HH:mm)`);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const openMin = parseTimeMinutes(open)!;
|
|
34
|
+
const closeMin = parseTimeMinutes(close)!;
|
|
35
|
+
if (closeMin <= openMin) {
|
|
36
|
+
errors.push(`${path} : l'heure de fermeture doit être après l'ouverture (UTC)`);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validateRule(rule: unknown, index: number, errors: string[]): rule is OpeningHoursRule {
|
|
43
|
+
if (!isPlainObject(rule)) {
|
|
44
|
+
errors.push(`rules[${index}] : règle invalide`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (!Array.isArray(rule.days) || rule.days.length === 0) {
|
|
48
|
+
errors.push(`rules[${index}].days : au moins un jour requis`);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
for (const day of rule.days) {
|
|
52
|
+
if (typeof day !== 'string' || !WEEKDAYS.includes(day as Weekday)) {
|
|
53
|
+
errors.push(`rules[${index}].days : jour invalide (${String(day)})`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!Array.isArray(rule.slots) || rule.slots.length === 0) {
|
|
57
|
+
errors.push(`rules[${index}].slots : au moins un créneau requis`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const slots: TimeSlot[] = [];
|
|
61
|
+
rule.slots.forEach((slot, slotIndex) => {
|
|
62
|
+
if (validateTimeSlot(slot, `rules[${index}].slots[${slotIndex}]`, errors)) {
|
|
63
|
+
slots.push(slot);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const sorted = sortSlots(slots);
|
|
67
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
68
|
+
if (slotsOverlap(sorted[i], sorted[i + 1])) {
|
|
69
|
+
errors.push(`rules[${index}].slots : créneaux qui se chevauchent`);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return errors.length === 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateException(
|
|
77
|
+
exc: unknown,
|
|
78
|
+
index: number,
|
|
79
|
+
errors: string[],
|
|
80
|
+
): exc is OpeningHoursException {
|
|
81
|
+
if (!isPlainObject(exc)) {
|
|
82
|
+
errors.push(`exceptions[${index}] : exception invalide`);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (typeof exc.date !== 'string' || !DATE_RE.test(exc.date)) {
|
|
86
|
+
errors.push(`exceptions[${index}].date : date invalide (YYYY-MM-DD)`);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (exc.closed === true) return true;
|
|
90
|
+
if (exc.closed === false) {
|
|
91
|
+
if (!Array.isArray(exc.slots) || exc.slots.length === 0) {
|
|
92
|
+
errors.push(`exceptions[${index}].slots : requis si non fermé`);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
exc.slots.forEach((slot, slotIndex) => {
|
|
96
|
+
validateTimeSlot(slot, `exceptions[${index}].slots[${slotIndex}]`, errors);
|
|
97
|
+
});
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
errors.push(`exceptions[${index}].closed : booléen requis`);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function validateOpeningHours(input: unknown): OpeningHoursValidationResult {
|
|
105
|
+
const errors: string[] = [];
|
|
106
|
+
if (!isPlainObject(input)) {
|
|
107
|
+
return { ok: false, errors: ['Horaires : objet attendu'] };
|
|
108
|
+
}
|
|
109
|
+
if (input.version !== 1) {
|
|
110
|
+
errors.push('version : doit être 1');
|
|
111
|
+
}
|
|
112
|
+
if (input.storage !== 'utc') {
|
|
113
|
+
errors.push('storage : doit être "utc"');
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(input.rules)) {
|
|
116
|
+
errors.push('rules : tableau attendu');
|
|
117
|
+
return { ok: false, errors };
|
|
118
|
+
}
|
|
119
|
+
input.rules.forEach((rule, index) => validateRule(rule, index, errors));
|
|
120
|
+
|
|
121
|
+
const daySet = new Set<Weekday>();
|
|
122
|
+
if (Array.isArray(input.rules)) {
|
|
123
|
+
for (const rule of input.rules) {
|
|
124
|
+
if (!isPlainObject(rule) || !Array.isArray(rule.days)) continue;
|
|
125
|
+
for (const day of rule.days) {
|
|
126
|
+
if (typeof day === 'string' && WEEKDAYS.includes(day as Weekday)) {
|
|
127
|
+
const wd = day as Weekday;
|
|
128
|
+
if (daySet.has(wd)) {
|
|
129
|
+
errors.push(`rules : le jour ${wd} apparaît dans plusieurs règles`);
|
|
130
|
+
}
|
|
131
|
+
daySet.add(wd);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (input.exceptions != null) {
|
|
138
|
+
if (!Array.isArray(input.exceptions)) {
|
|
139
|
+
errors.push('exceptions : tableau attendu');
|
|
140
|
+
} else {
|
|
141
|
+
input.exceptions.forEach((exc, index) => validateException(exc, index, errors));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (errors.length > 0) {
|
|
146
|
+
return { ok: false, errors };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
ok: true,
|
|
151
|
+
value: {
|
|
152
|
+
version: 1,
|
|
153
|
+
storage: 'utc',
|
|
154
|
+
rules: (input.rules as OpeningHoursRule[]).map((r) => ({
|
|
155
|
+
days: [...r.days],
|
|
156
|
+
slots: sortSlots([...r.slots]),
|
|
157
|
+
})),
|
|
158
|
+
exceptions: input.exceptions as OpeningHoursException[] | undefined,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Valide des horaires saisis en heure locale (même structure, sans storage/version). */
|
|
164
|
+
export function validateLocalOpeningHours(input: {
|
|
165
|
+
rules: OpeningHoursRule[];
|
|
166
|
+
exceptions?: OpeningHoursException[];
|
|
167
|
+
}): OpeningHoursValidationResult {
|
|
168
|
+
return validateOpeningHours({
|
|
169
|
+
version: 1,
|
|
170
|
+
storage: 'utc',
|
|
171
|
+
rules: input.rules,
|
|
172
|
+
exceptions: input.exceptions,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function createEmptyOpeningHours(): OpeningHours {
|
|
177
|
+
return { version: 1, storage: 'utc', rules: [] };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function createEmptyLocalRules(): OpeningHoursRule[] {
|
|
181
|
+
return [];
|
|
182
|
+
}
|