@infosoftas/s4-orders-js-sdk 0.1.19 → 0.1.21-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.
Files changed (62) hide show
  1. package/README.md +471 -426
  2. package/build/postcss-scope-tailwind.cjs +66 -0
  3. package/components.json +21 -0
  4. package/package.json +59 -43
  5. package/src/App.css +409 -0
  6. package/src/App.tsx +639 -569
  7. package/src/api/OrdersApi.ts +2 -2
  8. package/src/components/Alert/Alert.tsx +23 -22
  9. package/src/components/Button/Button.tsx +18 -22
  10. package/src/components/EHFForm/EHFForm.tsx +253 -235
  11. package/src/components/FormFields/AddressField.tsx +55 -62
  12. package/src/components/FormFields/EmailField.tsx +53 -58
  13. package/src/components/FormFields/InputField.tsx +52 -53
  14. package/src/components/FormFields/NameField.tsx +52 -53
  15. package/src/components/FormFields/PhoneField.tsx +53 -58
  16. package/src/components/FormFields/TermsCheckbox.tsx +70 -58
  17. package/src/components/FormFields/ToggleFiled.tsx +52 -63
  18. package/src/components/FormFields/fieldValidators.ts +71 -0
  19. package/src/components/OIOForm/OIOForm.tsx +132 -71
  20. package/src/components/OrderDenialModal/OrderDenialModal.tsx +102 -89
  21. package/src/components/OrderForm/ContactDetailsSection.tsx +71 -0
  22. package/src/components/OrderForm/OrderForm.tsx +177 -224
  23. package/src/components/OrderForm/PaymentMethodContent.tsx +62 -0
  24. package/src/components/OrderForm/PaymentMethodSection.tsx +79 -0
  25. package/src/components/OrderForm/SubmitConfirmationDialog.tsx +55 -0
  26. package/src/components/OrderForm/orderFormState.ts +171 -0
  27. package/src/components/OrderForm/useSlotElement.tsx +89 -0
  28. package/src/components/ui/alert-dialog.tsx +185 -0
  29. package/src/components/ui/alert.tsx +75 -0
  30. package/src/components/ui/button.tsx +55 -0
  31. package/src/components/ui/card.tsx +99 -0
  32. package/src/components/ui/checkbox.tsx +28 -0
  33. package/src/components/ui/field.tsx +238 -0
  34. package/src/components/ui/input.tsx +19 -0
  35. package/src/components/ui/label.tsx +19 -0
  36. package/src/components/ui/radio-group.tsx +37 -0
  37. package/src/components/ui/separator.tsx +24 -0
  38. package/src/components/ui/skeleton.tsx +13 -0
  39. package/src/components/ui/spinner.tsx +10 -0
  40. package/src/components/ui/switch.tsx +29 -0
  41. package/src/components/ui/textarea.tsx +17 -0
  42. package/src/constants/index.ts +0 -2
  43. package/src/custom.d.ts +4 -4
  44. package/src/enums/general.ts +1 -0
  45. package/src/hooks/useMessageEvent.tsx +54 -3
  46. package/src/hooks/useOrderForm.tsx +9 -2
  47. package/src/index.tsx +2 -0
  48. package/src/lib/utils.ts +1 -0
  49. package/src/types/general.ts +136 -111
  50. package/src/types/order.ts +8 -0
  51. package/src/utils/order.helper.ts +14 -3
  52. package/tsconfig.json +37 -38
  53. package/src/App.scss +0 -23
  54. package/src/assets/styles/forms.scss +0 -164
  55. package/src/assets/styles/general.scss +0 -46
  56. package/src/assets/styles/variables.scss +0 -29
  57. package/src/components/Alert/alert.scss +0 -21
  58. package/src/components/Button/button.scss +0 -48
  59. package/src/components/Loader/Loader.tsx +0 -22
  60. package/src/components/Loader/loader.scss +0 -37
  61. package/src/components/OrderDenialModal/orderDenialModal.scss +0 -42
  62. package/src/components/OrderForm/orderForm.scss +0 -10
