@ticketboothapp/booking 0.1.19 → 0.1.20

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 (154) hide show
  1. package/package.json +1 -1
  2. package/src/components/BookingWidget.tsx +282 -26
  3. package/src/components/ManageBookingView.tsx +75 -23
  4. package/src/components/booking/BookingProductGrid.tsx +1 -1
  5. package/src/components/booking/Calendar.module.css +3 -3
  6. package/src/components/booking/CheckoutForm.tsx +1 -1
  7. package/src/components/booking/InfoTooltip.tsx +2 -13
  8. package/src/components/booking/PickupLocationSelector.tsx +2 -2
  9. package/src/components/booking/PriceBreakdown.tsx +11 -34
  10. package/src/index.ts +3 -1
  11. package/src/app/photo-sessions/photo-packages.ts +0 -75
  12. package/src/assets/icons/minus.svg +0 -7
  13. package/src/assets/icons/partner-logos/getyourguide.svg +0 -8
  14. package/src/assets/icons/plus.svg +0 -3
  15. package/src/colours.css +0 -23
  16. package/src/components/BookingDetails.module.css +0 -1591
  17. package/src/components/BookingDetails.tsx +0 -2264
  18. package/src/components/JobApplicationDialog.module.css +0 -440
  19. package/src/components/JobApplicationDialog.tsx +0 -620
  20. package/src/components/PhoneInputWithCountry.module.css +0 -131
  21. package/src/components/PhoneInputWithCountry.tsx +0 -44
  22. package/src/components/PickupLocationDialog.module.css +0 -360
  23. package/src/components/PickupLocationDialog.tsx +0 -357
  24. package/src/components/PickupLocationMap.tsx +0 -110
  25. package/src/components/PostBookingDependentAddOnUpsell.module.css +0 -174
  26. package/src/components/PostBookingDependentAddOnUpsell.tsx +0 -407
  27. package/src/components/accordion.css +0 -27
  28. package/src/components/accordion.tsx +0 -29
  29. package/src/components/analytics/AnalyticsConsentRestore.tsx +0 -19
  30. package/src/components/analytics/AnalyticsScripts.tsx +0 -106
  31. package/src/components/analytics/CookieConsentBanner.css +0 -86
  32. package/src/components/analytics/CookieConsentBanner.tsx +0 -102
  33. package/src/components/bottom-sheet.module.css +0 -78
  34. package/src/components/bottom-sheet.tsx +0 -60
  35. package/src/components/breadcrumb.module.css +0 -40
  36. package/src/components/breadcrumb.tsx +0 -36
  37. package/src/components/button.css +0 -245
  38. package/src/components/button.tsx +0 -152
  39. package/src/components/client-bottom-sheet.tsx +0 -14
  40. package/src/components/colorable-svg.tsx +0 -29
  41. package/src/components/conditional-footer.tsx +0 -27
  42. package/src/components/contact-us.module.css +0 -147
  43. package/src/components/contact-us.tsx +0 -49
  44. package/src/components/email-signup.css +0 -151
  45. package/src/components/email-signup.tsx +0 -63
  46. package/src/components/faq-wrapper.module.css +0 -47
  47. package/src/components/faq-wrapper.tsx +0 -15
  48. package/src/components/footer.css +0 -187
  49. package/src/components/footer.tsx +0 -143
  50. package/src/components/global-simple-modal.tsx +0 -33
  51. package/src/components/google-review-summary.module.css +0 -77
  52. package/src/components/google-review-summary.tsx +0 -50
  53. package/src/components/hero-image.css +0 -13
  54. package/src/components/hero-image.tsx +0 -44
  55. package/src/components/image.css +0 -29
  56. package/src/components/image.tsx +0 -113
  57. package/src/components/language-aware-link.tsx +0 -72
  58. package/src/components/language-switcher.module.css +0 -124
  59. package/src/components/language-switcher.tsx +0 -75
  60. package/src/components/map-section.css +0 -59
  61. package/src/components/map-section.tsx +0 -63
  62. package/src/components/navbar.module.css +0 -152
  63. package/src/components/navbar.tsx +0 -125
  64. package/src/components/parallax-provider.tsx +0 -11
  65. package/src/components/product-tag.module.css +0 -30
  66. package/src/components/product-tag.tsx +0 -34
  67. package/src/components/product-theme-pages/best-option.module.css +0 -70
  68. package/src/components/product-theme-pages/best-option.tsx +0 -35
  69. package/src/components/product-theme-pages/extended-tour-options.module.css +0 -22
  70. package/src/components/product-theme-pages/extended-tour-options.tsx +0 -11
  71. package/src/components/product-theme-pages/image-modal.tsx +0 -248
  72. package/src/components/product-theme-pages/photo-gallery.module.css +0 -200
  73. package/src/components/product-theme-pages/photo-gallery.tsx +0 -90
  74. package/src/components/product-theme-pages/product-theme-page-layout.module.css +0 -13
  75. package/src/components/product-theme-pages/product-theme-page-layout.tsx +0 -67
  76. package/src/components/product-theme-pages/top-of-fold.module.css +0 -179
  77. package/src/components/product-theme-pages/top-of-fold.tsx +0 -80
  78. package/src/components/product-tile/image-only-product-tile-desktop.module.css +0 -106
  79. package/src/components/product-tile/image-only-product-tile-desktop.tsx +0 -56
  80. package/src/components/product-tile/image-only-product-tile-mobile.module.css +0 -122
  81. package/src/components/product-tile/image-only-product-tile-mobile.tsx +0 -89
  82. package/src/components/product-tile/image-only-product-tile.tsx +0 -44
  83. package/src/components/product-tile/product-tile-card.module.css +0 -84
  84. package/src/components/product-tile/product-tile-card.tsx +0 -61
  85. package/src/components/review-highlights-section.css +0 -85
  86. package/src/components/review-highlights-section.tsx +0 -127
  87. package/src/components/season-closure-overlay.module.css +0 -99
  88. package/src/components/season-closure-overlay.tsx +0 -98
  89. package/src/components/simple-modal.tsx +0 -69
  90. package/src/components/simple-top-of-fold.module.css +0 -76
  91. package/src/components/simple-top-of-fold.tsx +0 -34
  92. package/src/components/spacer.css +0 -41
  93. package/src/components/spacer.tsx +0 -23
  94. package/src/components/star-rating.module.css +0 -74
  95. package/src/components/star-rating.tsx +0 -48
  96. package/src/components/terms/TermsContent.tsx +0 -178
  97. package/src/components/title-subtitle.module.css +0 -10
  98. package/src/components/title-subtitle.tsx +0 -30
  99. package/src/components/translatable-reviews.tsx +0 -75
  100. package/src/components/value-pill.module.css +0 -59
  101. package/src/components/value-pill.tsx +0 -46
  102. package/src/components/value-props.css +0 -185
  103. package/src/components/value-props.tsx +0 -88
  104. package/src/constants/booking-guide-quiz.ts +0 -64
  105. package/src/constants/contact-info.ts +0 -2
  106. package/src/constants/faq.ts +0 -44
  107. package/src/constants/images.ts +0 -556
  108. package/src/constants/json-ld/faq-json-ld.tsx +0 -170
  109. package/src/constants/json-ld/homepage-json-ld.tsx +0 -138
  110. package/src/constants/json-ld/job-posting-json-ld.tsx +0 -92
  111. package/src/constants/json-ld/organization-json-ld.tsx +0 -62
  112. package/src/constants/json-ld/page-json-ld.tsx +0 -6
  113. package/src/constants/json-ld/product-json-ld.tsx +0 -154
  114. package/src/constants/json-ld/review-json-ld.tsx +0 -377
  115. package/src/constants/navigation-links/footer-links.ts +0 -48
  116. package/src/constants/navigation-links/nav-bar-links.ts +0 -41
  117. package/src/constants/navigation-links/navigation-link.ts +0 -6
  118. package/src/constants/pill-values.ts +0 -210
  119. package/src/constants/products.ts +0 -155
  120. package/src/constants/quiz-recommendations.ts +0 -506
  121. package/src/constants/reviews.ts +0 -75
  122. package/src/constants/staff.ts +0 -197
  123. package/src/constants/value-props.ts +0 -58
  124. package/src/data/dap-descriptions/session-couples-families-friends.en.json +0 -61
  125. package/src/data/dap-descriptions/session-elopements.en.json +0 -60
  126. package/src/data/dap-descriptions/session-proposals.en.json +0 -60
  127. package/src/data/product-descriptions/afternoon-delight.en.json +0 -35
  128. package/src/data/product-descriptions/emerald-lake-escape.en.json +0 -68
  129. package/src/data/product-descriptions/lake-louise-adventure.en.json +0 -74
  130. package/src/data/product-descriptions/moraine-lake-adventure.en.json +0 -78
  131. package/src/data/product-descriptions/moraine-lake-sunrise-lake-louise-golden-hour.en.json +0 -65
  132. package/src/data/product-descriptions/moraine-lake-sunrise.en.json +0 -64
  133. package/src/data/product-descriptions/private-tour.en.json +0 -80
  134. package/src/data/product-descriptions/two-lakes-combo.en.json +0 -65
  135. package/src/data/products-config.json +0 -101
  136. package/src/hooks/use-bottom-sheet.tsx +0 -15
  137. package/src/hooks/use-simple-modal.tsx +0 -27
  138. package/src/hooks/useBookingSourceMetadataFromLocation.ts +0 -21
  139. package/src/hooks/useEmailSubscription.tsx +0 -103
  140. package/src/hooks/useEmbeddedInIframe.ts +0 -16
  141. package/src/hooks/useIsBookingLaunchLive.ts +0 -49
  142. package/src/hooks/useQuiz.tsx +0 -210
  143. package/src/providers/bottom-sheet-provider.tsx +0 -40
  144. package/src/providers/dependent-add-on-dialog-provider.tsx +0 -105
  145. package/src/radius.css +0 -5
  146. package/src/spacing.css +0 -7
  147. package/src/strings/en.json +0 -1774
  148. package/src/strings/es.json +0 -1573
  149. package/src/strings/fr.json +0 -1573
  150. package/src/strings/index.js +0 -23
  151. package/src/text-style.css +0 -97
  152. package/src/types/fareharbor.d.ts +0 -12
  153. package/src/types/quiz.ts +0 -59
  154. package/src/utils/currency-converter.ts +0 -101
