ublo-lib 1.0.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/es/common/components/admin-links/admin-links.js +107 -0
- package/es/common/components/admin-links/admin-links.module.css +85 -0
- package/es/common/components/admin-links/index.js +2 -0
- package/es/common/components/analytics.js +46 -0
- package/es/common/components/breadcrumb.js +69 -0
- package/es/common/components/carousel-zone.js +61 -0
- package/es/common/components/carousel.js +365 -0
- package/es/common/components/cookie-consent/cookie-consent.js +111 -0
- package/es/common/components/cookie-consent/cookie-consent.module.css +103 -0
- package/es/common/components/cookie-consent/index.js +3 -0
- package/es/common/components/cookie-consent/messages.js +31 -0
- package/es/common/components/custom-contact-form/attachment.js +229 -0
- package/es/common/components/custom-contact-form/attachment.module.css +211 -0
- package/es/common/components/custom-contact-form/custom-contact-form.js +168 -0
- package/es/common/components/custom-contact-form/field.js +294 -0
- package/es/common/components/custom-contact-form/field.module.css +17 -0
- package/es/common/components/custom-contact-form/icons.js +55 -0
- package/es/common/components/custom-contact-form/index.js +2 -0
- package/es/common/components/custom-contact-form/index.module.css +119 -0
- package/es/common/components/custom-contact-form/messages.js +79 -0
- package/es/common/components/custom-contact-form/utils.js +132 -0
- package/es/common/components/date-picker/calendar.js +246 -0
- package/es/common/components/date-picker/calendar.module.css +123 -0
- package/es/common/components/date-picker/data.js +96 -0
- package/es/common/components/date-picker/date-item.js +127 -0
- package/es/common/components/date-picker/date-item.module.css +78 -0
- package/es/common/components/date-picker/date-picker.js +119 -0
- package/es/common/components/date-picker/date-picker.module.css +111 -0
- package/es/common/components/date-picker/helper.js +41 -0
- package/es/common/components/date-picker/helper.module.css +81 -0
- package/es/common/components/date-picker/index.js +3 -0
- package/es/common/components/date-picker/messages.js +34 -0
- package/es/common/components/date-picker/modes.js +42 -0
- package/es/common/components/date-picker/modes.module.css +90 -0
- package/es/common/components/date-picker/month.js +78 -0
- package/es/common/components/date-picker/month.module.css +54 -0
- package/es/common/components/date-picker/utils.js +121 -0
- package/es/common/components/error-404/error-404.js +51 -0
- package/es/common/components/error-404/error-404.module.css +55 -0
- package/es/common/components/error-404/index.js +2 -0
- package/es/common/components/error-404/messages.js +29 -0
- package/es/common/components/faq.js +54 -0
- package/es/common/components/info-buttons.js +116 -0
- package/es/common/components/plausible/hooks/use-plausible.js +33 -0
- package/es/common/components/plausible/index.js +7 -0
- package/es/common/components/plausible/plausible.js +20 -0
- package/es/common/components/plausible/services/callback.js +63 -0
- package/es/common/components/plausible/services/load.js +7 -0
- package/es/common/components/plausible/services/send-goal.js +10 -0
- package/es/common/components/popup.js +90 -0
- package/es/common/components/scroll-spy.js +53 -0
- package/es/common/components/tabbed-zones.js +110 -0
- package/es/common/components/unsupported-browser.js +158 -0
- package/es/common/components/video-player/controls.js +125 -0
- package/es/common/components/video-player/icons.js +45 -0
- package/es/common/components/video-player/index.js +1 -0
- package/es/common/components/video-player/player.module.css +151 -0
- package/es/common/components/video-player/utils.js +43 -0
- package/es/common/components/video-player/video-player.js +111 -0
- package/es/common/hooks/use-faq.js +44 -0
- package/es/common/hooks/use-in-view.js +73 -0
- package/es/common/hooks/use-injected-cms-markup.js +50 -0
- package/es/common/hooks/use-packages.js +127 -0
- package/es/common/hooks/use-scroll-direction.js +44 -0
- package/es/common/hooks/use-stay.js +32 -0
- package/es/common/hooks/use-sticky.js +38 -0
- package/es/common/hooks/use-tunnel.js +50 -0
- package/es/common/hooks/use-window-sizes.js +37 -0
- package/es/common/hooks/use-youtube-popup.js +62 -0
- package/es/common/hooks/use-zone-sync.js +65 -0
- package/es/common/utils/cookies.js +13 -0
- package/es/common/utils/copy.js +11 -0
- package/es/common/utils/dates.js +8 -0
- package/es/common/utils/events.js +25 -0
- package/es/common/utils/fetcher.js +37 -0
- package/es/common/utils/file-manager.js +14 -0
- package/es/common/utils/load-js.js +11 -0
- package/es/common/utils/msem-widget.js +16 -0
- package/es/common/utils/touch-device.js +1 -0
- package/es/common/utils/url-parameters.js +12 -0
- package/es/empty.d.ts +4 -0
- package/es/empty.d.ts.map +1 -0
- package/es/empty.js +6 -0
- package/es/esf/components/booking-form/data.js +213 -0
- package/es/esf/components/booking-form/field.js +140 -0
- package/es/esf/components/booking-form/hooks/use-custom-fields.js +20 -0
- package/es/esf/components/booking-form/hooks/use-stay.js +12 -0
- package/es/esf/components/booking-form/icons.js +50 -0
- package/es/esf/components/booking-form/index.js +78 -0
- package/es/esf/components/booking-form/lesson.js +59 -0
- package/es/esf/components/booking-form/lessons.js +93 -0
- package/es/esf/components/booking-form/messages.js +52 -0
- package/es/esf/components/booking-form/personal-data.js +73 -0
- package/es/esf/components/booking-form/progress-bar.js +35 -0
- package/es/esf/components/booking-form/response.js +42 -0
- package/es/esf/components/booking-form/steps.js +81 -0
- package/es/esf/components/booking-form/summary.js +138 -0
- package/es/esf/components/booking-form/utils.js +72 -0
- package/es/esf/components/contact-form/api.js +36 -0
- package/es/esf/components/contact-form/contact-form.js +293 -0
- package/es/esf/components/contact-form/contact-form.module.css +51 -0
- package/es/esf/components/contact-form/data.js +53 -0
- package/es/esf/components/contact-form/index.js +2 -0
- package/es/esf/components/contact-form/messages.js +75 -0
- package/es/esf/components/contact-form/validation.js +63 -0
- package/es/esf/components/covid-link/index.js +119 -0
- package/es/esf/components/covid-link/index.module.css +108 -0
- package/es/esf/components/covid-link/mask-icon.js +17 -0
- package/es/esf/components/covid-link/vax-pass-icon.js +34 -0
- package/es/esf/components/cp-form.js +65 -0
- package/es/esf/components/instructor-suggestions/fetcher.js +17 -0
- package/es/esf/components/instructor-suggestions/icons.js +266 -0
- package/es/esf/components/instructor-suggestions/index.js +181 -0
- package/es/esf/components/instructor-suggestions/loader.js +10 -0
- package/es/esf/components/instructor-suggestions/messages.js +16 -0
- package/es/esf/components/instructors-book/container.js +18 -0
- package/es/esf/components/instructors-book/details.js +120 -0
- package/es/esf/components/instructors-book/icons.js +266 -0
- package/es/esf/components/instructors-book/index.js +15 -0
- package/es/esf/components/instructors-book/link.js +17 -0
- package/es/esf/components/instructors-book/list-utils.js +21 -0
- package/es/esf/components/instructors-book/list.js +184 -0
- package/es/esf/components/instructors-book/loader.js +10 -0
- package/es/esf/components/instructors-book/messages.js +44 -0
- package/es/esf/components/instructors-book/utils.js +5 -0
- package/es/esf/components/levels.js +265 -0
- package/es/esf/components/loyal-customers/api.js +24 -0
- package/es/esf/components/loyal-customers/components/bin-icon.js +31 -0
- package/es/esf/components/loyal-customers/components/bin-icon.module.css +9 -0
- package/es/esf/components/loyal-customers/components/customer-form.js +105 -0
- package/es/esf/components/loyal-customers/components/customer-form.module.css +40 -0
- package/es/esf/components/loyal-customers/components/field.js +119 -0
- package/es/esf/components/loyal-customers/components/field.module.css +3 -0
- package/es/esf/components/loyal-customers/components/row.js +77 -0
- package/es/esf/components/loyal-customers/components/row.module.css +95 -0
- package/es/esf/components/loyal-customers/components/rows.js +38 -0
- package/es/esf/components/loyal-customers/components/rows.module.css +11 -0
- package/es/esf/components/loyal-customers/components/stay.js +37 -0
- package/es/esf/components/loyal-customers/components/stay.module.css +18 -0
- package/es/esf/components/loyal-customers/components/student-form.js +105 -0
- package/es/esf/components/loyal-customers/components/student-form.module.css +68 -0
- package/es/esf/components/loyal-customers/components/voucher.js +26 -0
- package/es/esf/components/loyal-customers/components/voucher.module.css +7 -0
- package/es/esf/components/loyal-customers/content.js +55 -0
- package/es/esf/components/loyal-customers/data.js +131 -0
- package/es/esf/components/loyal-customers/hooks/use-stored-rows.js +14 -0
- package/es/esf/components/loyal-customers/index.js +2 -0
- package/es/esf/components/loyal-customers/loyal-customers.js +141 -0
- package/es/esf/components/loyal-customers/loyal-customers.module.css +62 -0
- package/es/esf/components/loyal-customers/messages.js +59 -0
- package/es/esf/components/loyal-customers/utils.js +81 -0
- package/es/esf/components/village-maps/icons.js +35 -0
- package/es/esf/components/village-maps/index.js +214 -0
- package/es/esf/components/village-maps/messages.js +19 -0
- package/es/esf/components/village-maps/utils.js +26 -0
- package/es/esf/components/week-picker/index.js +244 -0
- package/es/esf/components/week-picker/messages.js +36 -0
- package/es/esf/components/week-picker/utils.js +65 -0
- package/es/esf/components/week-picker/week.js +52 -0
- package/es/esf/components/week-picker-2/index.js +283 -0
- package/es/esf/components/week-picker-2/messages.js +27 -0
- package/es/esf/components/week-picker-2/utils.js +65 -0
- package/es/esf/components/week-picker-2/week.js +55 -0
- package/es/esf/hooks/use-affiliation.js +26 -0
- package/es/esf/hooks/use-booking-links.js +36 -0
- package/es/esf/hooks/use-reviews.js +28 -0
- package/es/esf/hooks/use-season-products.js +100 -0
- package/package.json +51 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { useUbloContext } from "ublo/with-ublo";
|
|
4
|
+
import Attachment from "./attachment";
|
|
5
|
+
import Input from "dt-design-system/es/input";
|
|
6
|
+
import Textarea from "dt-design-system/es/textarea";
|
|
7
|
+
import Select from "dt-design-system/es/select";
|
|
8
|
+
import Checkbox from "dt-design-system/es/checkbox";
|
|
9
|
+
import NumberPicker from "dt-design-system/es/number-picker";
|
|
10
|
+
import * as Fetcher from "../../utils/fetcher";
|
|
11
|
+
import { FIELD_TESTS_ERROR_CODES, validate } from "./utils";
|
|
12
|
+
import * as Messages from "./messages";
|
|
13
|
+
import styles from "./field.module.css";
|
|
14
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
|
+
|
|
17
|
+
const getTag = type => {
|
|
18
|
+
switch (true) {
|
|
19
|
+
case type === "textarea":
|
|
20
|
+
return Textarea;
|
|
21
|
+
|
|
22
|
+
case type === "select":
|
|
23
|
+
return Select;
|
|
24
|
+
|
|
25
|
+
case type === "checkbox":
|
|
26
|
+
return Checkbox;
|
|
27
|
+
|
|
28
|
+
case type === "number":
|
|
29
|
+
return NumberPicker;
|
|
30
|
+
|
|
31
|
+
default:
|
|
32
|
+
return Input;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getProps = (type, name, className, data, required, placeholder, autoSizing, onChange) => {
|
|
37
|
+
const commonProps = {
|
|
38
|
+
className,
|
|
39
|
+
value: data[name].value,
|
|
40
|
+
placeholder,
|
|
41
|
+
required,
|
|
42
|
+
autoSizing
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (type === "textarea" || type === "select") {
|
|
46
|
+
return {
|
|
47
|
+
onValueChange: onChange,
|
|
48
|
+
...commonProps
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (type === "number") {
|
|
53
|
+
return {
|
|
54
|
+
withInput: true,
|
|
55
|
+
onChange,
|
|
56
|
+
...commonProps
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
type,
|
|
62
|
+
onValueChange: onChange,
|
|
63
|
+
...commonProps
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const Checkboxes = ({
|
|
68
|
+
checkboxes,
|
|
69
|
+
data,
|
|
70
|
+
name,
|
|
71
|
+
onChange,
|
|
72
|
+
disabled
|
|
73
|
+
}) => {
|
|
74
|
+
if (!checkboxes.length) return null;
|
|
75
|
+
return checkboxes.map((checkbox, i) => {
|
|
76
|
+
const {
|
|
77
|
+
value = checkbox,
|
|
78
|
+
label = checkbox
|
|
79
|
+
} = checkbox;
|
|
80
|
+
const checked = data[name].value.includes(value);
|
|
81
|
+
return _jsx(Checkbox, {
|
|
82
|
+
label: label,
|
|
83
|
+
onValueChange: onChange,
|
|
84
|
+
value: checked,
|
|
85
|
+
disabled: disabled
|
|
86
|
+
}, i);
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const getLabel = (lang, label, required) => {
|
|
91
|
+
if (!required) return `${label} (${Messages.get(lang, "optionnal")})`;
|
|
92
|
+
return label;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const Inner = ({
|
|
96
|
+
site,
|
|
97
|
+
lang,
|
|
98
|
+
label,
|
|
99
|
+
kind,
|
|
100
|
+
name,
|
|
101
|
+
classes,
|
|
102
|
+
field,
|
|
103
|
+
data,
|
|
104
|
+
setData,
|
|
105
|
+
disabled,
|
|
106
|
+
activated,
|
|
107
|
+
setActivated,
|
|
108
|
+
valid,
|
|
109
|
+
setValid
|
|
110
|
+
}) => {
|
|
111
|
+
const {
|
|
112
|
+
type,
|
|
113
|
+
className,
|
|
114
|
+
options: fieldOptions = [],
|
|
115
|
+
placeholder,
|
|
116
|
+
required,
|
|
117
|
+
autoSizing
|
|
118
|
+
} = field;
|
|
119
|
+
const [options, setOptions] = React.useState(fieldOptions);
|
|
120
|
+
const fieldClasses = classnames({
|
|
121
|
+
[className]: className,
|
|
122
|
+
[classes]: classes
|
|
123
|
+
});
|
|
124
|
+
const fieldsetRef = React.useRef();
|
|
125
|
+
const Tag = getTag(type);
|
|
126
|
+
const onChange = React.useCallback((value, event) => {
|
|
127
|
+
const isAutoFilled = document.activeElement !== event?.target;
|
|
128
|
+
|
|
129
|
+
if (fieldsetRef.current) {
|
|
130
|
+
const values = Array.from(fieldsetRef.current.querySelectorAll("input")).reduce((acc, checkbox) => {
|
|
131
|
+
const parent = checkbox.closest("label");
|
|
132
|
+
const value = parent.textContent.trim();
|
|
133
|
+
const {
|
|
134
|
+
checked
|
|
135
|
+
} = checkbox;
|
|
136
|
+
return checked ? [...acc, value] : acc;
|
|
137
|
+
}, []);
|
|
138
|
+
setData(data => ({ ...data,
|
|
139
|
+
[name]: {
|
|
140
|
+
value: values,
|
|
141
|
+
valid: true
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
setValid(true);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (type === "number" || isAutoFilled) {
|
|
149
|
+
const isValid = validate(type, value, required);
|
|
150
|
+
setData(data => ({ ...data,
|
|
151
|
+
[name]: { ...data[name],
|
|
152
|
+
value,
|
|
153
|
+
valid: isValid
|
|
154
|
+
}
|
|
155
|
+
}));
|
|
156
|
+
setValid(isValid);
|
|
157
|
+
setActivated(true);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setData(data => ({ ...data,
|
|
162
|
+
[name]: { ...data[name],
|
|
163
|
+
value
|
|
164
|
+
}
|
|
165
|
+
}));
|
|
166
|
+
}, [name, required, setActivated, setData, setValid, type]);
|
|
167
|
+
const props = getProps(type, name, fieldClasses, data, required, placeholder, autoSizing, onChange);
|
|
168
|
+
|
|
169
|
+
const onBlur = e => {
|
|
170
|
+
const {
|
|
171
|
+
value
|
|
172
|
+
} = e.target;
|
|
173
|
+
const isValid = validate(type, value, required);
|
|
174
|
+
setData(data => ({ ...data,
|
|
175
|
+
[name]: { ...data[name],
|
|
176
|
+
valid: isValid
|
|
177
|
+
}
|
|
178
|
+
}));
|
|
179
|
+
setValid(isValid);
|
|
180
|
+
setActivated(true);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
React.useEffect(() => {
|
|
184
|
+
if (name === "subject") {
|
|
185
|
+
const getSubjects = async () => {
|
|
186
|
+
const body = kind ? {
|
|
187
|
+
kind
|
|
188
|
+
} : {};
|
|
189
|
+
const subjects = await Fetcher.post(`https://contacts.valraiso.net/api/subjects/${site}`, body);
|
|
190
|
+
|
|
191
|
+
if (subjects?.length === 1) {
|
|
192
|
+
const value = subjects?.[0]?.value || "";
|
|
193
|
+
onChange(value);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
setOptions(subjects);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
getSubjects();
|
|
200
|
+
}
|
|
201
|
+
}, [kind, name, onChange, site]);
|
|
202
|
+
|
|
203
|
+
if (type === "checkbox-group") {
|
|
204
|
+
return _jsxs("fieldset", {
|
|
205
|
+
ref: fieldsetRef,
|
|
206
|
+
children: [label && _jsx("legend", {
|
|
207
|
+
children: label
|
|
208
|
+
}), _jsx(Checkboxes, {
|
|
209
|
+
checkboxes: options,
|
|
210
|
+
data: data,
|
|
211
|
+
name: name,
|
|
212
|
+
onChange: onChange,
|
|
213
|
+
disabled: disabled
|
|
214
|
+
})]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const fieldLabel = getLabel(lang, label, required);
|
|
219
|
+
const isInputValid = required && activated && valid;
|
|
220
|
+
const errorMessage = required && activated && !valid && Messages.get(lang, FIELD_TESTS_ERROR_CODES[type]);
|
|
221
|
+
return _jsx(Tag, {
|
|
222
|
+
label: fieldLabel,
|
|
223
|
+
options: options,
|
|
224
|
+
disabled: disabled,
|
|
225
|
+
valid: isInputValid,
|
|
226
|
+
error: errorMessage,
|
|
227
|
+
onBlur: onBlur,
|
|
228
|
+
...props
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const Field = ({
|
|
233
|
+
kind,
|
|
234
|
+
name,
|
|
235
|
+
field,
|
|
236
|
+
data,
|
|
237
|
+
setData,
|
|
238
|
+
settings,
|
|
239
|
+
presets
|
|
240
|
+
}) => {
|
|
241
|
+
const {
|
|
242
|
+
lang,
|
|
243
|
+
config
|
|
244
|
+
} = useUbloContext();
|
|
245
|
+
const {
|
|
246
|
+
type,
|
|
247
|
+
label,
|
|
248
|
+
hidden,
|
|
249
|
+
fullWidth,
|
|
250
|
+
required
|
|
251
|
+
} = field;
|
|
252
|
+
const [activated, setActivated] = React.useState(false);
|
|
253
|
+
const [valid, setValid] = React.useState(!required || false);
|
|
254
|
+
const disabled = presets && Object.keys(presets).includes(name);
|
|
255
|
+
const isTitle = type === "title";
|
|
256
|
+
const classes = classnames({
|
|
257
|
+
[styles.fieldFullWidth]: isTitle || fullWidth,
|
|
258
|
+
[styles.fieldHidden]: hidden
|
|
259
|
+
});
|
|
260
|
+
if (isTitle) return _jsx("div", {
|
|
261
|
+
className: styles.title,
|
|
262
|
+
children: label
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (type === "attachment") {
|
|
266
|
+
return _jsx(Attachment, {
|
|
267
|
+
lang: lang,
|
|
268
|
+
name: name,
|
|
269
|
+
label: label,
|
|
270
|
+
data: data,
|
|
271
|
+
setData: setData,
|
|
272
|
+
settings: settings
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return _jsx(Inner, {
|
|
277
|
+
site: config.site,
|
|
278
|
+
kind: kind,
|
|
279
|
+
label: label,
|
|
280
|
+
lang: lang,
|
|
281
|
+
name: name,
|
|
282
|
+
classes: classes,
|
|
283
|
+
field: field,
|
|
284
|
+
data: data,
|
|
285
|
+
setData: setData,
|
|
286
|
+
disabled: disabled,
|
|
287
|
+
activated: activated,
|
|
288
|
+
setActivated: setActivated,
|
|
289
|
+
valid: valid,
|
|
290
|
+
setValid: setValid
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export default Field;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.fieldHidden {
|
|
2
|
+
display: none !important;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
@media (min-width: 780px) {
|
|
6
|
+
.fieldFullWidth,
|
|
7
|
+
.title {
|
|
8
|
+
grid-column: 1 / -1;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.title {
|
|
13
|
+
color: var(--ds-grey-700, #171e30);
|
|
14
|
+
font-size: 14px;
|
|
15
|
+
text-transform: uppercase;
|
|
16
|
+
font-weight: 700;
|
|
17
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
const Icon = ({
|
|
6
|
+
children,
|
|
7
|
+
...props
|
|
8
|
+
}) => _jsx("svg", {
|
|
9
|
+
viewBox: "0 0 24 24",
|
|
10
|
+
...props,
|
|
11
|
+
children: children
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const Upload = props => {
|
|
15
|
+
return _jsxs(Icon, { ...props,
|
|
16
|
+
children: [_jsx("path", {
|
|
17
|
+
d: "M11.3 11.3a1 1 0 0 1 1.4 0l4 4a1 1 0 0 1-1.4 1.4L12 13.42l-3.3 3.3a1 1 0 0 1-1.4-1.42l4-4Z"
|
|
18
|
+
}), _jsx("path", {
|
|
19
|
+
d: "M12 11a1 1 0 0 1 1 1v9a1 1 0 1 1-2 0v-9a1 1 0 0 1 1-1Z"
|
|
20
|
+
}), _jsx("path", {
|
|
21
|
+
d: "M8.66 2a9 9 0 0 1 8.82 6H18a6 6 0 0 1 2.87 11.27 1 1 0 0 1-.96-1.76A4 4 0 0 0 18 10h-1.26a1 1 0 0 1-.97-.75 7 7 0 1 0-12.02 6.39 1 1 0 1 1-1.5 1.32A9 9 0 0 1 8.66 2.01Z"
|
|
22
|
+
}), _jsx("path", {
|
|
23
|
+
d: "M11.3 11.3a1 1 0 0 1 1.4 0l4 4a1 1 0 0 1-1.4 1.4L12 13.42l-3.3 3.3a1 1 0 0 1-1.4-1.42l4-4Z"
|
|
24
|
+
})]
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
export const File = props => {
|
|
28
|
+
return _jsxs(Icon, { ...props,
|
|
29
|
+
children: [_jsx("path", {
|
|
30
|
+
d: "M3.88 1.88A3 3 0 0 1 6 1h7a1 1 0 0 1 .7.3l7 7a1 1 0 0 1 .3.7v11a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V4a3 3 0 0 1 .88-2.12ZM6 3a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9.41L12.59 3H6Z"
|
|
31
|
+
}), _jsx("path", {
|
|
32
|
+
d: "M13 1a1 1 0 0 1 1 1v6h6a1 1 0 1 1 0 2h-7a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1Z"
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
export const Bin = props => {
|
|
37
|
+
return _jsxs(Icon, { ...props,
|
|
38
|
+
children: [_jsx("path", {
|
|
39
|
+
d: "M2 6a1 1 0 011-1h18a1 1 0 110 2H3a1 1 0 01-1-1z"
|
|
40
|
+
}), _jsx("path", {
|
|
41
|
+
fillRule: "evenodd",
|
|
42
|
+
clipRule: "evenodd",
|
|
43
|
+
d: "M10 3a1 1 0 00-1 1v1h6V4a1 1 0 00-1-1h-4zm7 2V4a3 3 0 00-3-3h-4a3 3 0 00-3 3v1H5a1 1 0 00-1 1v14a3 3 0 003 3h10a3 3 0 003-3V6a1 1 0 00-1-1h-2zM6 7v13a1 1 0 001 1h10a1 1 0 001-1V7H6z"
|
|
44
|
+
}), _jsx("path", {
|
|
45
|
+
d: "M10 10a1 1 0 011 1v6a1 1 0 11-2 0v-6a1 1 0 011-1zM14 10a1 1 0 011 1v6a1 1 0 11-2 0v-6a1 1 0 011-1z"
|
|
46
|
+
})]
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
export const Spinner = props => {
|
|
50
|
+
return _jsx(Icon, { ...props,
|
|
51
|
+
children: _jsx("path", {
|
|
52
|
+
d: "M10.7 3V1.4a1.3 1.3 0 012.6 0V3c0 .7-.6 1.3-1.3 1.3-.7 0-1.3-.6-1.3-1.3zM10.7 22.7V21a1.3 1.3 0 012.6 0v1.8c0 .7-.6 1.3-1.3 1.3-.7 0-1.3-.6-1.3-1.3zM17.4 6.6c-.5-.5-.5-1.3 0-1.8l1.3-1.3a1.3 1.3 0 111.8 1.8l-1.3 1.3a1.3 1.3 0 01-1.8 0zM3.5 20.5c-.5-.5-.5-1.3 0-1.8l1.3-1.3a1.3 1.3 0 011.8 1.8l-1.3 1.3a1.3 1.3 0 01-1.8 0zM21 13.3a1.3 1.3 0 110-2.6h1.7c.7 0 1.3.6 1.3 1.3 0 .7-.6 1.3-1.3 1.3H21zM1.3 13.3a1.3 1.3 0 110-2.6H3a1.3 1.3 0 010 2.6H1.3zM18.7 20.5l-1.3-1.3a1.3 1.3 0 011.8-1.8l1.3 1.3a1.3 1.3 0 01-1 2.2l-.8-.4zM4.8 6.6L3.5 5.3a1.3 1.3 0 011.8-1.8l1.3 1.3a1.3 1.3 0 01-1.8 1.8z"
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
width: 100%;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
margin: 0 auto;
|
|
6
|
+
padding-bottom: 100px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.title {
|
|
10
|
+
color: var(--ds-grey-700, #171e30);
|
|
11
|
+
font-size: 18px;
|
|
12
|
+
line-height: 1.2em;
|
|
13
|
+
text-transform: uppercase;
|
|
14
|
+
font-weight: 700;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@media (min-width: 480px) {
|
|
18
|
+
.title {
|
|
19
|
+
font-size: 25px;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@media (min-width: 1001px) {
|
|
24
|
+
.title {
|
|
25
|
+
font-size: 34px;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.inner {
|
|
30
|
+
display: grid;
|
|
31
|
+
grid-template-columns: 1fr;
|
|
32
|
+
gap: 34px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (min-width: 780px) {
|
|
36
|
+
.inner {
|
|
37
|
+
grid-template-columns: repeat(2, 1fr);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.bottom {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: flex-end;
|
|
45
|
+
margin-top: 20px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.message {
|
|
49
|
+
font-size: 14px;
|
|
50
|
+
color: var(--ds-grey-700, #171e30);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@media (min-width: 480px) {
|
|
54
|
+
.message {
|
|
55
|
+
font-size: 17px;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media (min-width: 1001px) {
|
|
60
|
+
.message {
|
|
61
|
+
font-size: 20px;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.resetButton {
|
|
66
|
+
margin-right: 10px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.submitButton {
|
|
70
|
+
margin-left: auto;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sendingOverlay {
|
|
74
|
+
position: fixed;
|
|
75
|
+
top: 0;
|
|
76
|
+
left: 0;
|
|
77
|
+
width: 100vw;
|
|
78
|
+
height: 100vh;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
background-color: rgba(255, 255, 255, 0.5);
|
|
84
|
+
backdrop-filter: blur(6px);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.sendingIcon {
|
|
88
|
+
width: 30px;
|
|
89
|
+
height: 30px;
|
|
90
|
+
margin-bottom: 20px;
|
|
91
|
+
fill: var(--ds-grey-700, #171e30);
|
|
92
|
+
animation: icon-loading-spin 1280ms
|
|
93
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)) infinite;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes icon-loading-spin {
|
|
97
|
+
100% {
|
|
98
|
+
transform: rotate(360deg);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.sendingMessage {
|
|
103
|
+
max-width: 420px;
|
|
104
|
+
color: var(--ds-grey-700, #171e30);
|
|
105
|
+
font-size: 12px;
|
|
106
|
+
text-align: center;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@media (min-width: 480px) {
|
|
110
|
+
.sendingMessage {
|
|
111
|
+
font-size: 14px;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (min-width: 1001px) {
|
|
116
|
+
.sendingMessage {
|
|
117
|
+
font-size: 17px;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const locales = {
|
|
2
|
+
fr: {
|
|
3
|
+
send: "Envoyer",
|
|
4
|
+
reset: "Réinitialiser le formulaire",
|
|
5
|
+
choose: "Selectionner",
|
|
6
|
+
sentTitle: "Message envoyé !",
|
|
7
|
+
sent: "Votre message a bien été pris en compte.",
|
|
8
|
+
errorTitle: "Erreur",
|
|
9
|
+
error: "Une erreur est survenue, veuillez réessayer.",
|
|
10
|
+
"Internal Server Error": "Une erreur est survenue, veuillez réessayer.",
|
|
11
|
+
settingsError: "Erreur lors du chargement de la configuration du formulaire",
|
|
12
|
+
sending: "Envoi en cours, cette opération peut prendre du temps en fonctionne de la qualité du réseau et de la taille de vos pièces jointes",
|
|
13
|
+
attachmentPlaceholder: "Cliquez sur cet encart ou bien glissez/déposez vos fichiers dedans pour les mettre en ligne.",
|
|
14
|
+
attachmentHelper1: "fichiers maximum",
|
|
15
|
+
attachmentHelper2: "MO par fichier maximum",
|
|
16
|
+
attachmentHelper3: "Formats autorisés :",
|
|
17
|
+
attachmentOverlay: "Déposez vos fichiers ici",
|
|
18
|
+
attachmentFormatError: "Ce format de fichier n'est pas autorisé",
|
|
19
|
+
attachmentNumberError: "Vous avez choisi un trop grand nombre de fichiers",
|
|
20
|
+
attachmentSizeError: "Vous avez choisi un fichier trop lourd",
|
|
21
|
+
MISSING_RECIPIENT: "Aucun sujet selectionné, veuillez réessayer en choisissant un sujet.",
|
|
22
|
+
MISSING_EMAIL: "Aucun email renseigné, veuillez réessayer en renseignant votre adresse email.",
|
|
23
|
+
NO_RECIPIENT_FOUND: "Erreur lors de la récupération du destinataire final.",
|
|
24
|
+
NO_SETTINGS_FOUND: "Erreur lors de la récupération du paramétrage du formulaire.",
|
|
25
|
+
ATTACHMENT_NOT_ALLOWED: "Les pièces jointes ne sont pas autorisées dans ce formulaire.",
|
|
26
|
+
ATTACHMENT_NUMBER_TO_HIGH: "Nombre de pièces jointes trop important.",
|
|
27
|
+
ATTACHMENT_TRUNCATED: "Pièce(s) jointe(s) corrompue(s), veuillez réessayer.",
|
|
28
|
+
ATTACHMENT_TO_BIG: "Pièce(s) jointe(s) trop lourdes, veuillez réessayer avec d'autres fichiers.",
|
|
29
|
+
WRONG_ATTACHMENT_FORMAT: "Pièce(s) jointe(s) au format incompatible.",
|
|
30
|
+
TESTS_TEXT_ERROR: "Ce champ doit faire au moins 2 caractères",
|
|
31
|
+
TESTS_EMAIL_ERROR: 'L\'email doit être au format "john.doe@domaine.com"',
|
|
32
|
+
TESTS_DATE_ERROR: 'La date doit être format "AAAA-MM-JJ"',
|
|
33
|
+
TESTS_PHONE_ERROR: 'Le téléphone doit être au format "0612345678" ou "+33612345678"',
|
|
34
|
+
TESTS_TEXTAREA_ERROR: "Ce champ ne doit pas être vide",
|
|
35
|
+
TESTS_SELECT_ERROR: "Ce champ ne doit pas être vide",
|
|
36
|
+
optionnal: "optionnel"
|
|
37
|
+
},
|
|
38
|
+
en: {
|
|
39
|
+
send: "Send",
|
|
40
|
+
choose: "Choose an option",
|
|
41
|
+
sentTitle: "Message sent!",
|
|
42
|
+
sent: "Your message has been sent.",
|
|
43
|
+
errorTitle: "Error",
|
|
44
|
+
error: "An error occured, please try again.",
|
|
45
|
+
"Internal Server Error": "An error occured, please try again.",
|
|
46
|
+
settingsError: "An error occured while loading the form configuration",
|
|
47
|
+
sending: "Sending... This operation can take a while depending on your network quality and the size of the attachment",
|
|
48
|
+
attachmentPlaceholder: "Clic on this area or drag'n'drop your files inside in order to upload them.",
|
|
49
|
+
attachmentHelper1: "files maximum",
|
|
50
|
+
attachmentHelper2: "MB maximum for each file",
|
|
51
|
+
attachmentHelper3: "Authorized formats:",
|
|
52
|
+
attachmentOverlay: "Drop your files here",
|
|
53
|
+
attachmentFormatError: "This file format is not authorized",
|
|
54
|
+
attachmentNumberError: "You have chosen to much files",
|
|
55
|
+
attachmentSizeError: "You have chosen a file to big",
|
|
56
|
+
MISSING_RECIPIENT: "No subject selected, please try again after selecting a subject.",
|
|
57
|
+
MISSING_EMAIL: "No email provied, please try again after filling the email field.",
|
|
58
|
+
NO_RECIPIENT_FOUND: "Error while retrieving the final recipient.",
|
|
59
|
+
NO_SETTINGS_FOUND: "Error while getting the form settings.",
|
|
60
|
+
ATTACHMENT_NOT_ALLOWED: "Attachments are not allowed in this form.",
|
|
61
|
+
ATTACHMENT_NUMBER_TO_HIGH: "Attachment number to high.",
|
|
62
|
+
ATTACHMENT_TRUNCATED: "Corrupted attachment(s), please try again with other files.",
|
|
63
|
+
ATTACHMENT_TO_BIG: "Attachment(s) to big, please try again with other files.",
|
|
64
|
+
WRONG_ATTACHMENT_FORMAT: "Attachment(s) in the wrong format, please try again with other files.",
|
|
65
|
+
TESTS_TEXT_ERROR: "This field must be at least 2 characters long",
|
|
66
|
+
TESTS_EMAIL_ERROR: 'The email must be in the format "john.doe@domain.com"',
|
|
67
|
+
TESTS_DATE_ERROR: 'The date must be in the following format: "YYYY-MM-DD"',
|
|
68
|
+
TESTS_PHONE_ERROR: 'The date must be in the following formats: "0612345678" or "+33612345678"',
|
|
69
|
+
TESTS_TEXTAREA_ERROR: "this field cannot be empty",
|
|
70
|
+
TESTS_SELECT_ERROR: "this field cannot be empty",
|
|
71
|
+
optionnal: "optionnal"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export const get = (lang, id) => {
|
|
75
|
+
const _lang = lang === "fr" ? "fr" : "en";
|
|
76
|
+
|
|
77
|
+
if (!locales || !locales[_lang]) return id;
|
|
78
|
+
return locales[_lang][id] || locales.fr[id] || `??${id}??`;
|
|
79
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as Fetcher from "../../utils/fetcher";
|
|
2
|
+
export const TESTS = {
|
|
3
|
+
text: /^[^%]{2,}$/,
|
|
4
|
+
date: /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/,
|
|
5
|
+
email: /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i,
|
|
6
|
+
phone: /^\+?[0-9]{3}-?[0-9]{6,12}$/,
|
|
7
|
+
textarea: /^(?!\s*$).+/,
|
|
8
|
+
select: /^(?!\s*$).+/
|
|
9
|
+
};
|
|
10
|
+
export const FIELD_TESTS_ERROR_CODES = {
|
|
11
|
+
text: "TESTS_TEXT_ERROR",
|
|
12
|
+
date: "TESTS_DATE_ERROR",
|
|
13
|
+
email: "TESTS_EMAIL_ERROR",
|
|
14
|
+
phone: "TESTS_PHONE_ERROR",
|
|
15
|
+
textarea: "TESTS_TEXTAREA_ERROR",
|
|
16
|
+
select: "TESTS_SELECT_ERROR"
|
|
17
|
+
};
|
|
18
|
+
const IGNORED_FIELDS = ["title", "subject", "attachment", "checkbox", "checkbox-group"];
|
|
19
|
+
export const getInitialFormState = (fields, presets) => Object.keys(fields).reduce((acc, key) => {
|
|
20
|
+
const field = fields[key];
|
|
21
|
+
const {
|
|
22
|
+
required,
|
|
23
|
+
type
|
|
24
|
+
} = field;
|
|
25
|
+
if (type === "title") return acc;
|
|
26
|
+
if (key === "subject") return { ...acc,
|
|
27
|
+
[key]: {
|
|
28
|
+
value: "",
|
|
29
|
+
valid: true
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (type === "checkbox") {
|
|
34
|
+
const value = presets?.[key] ?? false;
|
|
35
|
+
return { ...acc,
|
|
36
|
+
[key]: {
|
|
37
|
+
value,
|
|
38
|
+
valid: validate(type, value, required)
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (type === "number") {
|
|
44
|
+
const value = presets?.[key] ?? 0;
|
|
45
|
+
return { ...acc,
|
|
46
|
+
[key]: {
|
|
47
|
+
value,
|
|
48
|
+
valid: validate(type, value, required)
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (type === "checkbox-group") {
|
|
54
|
+
const value = presets?.[key].length || [];
|
|
55
|
+
return { ...acc,
|
|
56
|
+
[key]: {
|
|
57
|
+
value,
|
|
58
|
+
valid: validate(type, value, required)
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const value = presets?.[key] ?? "";
|
|
64
|
+
return { ...acc,
|
|
65
|
+
[key]: {
|
|
66
|
+
value,
|
|
67
|
+
valid: validate(type, value, required)
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}, {});
|
|
71
|
+
export const validate = (type, value, required) => {
|
|
72
|
+
if (IGNORED_FIELDS.includes(type) || !required) return true;
|
|
73
|
+
if (type === "number") return value >= 0;
|
|
74
|
+
if (!value) return false;
|
|
75
|
+
const regex = TESTS[type];
|
|
76
|
+
if (!regex) return false;
|
|
77
|
+
return regex.test(value);
|
|
78
|
+
};
|
|
79
|
+
export const convertToFileList = list => {
|
|
80
|
+
const dataTransfer = new DataTransfer();
|
|
81
|
+
list.forEach(item => dataTransfer.items.add(item));
|
|
82
|
+
return dataTransfer.files;
|
|
83
|
+
};
|
|
84
|
+
export const send = async ({
|
|
85
|
+
site,
|
|
86
|
+
data,
|
|
87
|
+
fields,
|
|
88
|
+
subjectPrefix,
|
|
89
|
+
subjectSuffix
|
|
90
|
+
}) => {
|
|
91
|
+
const endpoint = `https://contacts.valraiso.net/api/contact/${site}`;
|
|
92
|
+
const dataKeys = Object.keys(data);
|
|
93
|
+
const formData = new FormData();
|
|
94
|
+
dataKeys.forEach(key => {
|
|
95
|
+
if (key === "attachment") {
|
|
96
|
+
return Array.from(data[key].value).forEach(file => {
|
|
97
|
+
formData.append(key, file);
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
const {
|
|
101
|
+
formatter
|
|
102
|
+
} = fields[key];
|
|
103
|
+
const formatedValue = typeof formatter === "function" ? formatter(data[key].value) : data[key].value;
|
|
104
|
+
formData.append(key, formatedValue);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (subjectPrefix) {
|
|
109
|
+
formData.append("subjectPrefix", subjectPrefix);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (subjectSuffix) {
|
|
113
|
+
formData.append("subjectSuffix", subjectSuffix);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const response = await fetch(endpoint, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
body: formData
|
|
119
|
+
});
|
|
120
|
+
const json = response.json();
|
|
121
|
+
return json;
|
|
122
|
+
};
|
|
123
|
+
export const getSettings = async site => {
|
|
124
|
+
const endpoint = `https://contacts.valraiso.net/api/settings/${site}`;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const response = await Fetcher.get(endpoint);
|
|
128
|
+
return response;
|
|
129
|
+
} catch (e) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
};
|