@bookinglab/booking-ui-react 1.3.0 → 1.5.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/README.md CHANGED
@@ -344,6 +344,143 @@ const fields = [
344
344
 
345
345
  ---
346
346
 
347
+ ## ContactDetailsForm
348
+
349
+ A form component for collecting contact details (first name, last name, email) alongside optional dynamic questions. On submit, emits values with `q` and `answers` fields matching the JRNI Update Client / Member API format.
350
+
351
+ ### Props
352
+
353
+ | Prop | Type | Default | Description |
354
+ |------|------|---------|-------------|
355
+ | `questions` | `Question[]` | `[]` | Additional questions to render after the contact fields |
356
+ | `initialValues` | `ContactDetailsInitialValues` | `undefined` | Initial values for pre-populating the form |
357
+ | `onSubmit` | `(values: ContactDetailsValues) => void` | required | Callback fired on valid submission |
358
+ | `onChange` | `(values: Partial<ContactDetailsValues>, isValid: boolean) => void` | `undefined` | Callback fired on every field change |
359
+ | `submitLabel` | `string` | `"Submit"` | Text for the submit button |
360
+ | `validateOnBlur` | `boolean` | `true` | Whether to validate fields on blur |
361
+ | `className` | `string` | `""` | Class name for the form element |
362
+ | `classNames` | `ContactDetailsFormClassNames` | `undefined` | Custom class names for styling |
363
+ | `fieldSettings` | `ContactDetailsFieldSettings` | `undefined` | Per-field required/disabled settings |
364
+
365
+ ### Default Fields
366
+
367
+ | Field | Label | Required | Validation |
368
+ |-------|-------|----------|------------|
369
+ | `firstName` | First name | Yes | required |
370
+ | `lastName` | Last name | Yes | required |
371
+ | `email` | Email | Yes | required, email format |
372
+
373
+ ### Field Settings
374
+
375
+ Control the `required` and `disabled` state of each default field:
376
+
377
+ ```tsx
378
+ import { ContactDetailsForm } from '@bookinglab/booking-ui-react';
379
+
380
+ <ContactDetailsForm
381
+ onSubmit={handleSubmit}
382
+ fieldSettings={{
383
+ firstName: { required: true, disabled: false },
384
+ lastName: { required: false }, // optional last name
385
+ email: { required: true, disabled: true }, // pre-filled, non-editable
386
+ }}
387
+ />
388
+ ```
389
+
390
+ ```typescript
391
+ interface ContactFieldSettings {
392
+ required?: boolean; // default: true
393
+ disabled?: boolean; // default: false
394
+ }
395
+
396
+ interface ContactDetailsFieldSettings {
397
+ firstName?: ContactFieldSettings;
398
+ lastName?: ContactFieldSettings;
399
+ email?: ContactFieldSettings;
400
+ }
401
+ ```
402
+
403
+ ### Initial Values
404
+
405
+ Pre-populate the form with existing data using `initialValues`:
406
+
407
+ ```tsx
408
+ <ContactDetailsForm
409
+ onSubmit={handleSubmit}
410
+ initialValues={{
411
+ firstName: 'Sarah',
412
+ lastName: 'Grey',
413
+ email: 'sarah@example.com',
414
+ questions: {
415
+ 2: '5', // select option ID as string
416
+ 7: 25, // number value
417
+ 10: 'Wheelchair access needed',
418
+ 12: true, // checkbox
419
+ },
420
+ }}
421
+ />
422
+ ```
423
+
424
+ ```typescript
425
+ interface ContactDetailsInitialValues {
426
+ firstName?: string;
427
+ lastName?: string;
428
+ email?: string;
429
+ questions?: Record<number, string | number | boolean>;
430
+ }
431
+ ```
432
+
433
+ When `reset()` is called via ref, the form restores to these initial values rather than empty fields.
434
+
435
+
436
+
437
+ Pass additional questions to render after the contact fields:
438
+
439
+ ```tsx
440
+ <ContactDetailsForm
441
+ questions={[
442
+ { id: 2, name: 'Purpose', detail_type: 'select', required: true,
443
+ options: [{ id: 5, name: 'School Trip' }, { id: 6, name: 'Corporate' }] },
444
+ { id: 7, name: 'Group Size', detail_type: 'number', required: true },
445
+ ]}
446
+ onSubmit={(values) => console.log(values)}
447
+ />
448
+ ```
449
+
450
+ ### Output Format
451
+
452
+ The `onSubmit` callback receives a `ContactDetailsValues` object:
453
+
454
+ ```typescript
455
+ interface ContactDetailsValues {
456
+ firstName: string;
457
+ lastName: string;
458
+ email: string;
459
+ q: Record<string, { answer: string; answer_id: number; name: string }>;
460
+ answers: { question_id: number; name: string; answer: string; answer_id: number }[];
461
+ }
462
+ ```
463
+
464
+ ### Ref Methods
465
+
466
+ ```tsx
467
+ import { useRef } from 'react';
468
+ import { ContactDetailsForm, ContactDetailsFormRef } from '@bookinglab/booking-ui-react';
469
+
470
+ const formRef = useRef<ContactDetailsFormRef>(null);
471
+
472
+ <ContactDetailsForm ref={formRef} onSubmit={handleSubmit} />
473
+ <button onClick={() => formRef.current?.reset()}>Reset</button>
474
+ <button onClick={() => formRef.current?.setValues({ email: 'test@example.com' })}>Pre-fill</button>
475
+ ```
476
+
477
+ | Method | Description |
478
+ |--------|-------------|
479
+ | `reset()` | Clear all values, errors, and touched state |
480
+ | `setValues(values)` | Programmatically set contact detail values |
481
+
482
+ ---
483
+
347
484
  ## Requirements
348
485
 
349
486
  - React 17.0.0 or higher
@@ -369,6 +506,15 @@ import type {
369
506
  RegistrationFormValues,
370
507
  RegistrationFormErrors,
371
508
  RegistrationFormClassNames,
509
+ ContactDetailsFormProps,
510
+ ContactDetailsFormRef,
511
+ ContactDetailsValues,
512
+ ContactDetailsFormClassNames,
513
+ ContactDetailsQuestionEntry,
514
+ ContactDetailsAnswerEntry,
515
+ ContactDetailsFieldSettings,
516
+ ContactDetailsInitialValues,
517
+ ContactFieldSettings,
372
518
  } from '@bookinglab/booking-ui-react';
373
519
  ```
374
520
 
package/dist/index.d.cts CHANGED
@@ -91,6 +91,133 @@ interface RegistrationFormRef {
91
91
  setValues: (values: Partial<RegistrationFormValues>) => void;
92
92
  }
93
93
 
94
+ /**
95
+ * Entry in the `q` record, keyed by question ID string
96
+ */
97
+ interface ContactDetailsQuestionEntry {
98
+ /** The answer value (option name for selects, raw value for others) */
99
+ answer: string;
100
+ /** The selected option ID for selects, or 0 for text/number/other fields */
101
+ answer_id: number;
102
+ /** The question's display name */
103
+ name: string;
104
+ }
105
+ /**
106
+ * Entry in the `answers` array
107
+ */
108
+ interface ContactDetailsAnswerEntry {
109
+ /** The question ID */
110
+ question_id: number;
111
+ /** The question's display name */
112
+ name: string;
113
+ /** The answer value */
114
+ answer: string;
115
+ /** The selected option ID for selects, or 0 for text/number/other fields */
116
+ answer_id: number;
117
+ }
118
+ /**
119
+ * Values emitted by ContactDetailsForm on submit.
120
+ * The `q` and `answers` fields match the JRNI Update Client/Member API format.
121
+ */
122
+ interface ContactDetailsValues {
123
+ firstName: string;
124
+ lastName: string;
125
+ email: string;
126
+ /** Questions keyed by question ID string */
127
+ q: Record<string, ContactDetailsQuestionEntry>;
128
+ /** Questions as an array of answer entries */
129
+ answers: ContactDetailsAnswerEntry[];
130
+ }
131
+ /**
132
+ * Custom class names for styling ContactDetailsForm elements
133
+ */
134
+ interface ContactDetailsFormClassNames {
135
+ /** Wrapper for each form field */
136
+ fieldWrapper?: string;
137
+ /** All labels */
138
+ label?: string;
139
+ /** Heading elements */
140
+ heading?: string;
141
+ /** All input elements (text, textarea, select, date, number) */
142
+ input?: string;
143
+ /** Input elements in error state */
144
+ inputError?: string;
145
+ /** Checkbox input specifically */
146
+ checkbox?: string;
147
+ /** Help text below fields */
148
+ helpText?: string;
149
+ /** Error message text */
150
+ errorText?: string;
151
+ /** Submit button */
152
+ button?: string;
153
+ }
154
+ /**
155
+ * Per-field settings for a default contact field (firstName, lastName, email)
156
+ */
157
+ interface ContactFieldSettings {
158
+ /** Whether the field is required (default: true for all) */
159
+ required?: boolean;
160
+ /** Whether the field is disabled */
161
+ disabled?: boolean;
162
+ }
163
+ /**
164
+ * Settings for the default contact detail fields
165
+ */
166
+ interface ContactDetailsFieldSettings {
167
+ firstName?: ContactFieldSettings;
168
+ lastName?: ContactFieldSettings;
169
+ email?: ContactFieldSettings;
170
+ }
171
+ /**
172
+ * Initial values for pre-populating the form
173
+ */
174
+ interface ContactDetailsInitialValues {
175
+ /** Initial first name value */
176
+ firstName?: string;
177
+ /** Initial last name value */
178
+ lastName?: string;
179
+ /** Initial email value */
180
+ email?: string;
181
+ /** Initial question values keyed by question ID */
182
+ questions?: Record<number, string | number | boolean>;
183
+ }
184
+ /**
185
+ * Props for the ContactDetailsForm component
186
+ */
187
+ interface ContactDetailsFormProps {
188
+ /** Additional questions to render after the contact fields */
189
+ questions?: Question[];
190
+ /** Initial values for pre-populating the form fields */
191
+ initialValues?: ContactDetailsInitialValues;
192
+ /** Callback fired when form is submitted with valid values */
193
+ onSubmit: (values: ContactDetailsValues) => void;
194
+ /** Callback fired on every field change */
195
+ onChange?: (values: Partial<ContactDetailsValues>, isValid: boolean) => void;
196
+ /** Text for the submit button (default: "Submit") */
197
+ submitLabel?: string;
198
+ /** Whether to validate on blur (default: true) */
199
+ validateOnBlur?: boolean;
200
+ /** Class name for the form element */
201
+ className?: string;
202
+ /** Custom class names for styling individual elements */
203
+ classNames?: ContactDetailsFormClassNames;
204
+ /** Per-field settings for the default contact fields (required, disabled) */
205
+ fieldSettings?: ContactDetailsFieldSettings;
206
+ }
207
+ /**
208
+ * Imperative handle methods exposed via ref
209
+ */
210
+ interface ContactDetailsFormRef {
211
+ /** Reset all form values and errors */
212
+ reset: () => void;
213
+ /** Programmatically set contact detail values */
214
+ setValues: (values: Partial<{
215
+ firstName: string;
216
+ lastName: string;
217
+ email: string;
218
+ }>) => void;
219
+ }
220
+
94
221
  interface QuestionOption {
95
222
  id: number;
96
223
  name: string;
@@ -209,6 +336,25 @@ declare function BookingForm({ questions, onSubmit, submitLabel, className, labe
209
336
  */
210
337
  declare const RegistrationForm: React.ForwardRefExoticComponent<RegistrationFormProps & React.RefAttributes<RegistrationFormRef>>;
211
338
 
339
+ /**
340
+ * ContactDetailsForm — collects first name, last name, email and optional
341
+ * dynamic questions. On submit it emits values with `q` and `answers` fields
342
+ * matching the JRNI Update Client / Member API format.
343
+ *
344
+ * @example
345
+ * ```tsx
346
+ * <ContactDetailsForm
347
+ * questions={[
348
+ * { id: 2, name: 'Purpose', detail_type: 'select', required: true,
349
+ * options: [{ id: 5, name: 'School Trip' }, { id: 6, name: 'Corporate' }] },
350
+ * { id: 7, name: 'Group Size', detail_type: 'number', required: true },
351
+ * ]}
352
+ * onSubmit={(values) => console.log(values)}
353
+ * />
354
+ * ```
355
+ */
356
+ declare const ContactDetailsForm: React.ForwardRefExoticComponent<ContactDetailsFormProps & React.RefAttributes<ContactDetailsFormRef>>;
357
+
212
358
  /**
213
359
  * Validation helper functions for form fields
214
360
  * Each validator returns an error message string if invalid, or null if valid
@@ -242,4 +388,4 @@ declare function minLen(min: number): (value: string) => string | null;
242
388
  */
243
389
  declare function compose(...validators: Array<(value: string) => string | null>): (value: string) => string | null;
244
390
 
245
- export { BookingForm, type BookingFormClassNames, type BookingFormProps, type BookingUIConfig, type FieldConfig, type FieldOption, type FormErrors, type FormValues, type Question, type QuestionOption, type QuestionSettings, RegistrationForm, type RegistrationFormClassNames, type RegistrationFormErrors, type RegistrationFormProps, type RegistrationFormRef, type RegistrationFormValues, compose, email, minLen, phone, required, ukPostcode };
391
+ export { BookingForm, type BookingFormClassNames, type BookingFormProps, type BookingUIConfig, type ContactDetailsAnswerEntry, type ContactDetailsFieldSettings, ContactDetailsForm, type ContactDetailsFormClassNames, type ContactDetailsFormProps, type ContactDetailsFormRef, type ContactDetailsInitialValues, type ContactDetailsQuestionEntry, type ContactDetailsValues, type ContactFieldSettings, type FieldConfig, type FieldOption, type FormErrors, type FormValues, type Question, type QuestionOption, type QuestionSettings, RegistrationForm, type RegistrationFormClassNames, type RegistrationFormErrors, type RegistrationFormProps, type RegistrationFormRef, type RegistrationFormValues, compose, email, minLen, phone, required, ukPostcode };
package/dist/index.d.ts CHANGED
@@ -91,6 +91,133 @@ interface RegistrationFormRef {
91
91
  setValues: (values: Partial<RegistrationFormValues>) => void;
92
92
  }
93
93
 
94
+ /**
95
+ * Entry in the `q` record, keyed by question ID string
96
+ */
97
+ interface ContactDetailsQuestionEntry {
98
+ /** The answer value (option name for selects, raw value for others) */
99
+ answer: string;
100
+ /** The selected option ID for selects, or 0 for text/number/other fields */
101
+ answer_id: number;
102
+ /** The question's display name */
103
+ name: string;
104
+ }
105
+ /**
106
+ * Entry in the `answers` array
107
+ */
108
+ interface ContactDetailsAnswerEntry {
109
+ /** The question ID */
110
+ question_id: number;
111
+ /** The question's display name */
112
+ name: string;
113
+ /** The answer value */
114
+ answer: string;
115
+ /** The selected option ID for selects, or 0 for text/number/other fields */
116
+ answer_id: number;
117
+ }
118
+ /**
119
+ * Values emitted by ContactDetailsForm on submit.
120
+ * The `q` and `answers` fields match the JRNI Update Client/Member API format.
121
+ */
122
+ interface ContactDetailsValues {
123
+ firstName: string;
124
+ lastName: string;
125
+ email: string;
126
+ /** Questions keyed by question ID string */
127
+ q: Record<string, ContactDetailsQuestionEntry>;
128
+ /** Questions as an array of answer entries */
129
+ answers: ContactDetailsAnswerEntry[];
130
+ }
131
+ /**
132
+ * Custom class names for styling ContactDetailsForm elements
133
+ */
134
+ interface ContactDetailsFormClassNames {
135
+ /** Wrapper for each form field */
136
+ fieldWrapper?: string;
137
+ /** All labels */
138
+ label?: string;
139
+ /** Heading elements */
140
+ heading?: string;
141
+ /** All input elements (text, textarea, select, date, number) */
142
+ input?: string;
143
+ /** Input elements in error state */
144
+ inputError?: string;
145
+ /** Checkbox input specifically */
146
+ checkbox?: string;
147
+ /** Help text below fields */
148
+ helpText?: string;
149
+ /** Error message text */
150
+ errorText?: string;
151
+ /** Submit button */
152
+ button?: string;
153
+ }
154
+ /**
155
+ * Per-field settings for a default contact field (firstName, lastName, email)
156
+ */
157
+ interface ContactFieldSettings {
158
+ /** Whether the field is required (default: true for all) */
159
+ required?: boolean;
160
+ /** Whether the field is disabled */
161
+ disabled?: boolean;
162
+ }
163
+ /**
164
+ * Settings for the default contact detail fields
165
+ */
166
+ interface ContactDetailsFieldSettings {
167
+ firstName?: ContactFieldSettings;
168
+ lastName?: ContactFieldSettings;
169
+ email?: ContactFieldSettings;
170
+ }
171
+ /**
172
+ * Initial values for pre-populating the form
173
+ */
174
+ interface ContactDetailsInitialValues {
175
+ /** Initial first name value */
176
+ firstName?: string;
177
+ /** Initial last name value */
178
+ lastName?: string;
179
+ /** Initial email value */
180
+ email?: string;
181
+ /** Initial question values keyed by question ID */
182
+ questions?: Record<number, string | number | boolean>;
183
+ }
184
+ /**
185
+ * Props for the ContactDetailsForm component
186
+ */
187
+ interface ContactDetailsFormProps {
188
+ /** Additional questions to render after the contact fields */
189
+ questions?: Question[];
190
+ /** Initial values for pre-populating the form fields */
191
+ initialValues?: ContactDetailsInitialValues;
192
+ /** Callback fired when form is submitted with valid values */
193
+ onSubmit: (values: ContactDetailsValues) => void;
194
+ /** Callback fired on every field change */
195
+ onChange?: (values: Partial<ContactDetailsValues>, isValid: boolean) => void;
196
+ /** Text for the submit button (default: "Submit") */
197
+ submitLabel?: string;
198
+ /** Whether to validate on blur (default: true) */
199
+ validateOnBlur?: boolean;
200
+ /** Class name for the form element */
201
+ className?: string;
202
+ /** Custom class names for styling individual elements */
203
+ classNames?: ContactDetailsFormClassNames;
204
+ /** Per-field settings for the default contact fields (required, disabled) */
205
+ fieldSettings?: ContactDetailsFieldSettings;
206
+ }
207
+ /**
208
+ * Imperative handle methods exposed via ref
209
+ */
210
+ interface ContactDetailsFormRef {
211
+ /** Reset all form values and errors */
212
+ reset: () => void;
213
+ /** Programmatically set contact detail values */
214
+ setValues: (values: Partial<{
215
+ firstName: string;
216
+ lastName: string;
217
+ email: string;
218
+ }>) => void;
219
+ }
220
+
94
221
  interface QuestionOption {
95
222
  id: number;
96
223
  name: string;
@@ -209,6 +336,25 @@ declare function BookingForm({ questions, onSubmit, submitLabel, className, labe
209
336
  */
210
337
  declare const RegistrationForm: React.ForwardRefExoticComponent<RegistrationFormProps & React.RefAttributes<RegistrationFormRef>>;
211
338
 
339
+ /**
340
+ * ContactDetailsForm — collects first name, last name, email and optional
341
+ * dynamic questions. On submit it emits values with `q` and `answers` fields
342
+ * matching the JRNI Update Client / Member API format.
343
+ *
344
+ * @example
345
+ * ```tsx
346
+ * <ContactDetailsForm
347
+ * questions={[
348
+ * { id: 2, name: 'Purpose', detail_type: 'select', required: true,
349
+ * options: [{ id: 5, name: 'School Trip' }, { id: 6, name: 'Corporate' }] },
350
+ * { id: 7, name: 'Group Size', detail_type: 'number', required: true },
351
+ * ]}
352
+ * onSubmit={(values) => console.log(values)}
353
+ * />
354
+ * ```
355
+ */
356
+ declare const ContactDetailsForm: React.ForwardRefExoticComponent<ContactDetailsFormProps & React.RefAttributes<ContactDetailsFormRef>>;
357
+
212
358
  /**
213
359
  * Validation helper functions for form fields
214
360
  * Each validator returns an error message string if invalid, or null if valid
@@ -242,4 +388,4 @@ declare function minLen(min: number): (value: string) => string | null;
242
388
  */
243
389
  declare function compose(...validators: Array<(value: string) => string | null>): (value: string) => string | null;
244
390
 
245
- export { BookingForm, type BookingFormClassNames, type BookingFormProps, type BookingUIConfig, type FieldConfig, type FieldOption, type FormErrors, type FormValues, type Question, type QuestionOption, type QuestionSettings, RegistrationForm, type RegistrationFormClassNames, type RegistrationFormErrors, type RegistrationFormProps, type RegistrationFormRef, type RegistrationFormValues, compose, email, minLen, phone, required, ukPostcode };
391
+ export { BookingForm, type BookingFormClassNames, type BookingFormProps, type BookingUIConfig, type ContactDetailsAnswerEntry, type ContactDetailsFieldSettings, ContactDetailsForm, type ContactDetailsFormClassNames, type ContactDetailsFormProps, type ContactDetailsFormRef, type ContactDetailsInitialValues, type ContactDetailsQuestionEntry, type ContactDetailsValues, type ContactFieldSettings, type FieldConfig, type FieldOption, type FormErrors, type FormValues, type Question, type QuestionOption, type QuestionSettings, RegistrationForm, type RegistrationFormClassNames, type RegistrationFormErrors, type RegistrationFormProps, type RegistrationFormRef, type RegistrationFormValues, compose, email, minLen, phone, required, ukPostcode };