@@ -1,37 +1,44 @@
1
1
  import {
2
2
  FC,
3
- ChangeEvent,
3
+ KeyboardEvent,
4
4
  ReactNode,
5
- Suspense,
6
- useState,
7
- useMemo,
8
5
  useEffect,
6
+ useRef,
7
+ useState,
9
8
  } from 'react';
10
- import { FormProvider, useForm, SubmitHandler } from 'react-hook-form';
9
+ import {
10
+ FormProvider,
11
+ useForm,
12
+ Resolver,
13
+ SubmitHandler,
14
+ } from 'react-hook-form';
15
+ import { zodResolver } from '@hookform/resolvers/zod';
11
16
 
12
17
  import { PaymentMethodEnum, UserActionEnum } from '../../enums/general';
13
- import {
14
- PAYMENT_METHOD_DEFAULT,
15
- INVOICE_ALLOWED_PAYMENT_METHODS,
16
- } from '../../constants/index';
18
+ import { PAYMENT_METHOD_DEFAULT } from '../../constants/index';
17
19
  import Alert from '../Alert/Alert';
18
20
  import Button from '../Button/Button';
19
21
  import OrderDenialModal from '../OrderDenialModal/OrderDenialModal';
20
- import ToggleField from '../FormFields/ToggleFiled';
21
- import formFieldsMapper from '../FormFields/FormFieldsMapper';
22
- import { DEFAULT_ORDER_FORM_FIELDS } from '../FormFields/FormFields.helper';
22
+ import TermsCheckbox from '../FormFields/TermsCheckbox';
23
23
  import { OrderFormInputsType, OrderInfoType } from '../../types/order';
24
24
  import {
25
- PaymentMethodOptionsType,
25
+ PaymentMethodSettingsType,
26
+ PaymentMethodOptionType,
26
27
  OrderFormFieldType,
27
28
  ContactRequestType,
28
29
  OrderDenialFallbackOfferType,
30
+ SubmitConfirmationType,
29
31
  } from '../../types/general';
30
- import { orderInvoiceContactFields } from '../../utils/order.helper';
31
32
  import useOrderForm from '../../hooks/useOrderForm';
32
-
33
- import './orderForm.scss';
34
- import TermsCheckbox from '../FormFields/TermsCheckbox';
33
+ import { useSlotElement, renderInSlot } from './useSlotElement';
34
+ import {
35
+ deriveOrderFormState,
36
+ buildOrderFormSchema,
37
+ OrderFormConfig,
38
+ } from './orderFormState';
39
+ import PaymentMethodSection from './PaymentMethodSection';
40
+ import ContactDetailsSection from './ContactDetailsSection';
41
+ import SubmitConfirmationDialog from './SubmitConfirmationDialog';
35
42
 
