@reblu/site-blocks 0.24.0 → 0.26.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/CHANGELOG.md +32 -0
- package/README.md +127 -0
- package/dist/client.cjs +277 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +86 -2
- package/dist/client.d.ts +86 -2
- package/dist/client.js +274 -0
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +434 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +431 -332
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/client.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { ShopProductListItem, AcademyModulePublic, AcademyLessonPublic, DataRequestType, DataRequestInput, DataRequestSubmission } from '@reblu/site-contracts';
|
|
4
|
+
import { ShopProductListItem, AcademyModulePublic, AcademyLessonPublic, DataRequestType, DataRequestInput, DataRequestSubmission, WithdrawalRequestInput, WithdrawalRequestSubmission } from '@reblu/site-contracts';
|
|
5
5
|
export { DATA_REQUEST_TYPES } from '@reblu/site-contracts';
|
|
6
6
|
import { H as HeadingLevel } from './_heading-BsNbc1wc.cjs';
|
|
7
7
|
|
|
@@ -460,4 +460,88 @@ interface DataRequestFormProps {
|
|
|
460
460
|
}
|
|
461
461
|
declare function DataRequestForm({ getCaptchaToken, submit, endpoint, onInteract, onSubmitted, requestTypes, labels, locale, className, }: DataRequestFormProps): react_jsx_runtime.JSX.Element;
|
|
462
462
|
|
|
463
|
-
|
|
463
|
+
/**
|
|
464
|
+
* `WithdrawalRequestForm` — drop-in withdrawal form (REB-1201) over
|
|
465
|
+
* `POST /api/site/withdrawal-requests`. The buyer gives the order reference,
|
|
466
|
+
* their email, what they withdraw from and, optionally, a reason; base-api
|
|
467
|
+
* stores it, emails the tenant team and sends the buyer an acknowledgement.
|
|
468
|
+
*
|
|
469
|
+
* It decides nothing: it does not check the order, nor exclude any product
|
|
470
|
+
* or service. Whether the right applies is the tenant's decision, explained on
|
|
471
|
+
* its own withdrawal page (`LegalDocument` over `legal.withdrawalPolicy`).
|
|
472
|
+
*
|
|
473
|
+
* Stateful (client-only — ships from `./client`) but transport-agnostic, like
|
|
474
|
+
* `DataRequestForm`:
|
|
475
|
+
* - `getCaptchaToken` mints the reCAPTCHA v3 token; `undefined` blocks the
|
|
476
|
+
* submission (base-api rejects a request without one).
|
|
477
|
+
* - `submit` sends the body. By default it POSTs JSON to the SITE's own Route
|
|
478
|
+
* Handler (`endpoint`, default `/api/withdrawal-requests`) and unwraps the
|
|
479
|
+
* `{ data }` envelope — the API key never reaches the browser.
|
|
480
|
+
*
|
|
481
|
+
* On success the form is replaced by a receipt. When the acknowledgement email
|
|
482
|
+
* could not be sent, the receipt repeats what was communicated so the buyer
|
|
483
|
+
* can keep it. Errors never show server text: they map by HTTP status to the
|
|
484
|
+
* fixed (overridable) copy in `labels`.
|
|
485
|
+
*/
|
|
486
|
+
type WithdrawalRequestSubmitResult = {
|
|
487
|
+
ok: true;
|
|
488
|
+
data: WithdrawalRequestSubmission;
|
|
489
|
+
}
|
|
490
|
+
/** `status` is the HTTP status, or `0` when the request never got an answer. */
|
|
491
|
+
| {
|
|
492
|
+
ok: false;
|
|
493
|
+
status: number;
|
|
494
|
+
};
|
|
495
|
+
/** The server's field limits (base-api `POST /api/site/withdrawal-requests`). */
|
|
496
|
+
declare const WITHDRAWAL_REQUEST_FIELD_LIMITS: Readonly<{
|
|
497
|
+
orderReference: 100;
|
|
498
|
+
email: 320;
|
|
499
|
+
name: 200;
|
|
500
|
+
items: 2000;
|
|
501
|
+
reason: 2000;
|
|
502
|
+
}>;
|
|
503
|
+
interface WithdrawalRequestFormLabels {
|
|
504
|
+
orderReferenceLabel: string;
|
|
505
|
+
nameLabel: string;
|
|
506
|
+
emailLabel: string;
|
|
507
|
+
itemsLabel: string;
|
|
508
|
+
itemsPlaceholder: string;
|
|
509
|
+
reasonLabel: string;
|
|
510
|
+
/** Small print under the fields, e.g. how the data is used. `null` hides it. */
|
|
511
|
+
notice: ReactNode;
|
|
512
|
+
submit: string;
|
|
513
|
+
submitting: string;
|
|
514
|
+
orderReferenceRequired: string;
|
|
515
|
+
emailInvalid: string;
|
|
516
|
+
itemsRequired: string;
|
|
517
|
+
/** Receipt when the buyer was emailed a copy; receives the formatted received-at date. */
|
|
518
|
+
success: (receivedAt: string) => ReactNode;
|
|
519
|
+
/** Receipt when no copy could be emailed; the communication is listed below it. */
|
|
520
|
+
successWithoutEmail: (receivedAt: string) => ReactNode;
|
|
521
|
+
receiptOrderReference: string;
|
|
522
|
+
receiptItems: string;
|
|
523
|
+
captchaError: string;
|
|
524
|
+
validationError: string;
|
|
525
|
+
rateLimitError: string;
|
|
526
|
+
genericError: string;
|
|
527
|
+
}
|
|
528
|
+
declare const DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS: WithdrawalRequestFormLabels;
|
|
529
|
+
interface WithdrawalRequestFormProps {
|
|
530
|
+
/** Mints the reCAPTCHA v3 token; `undefined` blocks the submission. */
|
|
531
|
+
getCaptchaToken: () => Promise<string | undefined>;
|
|
532
|
+
/** Sends the body. Defaults to a JSON POST to `endpoint`. */
|
|
533
|
+
submit?: (input: WithdrawalRequestInput) => Promise<WithdrawalRequestSubmitResult>;
|
|
534
|
+
/** The SITE's Route Handler used by the default `submit`. @default '/api/withdrawal-requests' */
|
|
535
|
+
endpoint?: string;
|
|
536
|
+
/** First interaction with the form — wire it to the deferred reCAPTCHA `arm`. */
|
|
537
|
+
onInteract?: () => void;
|
|
538
|
+
/** Called with the acknowledgement after a successful submission. */
|
|
539
|
+
onSubmitted?: (ack: WithdrawalRequestSubmission) => void;
|
|
540
|
+
labels?: Partial<WithdrawalRequestFormLabels>;
|
|
541
|
+
/** Locale for the received-at date. @default 'es-ES' */
|
|
542
|
+
locale?: string;
|
|
543
|
+
className?: string;
|
|
544
|
+
}
|
|
545
|
+
declare function WithdrawalRequestForm({ getCaptchaToken, submit, endpoint, onInteract, onSubmitted, labels, locale, className, }: WithdrawalRequestFormProps): react_jsx_runtime.JSX.Element;
|
|
546
|
+
|
|
547
|
+
export { AddToCartButton, type AddToCartButtonLabels, type AddToCartButtonProps, type AddToCartButtonStatus, CartIcon, type CartIconLabels, type CartIconProps, Collection, type CollectionFacet, type CollectionFacetOption, type CollectionFilter, type CollectionLabels, type CollectionProps, CourseSyllabus, type CourseSyllabusLabels, type CourseSyllabusModule, type CourseSyllabusProps, DATA_REQUEST_FIELD_LIMITS, DEFAULT_COURSE_SYLLABUS_LABELS, DEFAULT_DATA_REQUEST_FORM_LABELS, DEFAULT_PRODUCT_CAROUSEL_LABELS, DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS, DataRequestForm, type DataRequestFormLabels, type DataRequestFormProps, type DataRequestSubmitResult, ProductCarousel, type ProductCarouselCustomCardProps, type ProductCarouselDefaultCardProps, type ProductCarouselLabels, type ProductCarouselProps, ShareButtons, type ShareButtonsLabels, type ShareButtonsProps, type UseCollectionOptions, type UseCollectionResult, WITHDRAWAL_REQUEST_FIELD_LIMITS, WithdrawalRequestForm, type WithdrawalRequestFormLabels, type WithdrawalRequestFormProps, type WithdrawalRequestSubmitResult, useCollection };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { ShopProductListItem, AcademyModulePublic, AcademyLessonPublic, DataRequestType, DataRequestInput, DataRequestSubmission } from '@reblu/site-contracts';
|
|
4
|
+
import { ShopProductListItem, AcademyModulePublic, AcademyLessonPublic, DataRequestType, DataRequestInput, DataRequestSubmission, WithdrawalRequestInput, WithdrawalRequestSubmission } from '@reblu/site-contracts';
|
|
5
5
|
export { DATA_REQUEST_TYPES } from '@reblu/site-contracts';
|
|
6
6
|
import { H as HeadingLevel } from './_heading-BsNbc1wc.js';
|
|
7
7
|
|
|
@@ -460,4 +460,88 @@ interface DataRequestFormProps {
|
|
|
460
460
|
}
|
|
461
461
|
declare function DataRequestForm({ getCaptchaToken, submit, endpoint, onInteract, onSubmitted, requestTypes, labels, locale, className, }: DataRequestFormProps): react_jsx_runtime.JSX.Element;
|
|
462
462
|
|
|
463
|
-
|
|
463
|
+
/**
|
|
464
|
+
* `WithdrawalRequestForm` — drop-in withdrawal form (REB-1201) over
|
|
465
|
+
* `POST /api/site/withdrawal-requests`. The buyer gives the order reference,
|
|
466
|
+
* their email, what they withdraw from and, optionally, a reason; base-api
|
|
467
|
+
* stores it, emails the tenant team and sends the buyer an acknowledgement.
|
|
468
|
+
*
|
|
469
|
+
* It decides nothing: it does not check the order, nor exclude any product
|
|
470
|
+
* or service. Whether the right applies is the tenant's decision, explained on
|
|
471
|
+
* its own withdrawal page (`LegalDocument` over `legal.withdrawalPolicy`).
|
|
472
|
+
*
|
|
473
|
+
* Stateful (client-only — ships from `./client`) but transport-agnostic, like
|
|
474
|
+
* `DataRequestForm`:
|
|
475
|
+
* - `getCaptchaToken` mints the reCAPTCHA v3 token; `undefined` blocks the
|
|
476
|
+
* submission (base-api rejects a request without one).
|
|
477
|
+
* - `submit` sends the body. By default it POSTs JSON to the SITE's own Route
|
|
478
|
+
* Handler (`endpoint`, default `/api/withdrawal-requests`) and unwraps the
|
|
479
|
+
* `{ data }` envelope — the API key never reaches the browser.
|
|
480
|
+
*
|
|
481
|
+
* On success the form is replaced by a receipt. When the acknowledgement email
|
|
482
|
+
* could not be sent, the receipt repeats what was communicated so the buyer
|
|
483
|
+
* can keep it. Errors never show server text: they map by HTTP status to the
|
|
484
|
+
* fixed (overridable) copy in `labels`.
|
|
485
|
+
*/
|
|
486
|
+
type WithdrawalRequestSubmitResult = {
|
|
487
|
+
ok: true;
|
|
488
|
+
data: WithdrawalRequestSubmission;
|
|
489
|
+
}
|
|
490
|
+
/** `status` is the HTTP status, or `0` when the request never got an answer. */
|
|
491
|
+
| {
|
|
492
|
+
ok: false;
|
|
493
|
+
status: number;
|
|
494
|
+
};
|
|
495
|
+
/** The server's field limits (base-api `POST /api/site/withdrawal-requests`). */
|
|
496
|
+
declare const WITHDRAWAL_REQUEST_FIELD_LIMITS: Readonly<{
|
|
497
|
+
orderReference: 100;
|
|
498
|
+
email: 320;
|
|
499
|
+
name: 200;
|
|
500
|
+
items: 2000;
|
|
501
|
+
reason: 2000;
|
|
502
|
+
}>;
|
|
503
|
+
interface WithdrawalRequestFormLabels {
|
|
504
|
+
orderReferenceLabel: string;
|
|
505
|
+
nameLabel: string;
|
|
506
|
+
emailLabel: string;
|
|
507
|
+
itemsLabel: string;
|
|
508
|
+
itemsPlaceholder: string;
|
|
509
|
+
reasonLabel: string;
|
|
510
|
+
/** Small print under the fields, e.g. how the data is used. `null` hides it. */
|
|
511
|
+
notice: ReactNode;
|
|
512
|
+
submit: string;
|
|
513
|
+
submitting: string;
|
|
514
|
+
orderReferenceRequired: string;
|
|
515
|
+
emailInvalid: string;
|
|
516
|
+
itemsRequired: string;
|
|
517
|
+
/** Receipt when the buyer was emailed a copy; receives the formatted received-at date. */
|
|
518
|
+
success: (receivedAt: string) => ReactNode;
|
|
519
|
+
/** Receipt when no copy could be emailed; the communication is listed below it. */
|
|
520
|
+
successWithoutEmail: (receivedAt: string) => ReactNode;
|
|
521
|
+
receiptOrderReference: string;
|
|
522
|
+
receiptItems: string;
|
|
523
|
+
captchaError: string;
|
|
524
|
+
validationError: string;
|
|
525
|
+
rateLimitError: string;
|
|
526
|
+
genericError: string;
|
|
527
|
+
}
|
|
528
|
+
declare const DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS: WithdrawalRequestFormLabels;
|
|
529
|
+
interface WithdrawalRequestFormProps {
|
|
530
|
+
/** Mints the reCAPTCHA v3 token; `undefined` blocks the submission. */
|
|
531
|
+
getCaptchaToken: () => Promise<string | undefined>;
|
|
532
|
+
/** Sends the body. Defaults to a JSON POST to `endpoint`. */
|
|
533
|
+
submit?: (input: WithdrawalRequestInput) => Promise<WithdrawalRequestSubmitResult>;
|
|
534
|
+
/** The SITE's Route Handler used by the default `submit`. @default '/api/withdrawal-requests' */
|
|
535
|
+
endpoint?: string;
|
|
536
|
+
/** First interaction with the form — wire it to the deferred reCAPTCHA `arm`. */
|
|
537
|
+
onInteract?: () => void;
|
|
538
|
+
/** Called with the acknowledgement after a successful submission. */
|
|
539
|
+
onSubmitted?: (ack: WithdrawalRequestSubmission) => void;
|
|
540
|
+
labels?: Partial<WithdrawalRequestFormLabels>;
|
|
541
|
+
/** Locale for the received-at date. @default 'es-ES' */
|
|
542
|
+
locale?: string;
|
|
543
|
+
className?: string;
|
|
544
|
+
}
|
|
545
|
+
declare function WithdrawalRequestForm({ getCaptchaToken, submit, endpoint, onInteract, onSubmitted, labels, locale, className, }: WithdrawalRequestFormProps): react_jsx_runtime.JSX.Element;
|
|
546
|
+
|
|
547
|
+
export { AddToCartButton, type AddToCartButtonLabels, type AddToCartButtonProps, type AddToCartButtonStatus, CartIcon, type CartIconLabels, type CartIconProps, Collection, type CollectionFacet, type CollectionFacetOption, type CollectionFilter, type CollectionLabels, type CollectionProps, CourseSyllabus, type CourseSyllabusLabels, type CourseSyllabusModule, type CourseSyllabusProps, DATA_REQUEST_FIELD_LIMITS, DEFAULT_COURSE_SYLLABUS_LABELS, DEFAULT_DATA_REQUEST_FORM_LABELS, DEFAULT_PRODUCT_CAROUSEL_LABELS, DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS, DataRequestForm, type DataRequestFormLabels, type DataRequestFormProps, type DataRequestSubmitResult, ProductCarousel, type ProductCarouselCustomCardProps, type ProductCarouselDefaultCardProps, type ProductCarouselLabels, type ProductCarouselProps, ShareButtons, type ShareButtonsLabels, type ShareButtonsProps, type UseCollectionOptions, type UseCollectionResult, WITHDRAWAL_REQUEST_FIELD_LIMITS, WithdrawalRequestForm, type WithdrawalRequestFormLabels, type WithdrawalRequestFormProps, type WithdrawalRequestSubmitResult, useCollection };
|
package/dist/client.js
CHANGED
|
@@ -1279,6 +1279,277 @@ function DataRequestForm({
|
|
|
1279
1279
|
}
|
|
1280
1280
|
);
|
|
1281
1281
|
}
|
|
1282
|
+
|
|
1283
|
+
// src/components/withdrawal-request-form.tsx
|
|
1284
|
+
import { useEffect as useEffect5, useId as useId4, useRef as useRef5, useState as useState8 } from "react";
|
|
1285
|
+
import { cn as cn10 } from "@reblu/site-ui/utils";
|
|
1286
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1287
|
+
var WITHDRAWAL_REQUEST_FIELD_LIMITS = Object.freeze({
|
|
1288
|
+
orderReference: 100,
|
|
1289
|
+
email: 320,
|
|
1290
|
+
name: 200,
|
|
1291
|
+
items: 2e3,
|
|
1292
|
+
reason: 2e3
|
|
1293
|
+
});
|
|
1294
|
+
var DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS = {
|
|
1295
|
+
orderReferenceLabel: "N\xFAmero de pedido",
|
|
1296
|
+
nameLabel: "Nombre y apellidos (opcional)",
|
|
1297
|
+
emailLabel: "Email",
|
|
1298
|
+
itemsLabel: "Qu\xE9 productos o servicios quieres devolver o cancelar",
|
|
1299
|
+
itemsPlaceholder: "Indica los productos, bonos o cursos y las unidades",
|
|
1300
|
+
reasonLabel: "Motivo (opcional)",
|
|
1301
|
+
notice: "Usaremos estos datos solo para gestionar tu desistimiento.",
|
|
1302
|
+
submit: "Enviar desistimiento",
|
|
1303
|
+
submitting: "Enviando\u2026",
|
|
1304
|
+
orderReferenceRequired: "Indica el n\xFAmero de pedido.",
|
|
1305
|
+
emailInvalid: "Introduce un email v\xE1lido.",
|
|
1306
|
+
itemsRequired: "Indica de qu\xE9 productos o servicios desistes.",
|
|
1307
|
+
success: (receivedAt) => /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1308
|
+
"Hemos recibido tu desistimiento el ",
|
|
1309
|
+
receivedAt,
|
|
1310
|
+
". Te hemos enviado una copia por email."
|
|
1311
|
+
] }),
|
|
1312
|
+
successWithoutEmail: (receivedAt) => /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1313
|
+
"Hemos recibido tu desistimiento el ",
|
|
1314
|
+
receivedAt,
|
|
1315
|
+
". Guarda esta pantalla como justificante de lo que nos has comunicado:"
|
|
1316
|
+
] }),
|
|
1317
|
+
receiptOrderReference: "Pedido",
|
|
1318
|
+
receiptItems: "Productos o servicios",
|
|
1319
|
+
captchaError: "No hemos podido verificar que no eres un robot. Recarga la p\xE1gina e int\xE9ntalo de nuevo.",
|
|
1320
|
+
validationError: "Revisa los datos del formulario e int\xE9ntalo de nuevo.",
|
|
1321
|
+
rateLimitError: "Has enviado demasiadas solicitudes. Espera unos minutos e int\xE9ntalo de nuevo.",
|
|
1322
|
+
genericError: "No hemos podido enviar tu desistimiento. Int\xE9ntalo de nuevo m\xE1s tarde."
|
|
1323
|
+
};
|
|
1324
|
+
var FIELD_ORDER2 = ["orderReference", "email", "items"];
|
|
1325
|
+
var EMAIL_PATTERN2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1326
|
+
async function postToRoute2(endpoint, input) {
|
|
1327
|
+
try {
|
|
1328
|
+
const res = await fetch(endpoint, {
|
|
1329
|
+
method: "POST",
|
|
1330
|
+
headers: { "Content-Type": "application/json" },
|
|
1331
|
+
body: JSON.stringify(input)
|
|
1332
|
+
});
|
|
1333
|
+
if (!res.ok) return { ok: false, status: res.status };
|
|
1334
|
+
const json = await res.json();
|
|
1335
|
+
return json.data ? { ok: true, data: json.data } : { ok: false, status: res.status };
|
|
1336
|
+
} catch {
|
|
1337
|
+
return { ok: false, status: 0 };
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
function rejectionMessage2(status, t) {
|
|
1341
|
+
if (status === 400) return t.validationError;
|
|
1342
|
+
if (status === 422) return t.captchaError;
|
|
1343
|
+
if (status === 429) return t.rateLimitError;
|
|
1344
|
+
return t.genericError;
|
|
1345
|
+
}
|
|
1346
|
+
var FIELD_CLASS2 = "w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20";
|
|
1347
|
+
function WithdrawalRequestForm({
|
|
1348
|
+
getCaptchaToken,
|
|
1349
|
+
submit,
|
|
1350
|
+
endpoint = "/api/withdrawal-requests",
|
|
1351
|
+
onInteract,
|
|
1352
|
+
onSubmitted,
|
|
1353
|
+
labels,
|
|
1354
|
+
locale = "es-ES",
|
|
1355
|
+
className
|
|
1356
|
+
}) {
|
|
1357
|
+
const t = { ...DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS, ...labels };
|
|
1358
|
+
const id = useId4();
|
|
1359
|
+
const [orderReference, setOrderReference] = useState8("");
|
|
1360
|
+
const [name, setName] = useState8("");
|
|
1361
|
+
const [email, setEmail] = useState8("");
|
|
1362
|
+
const [items, setItems] = useState8("");
|
|
1363
|
+
const [reason, setReason] = useState8("");
|
|
1364
|
+
const [errors, setErrors] = useState8({});
|
|
1365
|
+
const [formError, setFormError] = useState8(null);
|
|
1366
|
+
const [submitting, setSubmitting] = useState8(false);
|
|
1367
|
+
const [receipt, setReceipt] = useState8(null);
|
|
1368
|
+
const statusRef = useRef5(null);
|
|
1369
|
+
useEffect5(() => {
|
|
1370
|
+
if (receipt) statusRef.current?.focus();
|
|
1371
|
+
}, [receipt]);
|
|
1372
|
+
async function handleSubmit(event) {
|
|
1373
|
+
event.preventDefault();
|
|
1374
|
+
if (submitting) return;
|
|
1375
|
+
setFormError(null);
|
|
1376
|
+
const next = {};
|
|
1377
|
+
if (!orderReference.trim()) next.orderReference = t.orderReferenceRequired;
|
|
1378
|
+
if (!EMAIL_PATTERN2.test(email.trim())) next.email = t.emailInvalid;
|
|
1379
|
+
if (!items.trim()) next.items = t.itemsRequired;
|
|
1380
|
+
setErrors(next);
|
|
1381
|
+
const firstInvalid = FIELD_ORDER2.find((field) => next[field]);
|
|
1382
|
+
if (firstInvalid) {
|
|
1383
|
+
document.getElementById(`${id}-${firstInvalid}`)?.focus();
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
setSubmitting(true);
|
|
1387
|
+
try {
|
|
1388
|
+
const captcha = await getCaptchaToken().catch(() => void 0);
|
|
1389
|
+
if (!captcha) {
|
|
1390
|
+
setFormError(t.captchaError);
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
const input = {
|
|
1394
|
+
orderReference: orderReference.trim(),
|
|
1395
|
+
email: email.trim(),
|
|
1396
|
+
...name.trim() ? { name: name.trim() } : {},
|
|
1397
|
+
items: items.trim(),
|
|
1398
|
+
...reason.trim() ? { reason: reason.trim() } : {},
|
|
1399
|
+
captcha
|
|
1400
|
+
};
|
|
1401
|
+
const result = await (submit ?? ((body) => postToRoute2(endpoint, body)))(input).catch(
|
|
1402
|
+
() => ({ ok: false, status: 0 })
|
|
1403
|
+
);
|
|
1404
|
+
if (!result.ok) {
|
|
1405
|
+
setFormError(rejectionMessage2(result.status, t));
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
setReceipt({ ack: result.data, orderReference: input.orderReference, items: input.items });
|
|
1409
|
+
onSubmitted?.(result.data);
|
|
1410
|
+
} finally {
|
|
1411
|
+
setSubmitting(false);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (receipt) {
|
|
1415
|
+
const receivedAt = new Intl.DateTimeFormat(locale, {
|
|
1416
|
+
dateStyle: "long",
|
|
1417
|
+
timeStyle: "short"
|
|
1418
|
+
}).format(new Date(receipt.ack.receivedAt));
|
|
1419
|
+
return /* @__PURE__ */ jsx10(
|
|
1420
|
+
"div",
|
|
1421
|
+
{
|
|
1422
|
+
ref: statusRef,
|
|
1423
|
+
tabIndex: -1,
|
|
1424
|
+
role: "status",
|
|
1425
|
+
"data-slot": "withdrawal-request-form-success",
|
|
1426
|
+
className: cn10("space-y-3 rounded-lg border border-border p-6 text-sm", className),
|
|
1427
|
+
children: receipt.ack.acknowledgementSent ? /* @__PURE__ */ jsx10("p", { children: t.success(receivedAt) }) : /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1428
|
+
/* @__PURE__ */ jsx10("p", { children: t.successWithoutEmail(receivedAt) }),
|
|
1429
|
+
/* @__PURE__ */ jsxs8("dl", { className: "space-y-1", children: [
|
|
1430
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
1431
|
+
/* @__PURE__ */ jsx10("dt", { className: "font-medium", children: t.receiptOrderReference }),
|
|
1432
|
+
/* @__PURE__ */ jsx10("dd", { children: receipt.orderReference })
|
|
1433
|
+
] }),
|
|
1434
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
1435
|
+
/* @__PURE__ */ jsx10("dt", { className: "font-medium", children: t.receiptItems }),
|
|
1436
|
+
/* @__PURE__ */ jsx10("dd", { className: "whitespace-pre-line", children: receipt.items })
|
|
1437
|
+
] })
|
|
1438
|
+
] })
|
|
1439
|
+
] })
|
|
1440
|
+
}
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
const errorId = (field) => errors[field] ? `${id}-${field}-error` : void 0;
|
|
1444
|
+
const fieldError = (field) => errors[field] ? /* @__PURE__ */ jsx10("p", { id: `${id}-${field}-error`, className: "text-xs text-destructive", children: errors[field] }) : null;
|
|
1445
|
+
const borderFor = (field) => errors[field] ? "border-destructive" : "border-border";
|
|
1446
|
+
return /* @__PURE__ */ jsxs8(
|
|
1447
|
+
"form",
|
|
1448
|
+
{
|
|
1449
|
+
noValidate: true,
|
|
1450
|
+
onSubmit: handleSubmit,
|
|
1451
|
+
onFocusCapture: onInteract,
|
|
1452
|
+
onPointerDown: onInteract,
|
|
1453
|
+
"data-slot": "withdrawal-request-form",
|
|
1454
|
+
className: cn10("space-y-4", className),
|
|
1455
|
+
children: [
|
|
1456
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
|
|
1457
|
+
/* @__PURE__ */ jsx10("label", { htmlFor: `${id}-orderReference`, className: "text-sm font-medium", children: t.orderReferenceLabel }),
|
|
1458
|
+
/* @__PURE__ */ jsx10(
|
|
1459
|
+
"input",
|
|
1460
|
+
{
|
|
1461
|
+
id: `${id}-orderReference`,
|
|
1462
|
+
type: "text",
|
|
1463
|
+
maxLength: WITHDRAWAL_REQUEST_FIELD_LIMITS.orderReference,
|
|
1464
|
+
value: orderReference,
|
|
1465
|
+
onChange: (e) => setOrderReference(e.target.value),
|
|
1466
|
+
"aria-invalid": errors.orderReference ? true : void 0,
|
|
1467
|
+
"aria-describedby": errorId("orderReference"),
|
|
1468
|
+
className: cn10(FIELD_CLASS2, borderFor("orderReference"))
|
|
1469
|
+
}
|
|
1470
|
+
),
|
|
1471
|
+
fieldError("orderReference")
|
|
1472
|
+
] }),
|
|
1473
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
|
|
1474
|
+
/* @__PURE__ */ jsx10("label", { htmlFor: `${id}-name`, className: "text-sm font-medium", children: t.nameLabel }),
|
|
1475
|
+
/* @__PURE__ */ jsx10(
|
|
1476
|
+
"input",
|
|
1477
|
+
{
|
|
1478
|
+
id: `${id}-name`,
|
|
1479
|
+
type: "text",
|
|
1480
|
+
autoComplete: "name",
|
|
1481
|
+
maxLength: WITHDRAWAL_REQUEST_FIELD_LIMITS.name,
|
|
1482
|
+
value: name,
|
|
1483
|
+
onChange: (e) => setName(e.target.value),
|
|
1484
|
+
className: cn10(FIELD_CLASS2, "border-border")
|
|
1485
|
+
}
|
|
1486
|
+
)
|
|
1487
|
+
] }),
|
|
1488
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
|
|
1489
|
+
/* @__PURE__ */ jsx10("label", { htmlFor: `${id}-email`, className: "text-sm font-medium", children: t.emailLabel }),
|
|
1490
|
+
/* @__PURE__ */ jsx10(
|
|
1491
|
+
"input",
|
|
1492
|
+
{
|
|
1493
|
+
id: `${id}-email`,
|
|
1494
|
+
type: "email",
|
|
1495
|
+
autoComplete: "email",
|
|
1496
|
+
maxLength: WITHDRAWAL_REQUEST_FIELD_LIMITS.email,
|
|
1497
|
+
value: email,
|
|
1498
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1499
|
+
"aria-invalid": errors.email ? true : void 0,
|
|
1500
|
+
"aria-describedby": errorId("email"),
|
|
1501
|
+
className: cn10(FIELD_CLASS2, borderFor("email"))
|
|
1502
|
+
}
|
|
1503
|
+
),
|
|
1504
|
+
fieldError("email")
|
|
1505
|
+
] }),
|
|
1506
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
|
|
1507
|
+
/* @__PURE__ */ jsx10("label", { htmlFor: `${id}-items`, className: "text-sm font-medium", children: t.itemsLabel }),
|
|
1508
|
+
/* @__PURE__ */ jsx10(
|
|
1509
|
+
"textarea",
|
|
1510
|
+
{
|
|
1511
|
+
id: `${id}-items`,
|
|
1512
|
+
rows: 3,
|
|
1513
|
+
maxLength: WITHDRAWAL_REQUEST_FIELD_LIMITS.items,
|
|
1514
|
+
value: items,
|
|
1515
|
+
onChange: (e) => setItems(e.target.value),
|
|
1516
|
+
placeholder: t.itemsPlaceholder,
|
|
1517
|
+
"aria-invalid": errors.items ? true : void 0,
|
|
1518
|
+
"aria-describedby": errorId("items"),
|
|
1519
|
+
className: cn10(FIELD_CLASS2, borderFor("items"))
|
|
1520
|
+
}
|
|
1521
|
+
),
|
|
1522
|
+
fieldError("items")
|
|
1523
|
+
] }),
|
|
1524
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
|
|
1525
|
+
/* @__PURE__ */ jsx10("label", { htmlFor: `${id}-reason`, className: "text-sm font-medium", children: t.reasonLabel }),
|
|
1526
|
+
/* @__PURE__ */ jsx10(
|
|
1527
|
+
"textarea",
|
|
1528
|
+
{
|
|
1529
|
+
id: `${id}-reason`,
|
|
1530
|
+
rows: 3,
|
|
1531
|
+
maxLength: WITHDRAWAL_REQUEST_FIELD_LIMITS.reason,
|
|
1532
|
+
value: reason,
|
|
1533
|
+
onChange: (e) => setReason(e.target.value),
|
|
1534
|
+
className: cn10(FIELD_CLASS2, "border-border")
|
|
1535
|
+
}
|
|
1536
|
+
)
|
|
1537
|
+
] }),
|
|
1538
|
+
t.notice ? /* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t.notice }) : null,
|
|
1539
|
+
formError && /* @__PURE__ */ jsx10("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
|
|
1540
|
+
/* @__PURE__ */ jsx10(
|
|
1541
|
+
"button",
|
|
1542
|
+
{
|
|
1543
|
+
type: "submit",
|
|
1544
|
+
disabled: submitting,
|
|
1545
|
+
className: "cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60",
|
|
1546
|
+
children: submitting ? t.submitting : t.submit
|
|
1547
|
+
}
|
|
1548
|
+
)
|
|
1549
|
+
]
|
|
1550
|
+
}
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1282
1553
|
export {
|
|
1283
1554
|
AddToCartButton,
|
|
1284
1555
|
CartIcon,
|
|
@@ -1289,9 +1560,12 @@ export {
|
|
|
1289
1560
|
DEFAULT_COURSE_SYLLABUS_LABELS,
|
|
1290
1561
|
DEFAULT_DATA_REQUEST_FORM_LABELS,
|
|
1291
1562
|
DEFAULT_PRODUCT_CAROUSEL_LABELS,
|
|
1563
|
+
DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS,
|
|
1292
1564
|
DataRequestForm,
|
|
1293
1565
|
ProductCarousel,
|
|
1294
1566
|
ShareButtons,
|
|
1567
|
+
WITHDRAWAL_REQUEST_FIELD_LIMITS,
|
|
1568
|
+
WithdrawalRequestForm,
|
|
1295
1569
|
useCollection
|
|
1296
1570
|
};
|
|
1297
1571
|
//# sourceMappingURL=client.js.map
|