@ticketboothapp/booking 0.1.19 → 0.1.22

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 (106) hide show
  1. package/package.json +2 -1
  2. package/src/components/BookingWidget.tsx +282 -26
  3. package/src/components/ManageBookingView.tsx +75 -23
  4. package/src/components/PostBookingDependentAddOnUpsell.tsx +1 -1
  5. package/src/components/booking/BookingProductGrid.tsx +1 -1
  6. package/src/components/booking/Calendar.module.css +3 -3
  7. package/src/components/booking/CheckoutForm.tsx +1 -1
  8. package/src/components/booking/InfoTooltip.tsx +2 -13
  9. package/src/components/booking/PickupLocationSelector.tsx +2 -2
  10. package/src/components/booking/PriceBreakdown.tsx +11 -34
  11. package/src/index.ts +3 -1
  12. package/tsconfig.json +1 -1
  13. package/src/components/JobApplicationDialog.module.css +0 -440
  14. package/src/components/JobApplicationDialog.tsx +0 -620
  15. package/src/components/PickupLocationMap.tsx +0 -110
  16. package/src/components/accordion.css +0 -27
  17. package/src/components/accordion.tsx +0 -29
  18. package/src/components/analytics/AnalyticsConsentRestore.tsx +0 -19
  19. package/src/components/analytics/AnalyticsScripts.tsx +0 -106
  20. package/src/components/analytics/CookieConsentBanner.css +0 -86
  21. package/src/components/analytics/CookieConsentBanner.tsx +0 -102
  22. package/src/components/bottom-sheet.module.css +0 -78
  23. package/src/components/bottom-sheet.tsx +0 -60
  24. package/src/components/breadcrumb.module.css +0 -40
  25. package/src/components/breadcrumb.tsx +0 -36
  26. package/src/components/client-bottom-sheet.tsx +0 -14
  27. package/src/components/conditional-footer.tsx +0 -27
  28. package/src/components/contact-us.module.css +0 -147
  29. package/src/components/contact-us.tsx +0 -49
  30. package/src/components/email-signup.css +0 -151
  31. package/src/components/email-signup.tsx +0 -63
  32. package/src/components/faq-wrapper.module.css +0 -47
  33. package/src/components/faq-wrapper.tsx +0 -15
  34. package/src/components/footer.css +0 -187
  35. package/src/components/footer.tsx +0 -143
  36. package/src/components/global-simple-modal.tsx +0 -33
  37. package/src/components/google-review-summary.module.css +0 -77
  38. package/src/components/google-review-summary.tsx +0 -50
  39. package/src/components/hero-image.css +0 -13
  40. package/src/components/hero-image.tsx +0 -44
  41. package/src/components/language-aware-link.tsx +0 -72
  42. package/src/components/language-switcher.module.css +0 -124
  43. package/src/components/language-switcher.tsx +0 -75
  44. package/src/components/map-section.css +0 -59
  45. package/src/components/map-section.tsx +0 -63
  46. package/src/components/navbar.module.css +0 -152
  47. package/src/components/navbar.tsx +0 -125
  48. package/src/components/parallax-provider.tsx +0 -11
  49. package/src/components/product-theme-pages/best-option.module.css +0 -70
  50. package/src/components/product-theme-pages/best-option.tsx +0 -35
  51. package/src/components/product-theme-pages/extended-tour-options.module.css +0 -22
  52. package/src/components/product-theme-pages/extended-tour-options.tsx +0 -11
  53. package/src/components/product-theme-pages/photo-gallery.tsx +0 -90
  54. package/src/components/product-theme-pages/product-theme-page-layout.module.css +0 -13
  55. package/src/components/product-theme-pages/product-theme-page-layout.tsx +0 -67
  56. package/src/components/product-theme-pages/top-of-fold.module.css +0 -179
  57. package/src/components/product-theme-pages/top-of-fold.tsx +0 -80
  58. package/src/components/product-tile/image-only-product-tile-desktop.module.css +0 -106
  59. package/src/components/product-tile/image-only-product-tile-desktop.tsx +0 -56
  60. package/src/components/product-tile/image-only-product-tile-mobile.module.css +0 -122
  61. package/src/components/product-tile/image-only-product-tile-mobile.tsx +0 -89
  62. package/src/components/product-tile/image-only-product-tile.tsx +0 -44
  63. package/src/components/product-tile/product-tile-card.module.css +0 -84
  64. package/src/components/product-tile/product-tile-card.tsx +0 -61
  65. package/src/components/review-highlights-section.css +0 -85
  66. package/src/components/review-highlights-section.tsx +0 -127
  67. package/src/components/season-closure-overlay.module.css +0 -99
  68. package/src/components/season-closure-overlay.tsx +0 -98
  69. package/src/components/simple-modal.tsx +0 -69
  70. package/src/components/simple-top-of-fold.module.css +0 -76
  71. package/src/components/simple-top-of-fold.tsx +0 -34
  72. package/src/components/spacer.css +0 -41
  73. package/src/components/spacer.tsx +0 -23
  74. package/src/components/star-rating.module.css +0 -74
  75. package/src/components/star-rating.tsx +0 -48
  76. package/src/components/title-subtitle.module.css +0 -10
  77. package/src/components/title-subtitle.tsx +0 -30
  78. package/src/components/translatable-reviews.tsx +0 -75
  79. package/src/components/value-props.css +0 -185
  80. package/src/components/value-props.tsx +0 -88
  81. package/src/constants/booking-guide-quiz.ts +0 -64
  82. package/src/constants/contact-info.ts +0 -2
  83. package/src/constants/faq.ts +0 -44
  84. package/src/constants/json-ld/faq-json-ld.tsx +0 -170
  85. package/src/constants/json-ld/homepage-json-ld.tsx +0 -138
  86. package/src/constants/json-ld/job-posting-json-ld.tsx +0 -92
  87. package/src/constants/json-ld/organization-json-ld.tsx +0 -62
  88. package/src/constants/json-ld/page-json-ld.tsx +0 -6
  89. package/src/constants/json-ld/product-json-ld.tsx +0 -154
  90. package/src/constants/json-ld/review-json-ld.tsx +0 -377
  91. package/src/constants/navigation-links/footer-links.ts +0 -48
  92. package/src/constants/navigation-links/nav-bar-links.ts +0 -41
  93. package/src/constants/navigation-links/navigation-link.ts +0 -6
  94. package/src/constants/quiz-recommendations.ts +0 -506
  95. package/src/constants/reviews.ts +0 -75
  96. package/src/constants/staff.ts +0 -197
  97. package/src/constants/value-props.ts +0 -58
  98. package/src/hooks/use-bottom-sheet.tsx +0 -15
  99. package/src/hooks/use-simple-modal.tsx +0 -27
  100. package/src/hooks/useEmailSubscription.tsx +0 -103
  101. package/src/hooks/useEmbeddedInIframe.ts +0 -16
  102. package/src/hooks/useQuiz.tsx +0 -210
  103. package/src/providers/bottom-sheet-provider.tsx +0 -40
  104. package/src/types/fareharbor.d.ts +0 -12
  105. package/src/types/quiz.ts +0 -59
  106. /package/src/{app/photo-sessions → lib}/photo-packages.ts +0 -0
@@ -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 };