36
43
  type Props = {
37
44
  callback: (url: string | null, orderInfo?: OrderInfoType | null) => void;
@@ -51,18 +58,22 @@ type Props = {
51
58
  showIframe?: boolean;
52
59
  language: string;
53
60
  merchantAgreementUrl: string;
54
- paymentMethodsOptions?: PaymentMethodOptionsType;
61
+ paymentMethodsOptions?: PaymentMethodSettingsType;
55
62
  defaultValues?: OrderFormInputsType;
56
- paymentMethods?: { label: string; value: PaymentMethodEnum }[];
63
+ paymentMethods?: PaymentMethodOptionType[];
57
64
  allowedPaymentMethods?: PaymentMethodEnum[];
58
65
  submitButtonText?: string;
59
66
  orderDenialCloseButtonText?: string;
60
67
  orderDenialContinueButtonText?: string;
61
68
  paymentMethodLabel?: string;
69
+ contactDetailsLabel?: string;
70
+ paymentMethodElementId?: string;
71
+ contactDetailsElementId?: string;
62
72
  paymentMethodNotAllowedMsg?: string;
63
73
  errorReqMsg?: string;
64
74
  errorInvalidEmailMsg?: string;
65
75
  errorInvalidPhoneMsg?: string;
76
+ errorTermsMsg?: string;
66
77
  invoiceAddressSelection?: {
67
78
  enabled?: boolean;
68
79
  label?: string;
@@ -80,6 +91,7 @@ type Props = {
80
91
  organizationId: string
81
92
  ) => Promise<OrderDenialFallbackOfferType | undefined>;
82
93
  termsAndConditionsText?: string | ReactNode;
94
+ submitConfirmation?: SubmitConfirmationType;
83
95
  };
84
96
 
85
97
  const initialData = {
@@ -118,11 +130,15 @@ const OrderForm: FC<Props> = ({
118
130
  submitButtonText = 'Start',
119
131
  orderDenialCloseButtonText = 'Close',
120
132
  orderDenialContinueButtonText = 'Continue',
121
- paymentMethodLabel = 'Select Payment Method',
133
+ paymentMethodLabel = 'Select payment method',
134
+ contactDetailsLabel,
135
+ paymentMethodElementId,
136
+ contactDetailsElementId,
122
137
  paymentMethodNotAllowedMsg = 'This payment method not allowed!',
123
138
  errorReqMsg = '',
124
139
  errorInvalidEmailMsg = '',
125
140
  errorInvalidPhoneMsg = '',
141
+ errorTermsMsg = '',
126
142
  errorValidationTitleMsg,
127
143
  errorValidationDenialOrderBlockingMsg,
128
144
  errorValidationBlockingOffersMsg,
@@ -132,52 +148,68 @@ const OrderForm: FC<Props> = ({
132
148
  fetchDenialFallbackOffer,
133
149
  requireTermsAcceptance,
134
150
  termsAndConditionsText,
151
+ submitConfirmation,
135
152
  }) => {
136
- if (!templatePackageId) {
137
- console.error('"templatePackageId" should be set');
138
- }
153
+ const subscriberIdValue =
154
+ subscriberId || sessionStorage.getItem('subscriberId') || undefined;
139
155
 
140
- if (!organizationId) {
141
- console.error('"organizationId" should be set');
142
- }
156
+ useEffect(() => {
157
+ if (!templatePackageId)
158
+ console.error('"templatePackageId" should be set');
159
+ if (!organizationId) console.error('"organizationId" should be set');
160
+ if (!userId) console.error('"userId" should be set');
161
+ if (!identityProviderId)
162
+ console.error('"identityProviderId" should be set');
163
+ }, [templatePackageId, organizationId, userId, identityProviderId]);
143
164
 
144
- if (!subscriberId) {
145
- const sessionSubscriberId = sessionStorage.getItem('subscriberId');
146
- if (sessionSubscriberId) {
147
- subscriberId = sessionSubscriberId;
148
- }
149
- }
165
+ const config: OrderFormConfig = {
166
+ paymentMethods,
167
+ paymentMethodsOptions,
168
+ allowedPaymentMethods,
169
+ invoiceAddressSelection,
170
+ };
150
171
 
151
- if (!userId) {
152
- console.error('"userId" should be set');
153
- }
172
+ const messages = {
173
+ errorReqMsg,
174
+ errorInvalidEmailMsg,
175
+ errorInvalidPhoneMsg,
176
+ errorTermsMsg,
177
+ };
154
178
 
155
- if (!identityProviderId) {
156
- console.error('"identityProviderId" should be set');
157
- }
179
+ const configuredPaymentMethods = paymentMethods.map((m) => m.value);
180
+ const initialPaymentMethod = configuredPaymentMethods.length
181
+ ? ([defaultValues?.paymentMethod, PAYMENT_METHOD_DEFAULT].find(
182
+ (m) => m && configuredPaymentMethods.includes(m)
183
+ ) ?? configuredPaymentMethods[0])
184
+ : defaultValues?.paymentMethod || PAYMENT_METHOD_DEFAULT;
158
185
 
159
- const [orderFields, setOrderFields] = useState<OrderFormFieldType[]>(
160
- paymentMethodsOptions?.[
161
- defaultValues?.paymentMethod || PAYMENT_METHOD_DEFAULT
162
- ]?.orderFormFields ?? DEFAULT_ORDER_FORM_FIELDS
163
- );
186
+ const resolver: Resolver<OrderFormInputsType> = (values, ctx, options) => {
187
+ const schema = buildOrderFormSchema(
188
+ deriveOrderFormState(values, config),
189
+ requireTermsAcceptance,
190
+ messages
191
+ );
192
+ return zodResolver(schema)(values, ctx, options);
193
+ };
164
194
 
165
195
  const methods = useForm<OrderFormInputsType>({
196
+ resolver,
166
197
  defaultValues: {
167
198
  ...initialData,
168
199
  ...(defaultValues ?? {}),
169
- paymentMethod:
170
- defaultValues?.paymentMethod || PAYMENT_METHOD_DEFAULT,
200
+ paymentMethod: initialPaymentMethod,
171
201
  },
172
202
  });
173
203
 
174
- const {
175
- register,
176
- handleSubmit,
177
- setValue,
178
- watch,
179
- formState: { errors },
180
- } = methods;
204
+ const { control, handleSubmit, setValue, watch } = methods;
205
+
206
+ const derived = deriveOrderFormState(
207
+ {
208
+ paymentMethod: watch('paymentMethod'),
209
+ invoiceAddressSelection: watch('invoiceAddressSelection'),
210
+ },
211
+ config
212
+ );
181
213
 
182
214
  useEffect(() => {
183
215
  userActionCallback?.(UserActionEnum.SELECT_PAYMENT_METHOD, {
@@ -185,20 +217,6 @@ const OrderForm: FC<Props> = ({
185
217
  });
186
218
  }, []);
187
219
 
188
- const invoiceAddressToggle = watch('invoiceAddressSelection');
189
- const paymentMethodInput = watch('paymentMethod');
190
- const isTermsAccepted = watch('termsAccept', false);
191
-
192
- const invoiceOrderFields =
193
- paymentMethodsOptions?.[paymentMethodInput || PAYMENT_METHOD_DEFAULT]
194
- ?.paymentInvoiceFields ||
195
- invoiceAddressSelection?.fields ||
196
- orderInvoiceContactFields;
197
-
198
- const invoicePaymentMethods =
199
- invoiceAddressSelection?.paymentMethods ||
200
- INVOICE_ALLOWED_PAYMENT_METHODS;
201
-
202
220
  const {
203
221
  orderSubmit,
204
222
  continueWithFallbackOffer,
@@ -215,18 +233,18 @@ const OrderForm: FC<Props> = ({
215
233
  userActionCallback,
216
234
  setContactCallback,
217
235
  organizationId,
218
- subscriberId,
236
+ subscriberId: subscriberIdValue,
219
237
  userId,
220
238
  identityProviderId,
221
- orderFields,
239
+ orderFields: derived.orderFields,
222
240
  redirectUrl,
223
241
  showIframe,
224
242
  language,
225
243
  merchantAgreementUrl,
226
244
  paymentMethodsOptions,
227
245
  templatePackageId,
228
- invoiceAddressToggle,
229
- invoiceOrderFields,
246
+ invoiceAddressToggle: watch('invoiceAddressSelection'),
247
+ invoiceOrderFields: derived.invoiceOrderFields,
230
248
  errorValidationTitleMsg,
231
249
  errorValidationDenialOrderBlockingMsg,
232
250
  errorValidationBlockingOffersMsg,
@@ -236,210 +254,145 @@ const OrderForm: FC<Props> = ({
236
254
  fetchDenialFallbackOffer,
237
255
  });
238
256
 
239
- const onSubmit: SubmitHandler<OrderFormInputsType> = async (
240
- data
241
- ): Promise<void> => {
257
+ const [pendingSubmit, setPendingSubmit] =
258
+ useState<OrderFormInputsType | null>(null);
259
+
260
+ const submitOrder = async (data: OrderFormInputsType): Promise<void> => {
242
261
  if (
243
- paymentMethodInput === PaymentMethodEnum.EHF ||
244
- paymentMethodInput === PaymentMethodEnum.OIO
262
+ derived.paymentMethod === PaymentMethodEnum.EHF ||
263
+ derived.paymentMethod === PaymentMethodEnum.OIO
245
264
  ) {
246
- updateFormData(data);
265
+ updateFormData?.(data);
247
266
  return;
248
267
  }
249
268
  await orderSubmit(data);
250
269
  };
251
270
 
252
- const handlePaymentChange = (e: ChangeEvent<HTMLInputElement>) => {
253
- setValue('paymentMethod', e.target.value as PaymentMethodEnum);
254
- const orderFieldsLocal =
255
- paymentMethodsOptions?.[e.target.value as PaymentMethodEnum]
256
- ?.orderFormFields ?? DEFAULT_ORDER_FORM_FIELDS;
257
- setOrderFields(orderFieldsLocal);
258
- if (
259
- !invoicePaymentMethods.includes(e.target.value as PaymentMethodEnum)
260
- ) {
271
+ const onSubmit: SubmitHandler<OrderFormInputsType> = async (data) => {
272
+ if (submitConfirmation) {
273
+ setPendingSubmit(data);
274
+ return;
275
+ }
276
+ await submitOrder(data);
277
+ };
278
+
279
+ const confirmSubmit = () => {
280
+ const data = pendingSubmit;
281
+ setPendingSubmit(null);
282
+ if (data) submitOrder(data);
283
+ };
284
+
285
+ const handlePaymentChange = (paymentMethod: PaymentMethodEnum) => {
286
+ setValue('paymentMethod', paymentMethod, { shouldValidate: true });
287
+ if (!derived.invoicePaymentMethods.includes(paymentMethod)) {
261
288
  setValue('invoiceAddressSelection', false);
262
289
  }
263
290
 
264
291
  userActionCallback?.(UserActionEnum.SELECT_PAYMENT_METHOD, {
265
- paymentMethod: e.target.value,
292
+ paymentMethod,
266
293
  });
267
294
  };
268
295
 
269
- const allowPaymentMethod = useMemo(() => {
270
- if (!allowedPaymentMethods || allowedPaymentMethods?.length === 0) {
271
- return true;
272
- }
296
+ const paymentMethodSlot = useSlotElement(paymentMethodElementId);
297
+ const contactDetailsSlot = useSlotElement(contactDetailsElementId);
298
+ const formRef = useRef<HTMLFormElement>(null);
273
299
 
274
- if (
275
- allowedPaymentMethods.includes(
276
- paymentMethodInput as PaymentMethodEnum
277
- )
278
- ) {
279
- return true;
280
- }
300
+ const handleSlotKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
301
+ if (event.key !== 'Enter' || event.defaultPrevented) return;
302
+ if (!derived.allowPaymentMethod || loading) return;
281
303
 
282
- return false;
283
- }, [allowedPaymentMethods, paymentMethodInput]);
304
+ const target = event.target as HTMLElement;
305
+ if (target.tagName !== 'INPUT') return;
284
306
 
285
- const showInvoiceFields =
286
- invoiceAddressToggle &&
287
- invoiceOrderFields?.length > 0 &&
288
- allowPaymentMethod;
307
+ const { type } = target as HTMLInputElement;
308
+ if (type === 'button' || type === 'submit' || type === 'reset') return;
289
309
 
290
- const showInvoiceToggle =
291
- invoiceAddressSelection?.enabled &&
292
- allowPaymentMethod &&
293
- invoicePaymentMethods.includes(paymentMethodInput as PaymentMethodEnum);
310
+ event.preventDefault();
311
+ formRef.current?.requestSubmit();
312
+ };
313
+
314
+ const paymentMethodSection = derived.showPaymentMethodSelection ? (
315
+ <PaymentMethodSection
316
+ control={control}
317
+ paymentMethods={paymentMethods}
318
+ label={paymentMethodLabel}
319
+ onChange={handlePaymentChange}
320
+ />
321
+ ) : null;
294
322
 
295
- const isSubmitButtonDisabled = requireTermsAcceptance
296
- ? !isTermsAccepted
297
- : false;
323
+ const contactDetailsSection = derived.showContactDetails ? (
324
+ <ContactDetailsSection
325
+ label={contactDetailsLabel}
326
+ orderFields={derived.orderFields}
327
+ invoiceOrderFields={derived.invoiceOrderFields}
328
+ showOrderFields={derived.showOrderFields}
329
+ showInvoiceToggle={derived.showInvoiceToggle}
330
+ showInvoiceFields={derived.showInvoiceFields}
331
+ invoiceToggleLabel={invoiceAddressSelection?.label}
332
+ onInvoiceToggle={(value) =>
333
+ userActionCallback?.(UserActionEnum.TOGGLE_INVOICE_ADDRESS, {
334
+ value,
335
+ })
336
+ }
337
+ />
338
+ ) : null;
298
339
 
299
340
  return (
300
341
  <FormProvider {...methods}>
301
342
  <form
302
- className="sdk-order-form"
343
+ ref={formRef}
344
+ className="sdk-order-form flex w-full flex-col gap-6"
345
+ noValidate
303
346
  onSubmit={handleSubmit(onSubmit)}
304
347
  data-testid="sdk-order-form-id"
305
348
  >
306
- {paymentMethods?.length > 0 && (
307
- <div className="sdk-payment-method-section">
308
- <div className="field-wrapper radio-button-group">
309
- <div className="field-label">
310
- {paymentMethodLabel}
311
- </div>
312
- {paymentMethods.map((item) => (
313
- <div
314
- className="radio-button-control"
315
- key={item.value}
316
- >
317
- <label className="radio-badge">
318
- <input
319
- {...register('paymentMethod', {
320
- required: true,
321
- })}
322
- onChange={handlePaymentChange}
323
- type="radio"
324
- value={item.value}
325
- />
326
- <span className="value-block">
327
- <span className="prefix-icon"></span>
328
- <span className="text-block">
329
- {item.label}
330
- </span>
331
- </span>
332
- </label>
333
- </div>
334
- ))}
335
- {errors.paymentMethod && (
336
- <div className="text-error caption">
337
- {errors.paymentMethod.message}
338
- </div>
339
- )}
340
- </div>
341
- </div>
349
+ {renderInSlot(
350
+ paymentMethodSection,
351
+ paymentMethodSlot,
352
+ handleSlotKeyDown
342
353
  )}
343
- <div className="sdk-order-details-section">
344
- {orderFields?.length > 0 && allowPaymentMethod && (
345
- <Suspense fallback={null}>
346
- {orderFields.map((field) => {
347
- const Component = formFieldsMapper[field.name];
348
-
349
- return Component ? (
350
- <Component
351
- key={field.name}
352
- name={field.name}
353
- label={field.label}
354
- required={field.required || false}
355
- readOnly={field.readOnly || false}
356
- errors={errors}
357
- errorReqMsg={errorReqMsg}
358
- errorInvalidEmailMsg={
359
- errorInvalidEmailMsg
360
- }
361
- errorInvalidPhoneMsg={
362
- errorInvalidPhoneMsg
363
- }
364
- />
365
- ) : null;
366
- })}
367
- </Suspense>
368
- )}
369
- {showInvoiceToggle && (
370
- <ToggleField
371
- name="invoiceAddressSelection"
372
- label={
373
- invoiceAddressSelection?.label ??
374
- 'Invoice Address'
375
- }
376
- toggleCallback={(e) =>
377
- userActionCallback?.(
378
- UserActionEnum.TOGGLE_INVOICE_ADDRESS,
379
- { value: e }
380
- )
381
- }
382
- />
383
- )}
384
- {showInvoiceFields && (
385
- <Suspense fallback={null}>
386
- {invoiceOrderFields.map((field) => {
387
- const Component = formFieldsMapper[field.name];
388
-
389
- return Component ? (
390
- <Component
391
- key={field.name}
392
- name={field.name}
393
- label={field.label}
394
- required={field.required || false}
395
- readOnly={field.readOnly || false}
396
- errors={errors}
397
- errorReqMsg={errorReqMsg}
398
- errorInvalidEmailMsg={
399
- errorInvalidEmailMsg
400
- }
401
- errorInvalidPhoneMsg={
402
- errorInvalidPhoneMsg
403
- }
404
- />
405
- ) : null;
406
- })}
407
- </Suspense>
408
- )}
354
+ {renderInSlot(
355
+ contactDetailsSection,
356
+ contactDetailsSlot,
357
+ handleSlotKeyDown
358
+ )}
359
+ <div className="sdk-order-actions-section flex flex-col gap-4">
409
360
  {requireTermsAcceptance && (
410
361
  <TermsCheckbox
411
362
  name="termsAccept"
412
363
  label={termsAndConditionsText}
413
364
  required
414
- errors={errors}
415
365
  />
416
366
  )}
417
- {allowPaymentMethod && (
367
+ {derived.allowPaymentMethod && (
418
368
  <Button
419
369
  type="submit"
420
370
  loading={loading}
421
371
  buttonText={submitButtonText}
422
- disable={isSubmitButtonDisabled}
372
+ className="w-full"
423
373
  />
424
374
  )}
425
375
  <Alert
426
- className="mt-2"
427
376
  msg={
428
- allowPaymentMethod
377
+ derived.allowPaymentMethod
429
378
  ? apiErrorMsg
430
379
  : paymentMethodNotAllowedMsg
431
380
  }
432
381
  />
433
382
  {errorsMsg?.length > 0 &&
434
- allowPaymentMethod &&
383
+ derived.allowPaymentMethod &&
435
384
  errorsMsg.map((i, index) => (
436
- <Alert
437
- key={`${i}-${index}`}
438
- className="mt-2"
439
- msg={i}
440
- />
385
+ <Alert key={`${i}-${index}`} msg={i} />
441
386
  ))}
442
387
  </div>
388
+ {submitConfirmation && (
389
+ <SubmitConfirmationDialog
390
+ isOpen={!!pendingSubmit}
391
+ confirmation={submitConfirmation}
392
+ onConfirm={confirmSubmit}
393
+ onCancel={() => setPendingSubmit(null)}
394
+ />
395
+ )}
443
396
  <OrderDenialModal
444
397
  isOpen={!!orderDenialType}
445
398
  message={orderDenialMessage}
@@ -0,0 +1,62 @@
1
+ import { FC } from 'react';
2
+ import { cn } from 'cn';
3
+
4
+ import { PaymentMethodOptionType } from '../../types/general';
5
+ import { FieldDescription, FieldTitle } from '../ui/field';
6
+
7
+ type Props = {
8
+ method: PaymentMethodOptionType;
9
+ };
10
+
11
+ const PaymentMethodContent: FC<Props> = ({ method }) => {
12
+ const hasIcons = !!method.icons?.length;
13
+
14
+ if (!hasIcons) {
15
+ return (
16
+ <div className="flex flex-1 flex-col gap-0.5">
17
+ <FieldTitle className="sdk-payment-method-label">
18
+ {method.label}
19
+ </FieldTitle>
20
+ {method.description && (
21
+ <FieldDescription className="sdk-payment-method-description">
22
+ {method.description}
23
+ </FieldDescription>
24
+ )}
25
+ </div>
26
+ );
27
+ }
28
+
29
+ return (
30
+ <div className="flex flex-1 flex-col gap-0.5">
31
+ <FieldTitle className="sdk-payment-method-icons min-h-5 flex-wrap items-center gap-1.5">
32
+ {method.description && (
33
+ <span className="sdk-payment-method-label sr-only">
34
+ {method.label}
35
+ </span>
36
+ )}
37
+ {method.icons?.map((icon) => (
38
+ <img
39
+ key={icon.src}
40
+ src={icon.src}
41
+ alt={icon.alt ?? ''}
42
+ className={cn(
43
+ 'h-5 w-auto object-contain',
44
+ icon.className
45
+ )}
46
+ />
47
+ ))}
48
+ </FieldTitle>
49
+ {method.description ? (
50
+ <FieldDescription className="sdk-payment-method-description">
51
+ {method.description}
52
+ </FieldDescription>
53
+ ) : (
54
+ <FieldDescription className="sdk-payment-method-label">
55
+ {method.label}
56
+ </FieldDescription>
57
+ )}
58
+ </div>
59
+ );
60
+ };
61
+
62
+ export default PaymentMethodContent;