@@ -1,620 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useState, useEffect } from 'react';
4
- import {
5
- getPresignedUploadUrl,
6
- uploadFileToPresignedUrl,
7
- submitJobApplication,
8
- type SubmitApplicationPayload,
9
- } from '@/lib/job-application-api';
10
- import strings from '@/strings';
11
- import PhoneInputWithCountry from '@/components/PhoneInputWithCountry';
12
- import styles from './JobApplicationDialog.module.css';
13
- import Button, { ButtonHoverColor } from './button';
14
-
15
- const JOB_ID_GUEST_EXPERIENCE = 'guest-experience-coordinator';
16
-
17
- function YesNoField({
18
- id,
19
- label,
20
- value,
21
- onChange,
22
- t,
23
- submitting,
24
- required,
25
- }: {
26
- id: string;
27
- label: string;
28
- value: string;
29
- onChange: (v: string) => void;
30
- t: (key: string) => string;
31
- submitting: boolean;
32
- required?: boolean;
33
- }) {
34
- return (
35
- <div className={styles.formGroup}>
36
- <label className={styles.label}>
37
- {label} {required && '*'}
38
- </label>
39
- <div className={styles.radioGroup}>
40
- <label className={styles.radioLabel}>
41
- <input
42
- type="radio"
43
- name={id}
44
- value="YES"
45
- checked={value === 'YES'}
46
- onChange={(e) => onChange(e.target.value)}
47
- disabled={submitting}
48
- required={required}
49
- />
50
- <span>{t('yes')}</span>
51
- </label>
52
- <label className={styles.radioLabel}>
53
- <input
54
- type="radio"
55
- name={id}
56
- value="NO"
57
- checked={value === 'NO'}
58
- onChange={(e) => onChange(e.target.value)}
59
- disabled={submitting}
60
- />
61
- <span>{t('no')}</span>
62
- </label>
63
- </div>
64
- </div>
65
- );
66
- }
67
-
68
- interface JobApplicationDialogProps {
69
- isOpen: boolean;
70
- onClose: () => void;
71
- jobId: string;
72
- jobTitle: string;
73
- jobDescription: string;
74
- jobDescriptionExpanded?: string;
75
- }
76
-
77
- export default function JobApplicationDialog({
78
- isOpen,
79
- onClose,
80
- jobId,
81
- jobTitle,
82
- jobDescription,
83
- jobDescriptionExpanded,
84
- }: JobApplicationDialogProps) {
85
- const jobAppStrings = (strings as { careers?: { jobApplication?: Record<string, string> } }).careers?.jobApplication;
86
- const t = (key: string) => jobAppStrings?.[key] ?? key;
87
-
88
- const [name, setName] = useState('');
89
- const [email, setEmail] = useState('');
90
- const [phone, setPhone] = useState('');
91
- const [eligibleToWorkInCanada, setEligibleToWorkInCanada] = useState<string>('');
92
- const [accommodationInBowValley, setAccommodationInBowValley] = useState<string>('');
93
- const [whyJoinViaVia, setWhyJoinViaVia] = useState('');
94
- const [comfortableEarlyMornings, setComfortableEarlyMornings] = useState<string>('');
95
- const [languagesSpoken, setLanguagesSpoken] = useState('');
96
- const [fastPacedExperience, setFastPacedExperience] = useState<string>('');
97
- const [whatMakesYouReliable, setWhatMakesYouReliable] = useState('');
98
- const [eligibleToWorkInCanadaExplain, setEligibleToWorkInCanadaExplain] = useState('');
99
- const [accommodationInBowValleyExplain, setAccommodationInBowValleyExplain] = useState('');
100
- const [fastPacedExperienceExplain, setFastPacedExperienceExplain] = useState('');
101
- const [resumeFile, setResumeFile] = useState<File | null>(null);
102
- const [coverLetterFile, setCoverLetterFile] = useState<File | null>(null);
103
- const [descriptionExpanded, setDescriptionExpanded] = useState(false);
104
- const [error, setError] = useState<string | null>(null);
105
-
106
- useEffect(() => {
107
- if (isOpen) setDescriptionExpanded(false);
108
- }, [isOpen]);
109
- const [success, setSuccess] = useState(false);
110
- const [submitting, setSubmitting] = useState(false);
111
- const [showCloseConfirm, setShowCloseConfirm] = useState(false);
112
-
113
- const hasMeaningfulPhone = phone.replace(/\D/g, '').length > 1;
114
- const isFormDirty = Boolean(
115
- name.trim() ||
116
- email.trim() ||
117
- hasMeaningfulPhone ||
118
- eligibleToWorkInCanada ||
119
- accommodationInBowValley ||
120
- whyJoinViaVia.trim() ||
121
- comfortableEarlyMornings ||
122
- languagesSpoken.trim() ||
123
- fastPacedExperience ||
124
- whatMakesYouReliable.trim() ||
125
- eligibleToWorkInCanadaExplain.trim() ||
126
- accommodationInBowValleyExplain.trim() ||
127
- fastPacedExperienceExplain.trim() ||
128
- resumeFile ||
129
- coverLetterFile
130
- );
131
-
132
- const resetForm = () => {
133
- setName('');
134
- setEmail('');
135
- setPhone('');
136
- setEligibleToWorkInCanada('');
137
- setAccommodationInBowValley('');
138
- setWhyJoinViaVia('');
139
- setComfortableEarlyMornings('');
140
- setLanguagesSpoken('');
141
- setFastPacedExperience('');
142
- setWhatMakesYouReliable('');
143
- setEligibleToWorkInCanadaExplain('');
144
- setAccommodationInBowValleyExplain('');
145
- setFastPacedExperienceExplain('');
146
- setResumeFile(null);
147
- setCoverLetterFile(null);
148
- setError(null);
149
- setSuccess(false);
150
- setShowCloseConfirm(false);
151
- };
152
-
153
- const handleClose = () => {
154
- setShowCloseConfirm(false);
155
- resetForm();
156
- onClose();
157
- };
158
-
159
- const requestClose = () => {
160
- if (!success && isFormDirty) {
161
- setShowCloseConfirm(true);
162
- } else {
163
- handleClose();
164
- }
165
- };
166
-
167
- const handleSubmit = async (e: React.FormEvent) => {
168
- e.preventDefault();
169
- setError(null);
170
-
171
- if (!name.trim()) {
172
- setError(t('name') + ' is required');
173
- return;
174
- }
175
- if (!email.trim()) {
176
- setError(t('email') + ' is required');
177
- return;
178
- }
179
- if (!email.includes('@')) {
180
- setError('Please enter a valid email address');
181
- return;
182
- }
183
- if (!eligibleToWorkInCanada) {
184
- setError(t('eligibleToWorkInCanada') + ' is required');
185
- return;
186
- }
187
- if (eligibleToWorkInCanada === 'NO' && !eligibleToWorkInCanadaExplain.trim()) {
188
- setError(t('followUpRequired'));
189
- return;
190
- }
191
- if (!accommodationInBowValley) {
192
- setError(t('accommodationInBowValley') + ' is required');
193
- return;
194
- }
195
- if (accommodationInBowValley === 'NO' && !accommodationInBowValleyExplain.trim()) {
196
- setError(t('followUpRequired'));
197
- return;
198
- }
199
- if (!whyJoinViaVia.trim()) {
200
- setError(t('whyJoinViaVia') + ' is required');
201
- return;
202
- }
203
- if (!comfortableEarlyMornings) {
204
- setError(t('comfortableEarlyMornings') + ' is required');
205
- return;
206
- }
207
- if (!languagesSpoken.trim()) {
208
- setError(t('languagesSpoken') + ' is required');
209
- return;
210
- }
211
- if (!fastPacedExperience) {
212
- setError(t('fastPacedExperience') + ' is required');
213
- return;
214
- }
215
- if (!whatMakesYouReliable.trim()) {
216
- setError(t('whatMakesYouReliable') + ' is required');
217
- return;
218
- }
219
- if (!resumeFile) {
220
- setError(t('resume') + ' is required');
221
- return;
222
- }
223
-
224
- const allowedResumeTypes = ['.pdf', '.doc', '.docx'];
225
- const resumeExt = resumeFile.name.substring(resumeFile.name.lastIndexOf('.')).toLowerCase();
226
- if (!allowedResumeTypes.includes(resumeExt)) {
227
- setError('Resume must be PDF, DOC, or DOCX');
228
- return;
229
- }
230
-
231
- setSubmitting(true);
232
-
233
- try {
234
- // Upload resume
235
- const resumePresigned = await getPresignedUploadUrl(jobId, 'resume', resumeFile.name);
236
- await uploadFileToPresignedUrl(
237
- resumePresigned.uploadUrl,
238
- resumeFile,
239
- resumePresigned.contentType
240
- );
241
- const resumeS3Key = resumePresigned.s3Key;
242
-
243
- // Upload cover letter if provided
244
- let coverLetterS3Key: string | undefined;
245
- if (coverLetterFile) {
246
- const clPresigned = await getPresignedUploadUrl(jobId, 'coverLetter', coverLetterFile.name);
247
- await uploadFileToPresignedUrl(
248
- clPresigned.uploadUrl,
249
- coverLetterFile,
250
- clPresigned.contentType
251
- );
252
- coverLetterS3Key = clPresigned.s3Key;
253
- }
254
-
255
- const payload: SubmitApplicationPayload = {
256
- jobId,
257
- fullName: name.trim(),
258
- email: email.trim(),
259
- phone: hasMeaningfulPhone ? phone.trim() : undefined,
260
- answers: {
261
- eligibleToWorkInCanada: eligibleToWorkInCanada.trim(),
262
- eligibleToWorkInCanadaExplain: eligibleToWorkInCanadaExplain.trim(),
263
- accommodationInBowValley: accommodationInBowValley.trim(),
264
- accommodationInBowValleyExplain: accommodationInBowValleyExplain.trim(),
265
- whyJoinViaVia: whyJoinViaVia.trim(),
266
- comfortableEarlyMornings: comfortableEarlyMornings.trim(),
267
- languagesSpoken: languagesSpoken.trim(),
268
- fastPacedExperience: fastPacedExperience.trim(),
269
- fastPacedExperienceExplain: fastPacedExperienceExplain.trim(),
270
- whatMakesYouReliable: whatMakesYouReliable.trim(),
271
- },
272
- resumeS3Key,
273
- coverLetterS3Key,
274
- };
275
-
276
- await submitJobApplication(payload);
277
- setSuccess(true);
278
- } catch (err) {
279
- setError(err instanceof Error ? err.message : t('error'));
280
- } finally {
281
- setSubmitting(false);
282
- }
283
- };
284
-
285
- if (!isOpen) return null;
286
-
287
- const handleOverlayClick = () => {
288
- if (showCloseConfirm) {
289
- setShowCloseConfirm(false);
290
- } else {
291
- requestClose();
292
- }
293
- };
294
-
295
- return (
296
- <div
297
- className={styles.overlay}
298
- onClick={handleOverlayClick}
299
- role="dialog"
300
- aria-modal="true"
301
- aria-labelledby="job-application-dialog-title"
302
- >
303
- <div className={styles.modal} onClick={(e) => e.stopPropagation()}>
304
- <div className={`${styles.header} ${descriptionExpanded ? styles.headerExpanded : ''}`}>
305
- <div className={styles.headerContent}>
306
- <h2 id="job-application-dialog-title" className={styles.title}>
307
- {jobTitle}
308
- </h2>
309
- <div
310
- className={`${styles.descriptionSection} ${descriptionExpanded ? styles.descriptionExpanded : ''}`}
311
- >
312
- {descriptionExpanded && jobDescriptionExpanded ? (
313
- <div
314
- className={styles.descriptionExpandedContent}
315
- dangerouslySetInnerHTML={{ __html: jobDescriptionExpanded }}
316
- />
317
- ) : (
318
- <p className={styles.description}>{jobDescription}</p>
319
- )}
320
- <button
321
- type="button"
322
- onClick={() => setDescriptionExpanded((v) => !v)}
323
- className={styles.descriptionToggle}
324
- aria-label={descriptionExpanded ? t('collapseDescription') : t('expandDescription')}
325
- aria-expanded={descriptionExpanded}
326
- >
327
- <svg width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
328
- <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
329
- </svg>
330
- </button>
331
- </div>
332
- </div>
333
- <button
334
- type="button"
335
- onClick={requestClose}
336
- className={styles.closeBtn}
337
- aria-label={t('close')}
338
- >
339
- <svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
340
- <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
341
- </svg>
342
- </button>
343
- </div>
344
-
345
- <div className={styles.formRow}>
346
- <div className={styles.bodyScroll}>
347
- <div className={styles.content}>
348
- {success ? (
349
- <>
350
- <div className={styles.success}>{t('success')}</div>
351
- <div className={styles.footer}>
352
- <button type="button" onClick={handleClose} className={styles.submitBtn}>
353
- {t('close')}
354
- </button>
355
- </div>
356
- </>
357
- ) : showCloseConfirm ? (
358
- <div className={styles.closeConfirm}>
359
- <p className={styles.closeConfirmTitle}>{t('closeConfirmTitle')}</p>
360
- <p className={styles.closeConfirmMessage}>{t('closeConfirmMessage')}</p>
361
- <div className={styles.closeConfirmActions}>
362
- <button
363
- type="button"
364
- onClick={() => setShowCloseConfirm(false)}
365
- className={styles.cancelBtn}
366
- >
367
- {t('keepEditing')}
368
- </button>
369
- <button
370
- type="button"
371
- onClick={handleClose}
372
- className={styles.submitBtn}
373
- >
374
- {t('discard')}
375
- </button>
376
- </div>
377
- </div>
378
- ) : (
379
- <form onSubmit={handleSubmit} className={styles.form}>
380
- <div className={styles.formGroup}>
381
- <label htmlFor="name" className={styles.label}>
382
- {t('name')} *
383
- </label>
384
- <input
385
- id="name"
386
- type="text"
387
- value={name}
388
- onChange={(e) => setName(e.target.value)}
389
- className={styles.input}
390
- required
391
- disabled={submitting}
392
- />
393
- </div>
394
-
395
- <div className={styles.formGroup}>
396
- <label htmlFor="email" className={styles.label}>
397
- {t('email')} *
398
- </label>
399
- <input
400
- id="email"
401
- type="email"
402
- value={email}
403
- onChange={(e) => setEmail(e.target.value)}
404
- className={styles.input}
405
- required
406
- disabled={submitting}
407
- />
408
- </div>
409
-
410
- <div className={styles.formGroup}>
411
- <label htmlFor="phone" className={styles.label}>
412
- {t('phoneNumber')}
413
- </label>
414
- <PhoneInputWithCountry
415
- id="phone"
416
- value={phone}
417
- onChange={(value) => setPhone(value ?? '')}
418
- placeholder="e.g. +1 403 555 0123"
419
- disabled={submitting}
420
- className={styles.phoneInputMatch}
421
- />
422
- </div>
423
-
424
- <YesNoField
425
- id="eligibleToWorkInCanada"
426
- label={t('eligibleToWorkInCanada')}
427
- value={eligibleToWorkInCanada}
428
- onChange={setEligibleToWorkInCanada}
429
- t={t}
430
- submitting={submitting}
431
- required
432
- />
433
- {eligibleToWorkInCanada === 'NO' && (
434
- <div className={styles.formGroup}>
435
- <label htmlFor="eligibleToWorkInCanadaExplain" className={styles.label}>
436
- {t('eligibleToWorkInCanadaFollowUp')} *
437
- </label>
438
- <input
439
- id="eligibleToWorkInCanadaExplain"
440
- type="text"
441
- value={eligibleToWorkInCanadaExplain}
442
- onChange={(e) => setEligibleToWorkInCanadaExplain(e.target.value)}
443
- className={styles.input}
444
- required={eligibleToWorkInCanada === 'NO'}
445
- disabled={submitting}
446
- />
447
- </div>
448
- )}
449
-
450
- <YesNoField
451
- id="accommodationInBowValley"
452
- label={t('accommodationInBowValley')}
453
- value={accommodationInBowValley}
454
- onChange={setAccommodationInBowValley}
455
- t={t}
456
- submitting={submitting}
457
- required
458
- />
459
- {accommodationInBowValley === 'NO' && (
460
- <div className={styles.formGroup}>
461
- <label htmlFor="accommodationInBowValleyExplain" className={styles.label}>
462
- {t('accommodationInBowValleyFollowUp')} *
463
- </label>
464
- <input
465
- id="accommodationInBowValleyExplain"
466
- type="text"
467
- value={accommodationInBowValleyExplain}
468
- onChange={(e) => setAccommodationInBowValleyExplain(e.target.value)}
469
- className={styles.input}
470
- required={accommodationInBowValley === 'NO'}
471
- disabled={submitting}
472
- />
473
- </div>
474
- )}
475
-
476
- <div className={styles.formGroup}>
477
- <label htmlFor="whyJoinViaVia" className={styles.label}>
478
- {t('whyJoinViaVia')} *
479
- </label>
480
- <textarea
481
- id="whyJoinViaVia"
482
- value={whyJoinViaVia}
483
- onChange={(e) => setWhyJoinViaVia(e.target.value)}
484
- className={`${styles.input} ${styles.textarea}`}
485
- disabled={submitting}
486
- required
487
- />
488
- </div>
489
-
490
- <YesNoField
491
- id="comfortableEarlyMornings"
492
- label={t('comfortableEarlyMornings')}
493
- value={comfortableEarlyMornings}
494
- onChange={setComfortableEarlyMornings}
495
- t={t}
496
- submitting={submitting}
497
- required
498
- />
499
-
500
- <div className={styles.formGroup}>
501
- <label htmlFor="languagesSpoken" className={styles.label}>
502
- {t('languagesSpoken')} *
503
- </label>
504
- <input
505
- id="languagesSpoken"
506
- type="text"
507
- value={languagesSpoken}
508
- onChange={(e) => setLanguagesSpoken(e.target.value)}
509
- className={styles.input}
510
- required
511
- disabled={submitting}
512
- />
513
- </div>
514
-
515
- <YesNoField
516
- id="fastPacedExperience"
517
- label={t('fastPacedExperience')}
518
- value={fastPacedExperience}
519
- onChange={setFastPacedExperience}
520
- t={t}
521
- submitting={submitting}
522
- required
523
- />
524
- {(fastPacedExperience === 'YES' || fastPacedExperience === 'NO') && (
525
- <div className={styles.formGroup}>
526
- <label htmlFor="fastPacedExperienceExplain" className={styles.label}>
527
- {t('explainOptional')}
528
- </label>
529
- <input
530
- id="fastPacedExperienceExplain"
531
- type="text"
532
- value={fastPacedExperienceExplain}
533
- onChange={(e) => setFastPacedExperienceExplain(e.target.value)}
534
- className={styles.input}
535
- disabled={submitting}
536
- />
537
- </div>
538
- )}
539
-
540
- <div className={styles.formGroup}>
541
- <label htmlFor="whatMakesYouReliable" className={styles.label}>
542
- {t('whatMakesYouReliable')} *
543
- </label>
544
- <textarea
545
- id="whatMakesYouReliable"
546
- value={whatMakesYouReliable}
547
- onChange={(e) => setWhatMakesYouReliable(e.target.value)}
548
- className={`${styles.input} ${styles.textarea}`}
549
- disabled={submitting}
550
- required
551
- />
552
- </div>
553
-
554
- <div className={styles.formGroup}>
555
- <label className={styles.label}>
556
- {t('resume')} *
557
- </label>
558
- <div className={`${styles.fileInput} ${submitting ? styles.fileInputDisabled : ''}`}>
559
- <input
560
- id="resume-file"
561
- type="file"
562
- accept=".pdf,.doc,.docx"
563
- onChange={(e) => setResumeFile(e.target.files?.[0] ?? null)}
564
- disabled={submitting}
565
- className={styles.fileInputHidden}
566
- />
567
- <label htmlFor="resume-file" className={styles.fileInputButton}>
568
- {t('chooseFile')}
569
- </label>
570
- {resumeFile && <div className={styles.fileName}>{resumeFile.name}</div>}
571
- </div>
572
- </div>
573
-
574
- <div className={styles.formGroup}>
575
- <label className={styles.label}>
576
- {t('coverLetter')}
577
- </label>
578
- <div className={`${styles.fileInput} ${submitting ? styles.fileInputDisabled : ''}`}>
579
- <input
580
- id="cover-letter-file"
581
- type="file"
582
- accept=".pdf,.doc,.docx"
583
- onChange={(e) => setCoverLetterFile(e.target.files?.[0] ?? null)}
584
- disabled={submitting}
585
- className={styles.fileInputHidden}
586
- />
587
- <label htmlFor="cover-letter-file" className={styles.fileInputButton}>
588
- {t('chooseFile')}
589
- </label>
590
- {coverLetterFile && <div className={styles.fileName}>{coverLetterFile.name}</div>}
591
- </div>
592
- </div>
593
-
594
- {error && <div className={styles.error}>{error}</div>}
595
-
596
- <div className={styles.footer}>
597
- <Button
598
- variant={submitting ? 'disabled' : 'primary'}
599
- hoverColor={ButtonHoverColor.Turquoise}
600
- onClick={handleSubmit}
601
- >
602
- {t('submit')}
603
- </Button>
604
- </div>
605
- </form>
606
- )}
607
- </div>
608
- </div>
609
- <div className={styles.rightColumn}>
610
- <p className={styles.rightColumnText}>Good people.</p>
611
- <p className={styles.rightColumnText}>Epic journeys.</p>
612
- <p className={styles.rightColumnText}>Real impact.</p>
613
- </div>
614
- </div>
615
- </div>
616
- </div>
617
- );
618
- }
619
-
620
- export { JOB_ID_GUEST_EXPERIENCE };