feat(forms): add local Form A presentation preview
This commit is contained in:
13 files changed
+983
-8
No files matched your search
@@ -25,7 +25,8 @@ const mockResult = resolveMockFormResult(import.meta.env.PUBLIC_AIA_FORMS_MOCK_R
|
||||
<label>{text.email}<input type="email" name="email" autocomplete="email" disabled={mode === 'off'} /></label>
|
||||
<label>{text.phone}<input type="tel" name="phone" autocomplete="tel" disabled={mode === 'off'} /></label>
|
||||
<label>{text.service}<select name="service" disabled={mode === 'off'}>{text.options.map((label) => <option>{label}</option>)}</select></label>
|
||||
<label>{text.message}<textarea name="message" rows="5" disabled={mode === 'off'}></textarea></label>
|
||||
<label>{text.message}<textarea name="message" rows="5" aria-describedby="contact-sensitive-note" disabled={mode === 'off'}></textarea></label>
|
||||
<p id="contact-sensitive-note" class="contact-form-card__caution" role="note">{text.formAPreview.caution}</p>
|
||||
<Button type="button" data-mock-submit disabled={mode === 'off'} aria-describedby="contact-form-note" class="contact-form-card__submit">{text.submit}</Button>
|
||||
<p id="contact-form-note" class="contact-form-card__note" data-form-status>{mode === 'mock' ? text.mock : <>{text.disabled} <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />.</>}</p>
|
||||
</div>
|
||||
@@ -99,4 +100,11 @@ const mockResult = resolveMockFormResult(import.meta.env.PUBLIC_AIA_FORMS_MOCK_R
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-form-card__caution {
|
||||
color: var(--color-muted);
|
||||
font-size: .85rem;
|
||||
line-height: 1.65;
|
||||
margin: -.25rem 0 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,279 @@
|
||||
---
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormMode } from '../utils/form-integration';
|
||||
import { formMessages } from '../utils/form-messages';
|
||||
|
||||
interface Props {
|
||||
lang?: 'en' | 'es';
|
||||
variant?: 'full' | 'compact';
|
||||
submitText?: string;
|
||||
}
|
||||
|
||||
const { lang = 'en', variant = 'full', submitText } = Astro.props;
|
||||
const text = formMessages(lang).formAPreview;
|
||||
const config = resolveFormMode(import.meta.env.PUBLIC_AIA_FORMS_MODE, import.meta.env.PUBLIC_AIA_FORMS_LIVE_APPROVED);
|
||||
const mode = formModeForRuntime(config);
|
||||
const enabled = formAPreviewEnabled(import.meta.env.PUBLIC_AIA_FORM_A_PREVIEW, import.meta.env.PUBLIC_ALLOW_INDEXING, mode);
|
||||
const prefix = `form-a-${variant}`;
|
||||
const buttonLabel = submitText ?? text.submit;
|
||||
---
|
||||
<div
|
||||
class:list={['form-a-preview', `form-a-preview--${variant}`]}
|
||||
role="group"
|
||||
aria-labelledby={`${prefix}-heading`}
|
||||
data-form-a-preview
|
||||
data-form-a-variant={variant}
|
||||
data-form-a-preview-enabled={String(enabled)}
|
||||
data-form-mode={mode}
|
||||
data-required-error={text.requiredError}
|
||||
data-email-error={text.emailError}
|
||||
data-email-required={text.emailRequired}
|
||||
data-email-optional={text.optional}
|
||||
data-success={text.success}
|
||||
>
|
||||
<p class="form-a-preview__eyebrow">{text.internalPreview}</p>
|
||||
<h3 id={`${prefix}-heading`}>{text.heading}</h3>
|
||||
{variant === 'full' && <p class="form-a-preview__note">{text.previewNote}</p>}
|
||||
|
||||
<div class="form-a-preview__fields">
|
||||
<div class="form-a-preview__field form-a-preview__field--full">
|
||||
<label for={`${prefix}-name`}>{text.name}<span aria-hidden="true"> *</span></label>
|
||||
<input id={`${prefix}-name`} name="name" autocomplete="name" required disabled={!enabled} />
|
||||
</div>
|
||||
|
||||
<div class="form-a-preview__field">
|
||||
<label for={`${prefix}-phone`}>{text.phone}<span aria-hidden="true"> *</span></label>
|
||||
<input id={`${prefix}-phone`} name="phone" type="tel" autocomplete="tel" required disabled={!enabled} />
|
||||
</div>
|
||||
|
||||
<div class="form-a-preview__field">
|
||||
<label for={`${prefix}-email`}>{text.email} <span id={`${prefix}-email-requirement`} class="form-a-preview__optional">({text.optional})</span></label>
|
||||
<input id={`${prefix}-email`} name="email" type="email" autocomplete="email" aria-describedby={`${prefix}-email-requirement`} disabled={!enabled} />
|
||||
</div>
|
||||
|
||||
<div class="form-a-preview__field form-a-preview__field--full">
|
||||
<label for={`${prefix}-preferred`}>{text.preferredContact}<span aria-hidden="true"> *</span></label>
|
||||
<select id={`${prefix}-preferred`} name="preferredContact" required disabled={!enabled}>
|
||||
<option value="">{text.choose}</option>
|
||||
<option value="phone">{text.phoneOption}</option>
|
||||
<option value="email">{text.emailOption}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{variant === 'full' && <div class="form-a-preview__field form-a-preview__field--full">
|
||||
<label for={`${prefix}-message`}>{text.message} <span class="form-a-preview__optional">({text.optional})</span></label>
|
||||
<textarea id={`${prefix}-message`} name="message" rows="4" disabled={!enabled}></textarea>
|
||||
<p class="form-a-preview__caution" role="note">{text.caution}</p>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
<button class="form-a-preview__submit" type="button" data-preview-submit disabled={!enabled}>{buttonLabel}</button>
|
||||
{variant === 'compact' ? (
|
||||
<p class="form-a-preview__status" data-preview-status data-form-status-notice role="status" aria-live="polite">
|
||||
{text.previewOnly} <a href="tel:+14806877099">{text.callAia}</a>. <a href="/privacy-policy">{text.privacyPolicy}</a>.
|
||||
</p>
|
||||
) : (
|
||||
<p class="form-a-preview__status" data-preview-status role="status" aria-live="polite">{enabled ? text.previewNote : text.noScript}</p>
|
||||
)}
|
||||
{variant === 'full' && <noscript><p class="form-a-preview__noscript">{text.noScript}</p></noscript>}
|
||||
</div>
|
||||
|
||||
{enabled && <script>
|
||||
const preview = document.querySelector<HTMLElement>('[data-form-a-preview][data-form-a-preview-enabled="true"]');
|
||||
if (preview) {
|
||||
const preferred = preview.querySelector<HTMLSelectElement>('select[name="preferredContact"]');
|
||||
const email = preview.querySelector<HTMLInputElement>('input[name="email"]');
|
||||
const requirement = preview.querySelector<HTMLElement>('[id$="-email-requirement"]');
|
||||
const button = preview.querySelector<HTMLButtonElement>('[data-preview-submit]');
|
||||
const status = preview.querySelector<HTMLElement>('[data-preview-status]');
|
||||
const initialStatus = status?.innerHTML ?? '';
|
||||
const updateEmailRequirement = () => {
|
||||
const required = preferred?.value === 'email';
|
||||
if (email) {
|
||||
email.required = required;
|
||||
email.setAttribute('aria-required', String(required));
|
||||
}
|
||||
if (requirement) {
|
||||
const label = required ? preview.dataset.emailRequired : preview.dataset.emailOptional;
|
||||
requirement.textContent = `(${label ?? ''})`;
|
||||
}
|
||||
};
|
||||
preferred?.addEventListener('change', updateEmailRequirement);
|
||||
updateEmailRequirement();
|
||||
button?.addEventListener('click', () => {
|
||||
const name = preview.querySelector<HTMLInputElement>('input[name="name"]');
|
||||
const phone = preview.querySelector<HTMLInputElement>('input[name="phone"]');
|
||||
const fields = [name, phone, preferred].filter(Boolean) as Array<HTMLInputElement | HTMLSelectElement>;
|
||||
fields.forEach((field) => field.removeAttribute('aria-invalid'));
|
||||
email?.removeAttribute('aria-invalid');
|
||||
const missing = fields.filter((field) => !field.value.trim());
|
||||
const invalidEmail = preferred?.value === 'email' && (!email?.value.trim() || !email.validity.valid);
|
||||
if (missing.length || invalidEmail) {
|
||||
missing.forEach((field) => field.setAttribute('aria-invalid', 'true'));
|
||||
if (invalidEmail) email?.setAttribute('aria-invalid', 'true');
|
||||
if (status) status.textContent = invalidEmail ? (preview.dataset.emailError ?? '') : (preview.dataset.requiredError ?? '');
|
||||
(missing[0] ?? email)?.focus();
|
||||
return;
|
||||
}
|
||||
if (status) {
|
||||
if (preview.dataset.formAVariant === 'compact') status.innerHTML = initialStatus;
|
||||
else status.textContent = preview.dataset.success ?? 'Demo only: no information was sent or saved.';
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>}
|
||||
|
||||
<style>
|
||||
.form-a-preview {
|
||||
color: var(--color-primary, #254080);
|
||||
}
|
||||
|
||||
.form-a-preview--full {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 4px rgb(81 81 81 / 4%), 0 4px 16px rgb(81 81 81 / 8%);
|
||||
margin: 0 auto;
|
||||
max-width: 600px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.form-a-preview__eyebrow {
|
||||
color: var(--color-muted, #65718a);
|
||||
font-size: .72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .08em;
|
||||
margin: 0 0 .35rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.form-a-preview h3 {
|
||||
margin: 0 0 .4rem;
|
||||
}
|
||||
|
||||
.form-a-preview__note,
|
||||
.form-a-preview__caution,
|
||||
.form-a-preview__status,
|
||||
.form-a-preview__noscript {
|
||||
color: var(--color-muted, #65718a);
|
||||
font-size: .85rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.form-a-preview__fields {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.form-a-preview__field {
|
||||
display: grid;
|
||||
gap: .4rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-a-preview__field--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.form-a-preview label {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.form-a-preview input,
|
||||
.form-a-preview select,
|
||||
.form-a-preview textarea {
|
||||
background: var(--color-background, #fcf9f5);
|
||||
border: 1px solid var(--color-border, #edeef3);
|
||||
border-radius: 4px;
|
||||
color: var(--color-primary, #254080);
|
||||
font: inherit;
|
||||
min-height: 2.9rem;
|
||||
padding: .7rem .8rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-a-preview textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.form-a-preview input:focus-visible,
|
||||
.form-a-preview select:focus-visible,
|
||||
.form-a-preview textarea:focus-visible,
|
||||
.form-a-preview__submit:focus-visible {
|
||||
outline: 3px solid var(--color-focus, #115bbd);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.form-a-preview [aria-invalid="true"] {
|
||||
border-color: #a21c28;
|
||||
outline: 2px solid #a21c28;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.form-a-preview__optional {
|
||||
color: var(--color-muted, #65718a);
|
||||
font-size: .82em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-a-preview__caution {
|
||||
color: var(--color-muted, #254080);
|
||||
font-size: .85rem;
|
||||
line-height: 1.65;
|
||||
margin: .8rem 0 0;
|
||||
}
|
||||
|
||||
.form-a-preview__submit {
|
||||
background: var(--color-accent, #ffbd59);
|
||||
border: 2px solid var(--color-accent, #ffbd59);
|
||||
border-radius: 4px;
|
||||
color: var(--color-primary, #254080);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
min-height: 3rem;
|
||||
padding: .65rem 1.2rem;
|
||||
transition: .3s linear all;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-a-preview__submit:hover,
|
||||
.form-a-preview__submit:focus-visible {
|
||||
background: transparent;
|
||||
border-color: var(--color-accent-strong, #ff914d);
|
||||
color: var(--color-primary, #254080);
|
||||
}
|
||||
|
||||
.form-a-preview__status {
|
||||
margin: .75rem 0 0;
|
||||
}
|
||||
|
||||
.form-a-preview__status a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: .15em;
|
||||
}
|
||||
|
||||
.form-a-preview__noscript {
|
||||
margin: .75rem 0 0;
|
||||
}
|
||||
|
||||
.form-a-preview--compact {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.form-a-preview--compact .form-a-preview__fields {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.form-a-preview--full .form-a-preview__fields {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-a-preview--full .form-a-preview__field--full {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
import Icon from './Icon.astro';
|
||||
import FormAPreview from '../FormAPreview.astro';
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormMode } from '../../utils/form-integration';
|
||||
interface Props {
|
||||
sourcePage: 'general' | 'commercial' | 'ahcccs' | 'private_pay';
|
||||
title: string;
|
||||
@@ -9,6 +11,8 @@ interface Props {
|
||||
const { sourcePage, title, intro, buttonText } = Astro.props;
|
||||
const phone = import.meta.env.PUBLIC_AIA_PHONE || '480-687-7099';
|
||||
const tel = phone.replace(/[^0-9+]/g, '');
|
||||
const formConfig = resolveFormMode(import.meta.env.PUBLIC_AIA_FORMS_MODE, import.meta.env.PUBLIC_AIA_FORMS_LIVE_APPROVED);
|
||||
const formPreview = formAPreviewEnabled(import.meta.env.PUBLIC_AIA_FORM_A_PREVIEW, import.meta.env.PUBLIC_ALLOW_INDEXING, formModeForRuntime(formConfig));
|
||||
---
|
||||
<section class="section consultation" id="consultation" aria-labelledby="consultation-heading">
|
||||
<div class="container consultation__panel grid-2">
|
||||
@@ -18,9 +22,9 @@ const tel = phone.replace(/[^0-9+]/g, '');
|
||||
<p class="lede">{intro}</p>
|
||||
</div>
|
||||
<div class="form-wrap">
|
||||
<!-- TODO: Replace this inactive presentation with the approved Jotform integration under P9-03. -->
|
||||
<!-- TODO: Verify privacy, spam protection and notifications before production activation. -->
|
||||
<div id="lead-form" role="group" aria-labelledby="campaign-form-heading" data-forms-mode="off">
|
||||
{formPreview ? <FormAPreview lang="en" variant="compact" submitText={buttonText} /> : <div id="lead-form" role="group" aria-labelledby="campaign-form-heading" data-forms-mode="off">
|
||||
<!-- TODO: Replace this inactive presentation with the approved Jotform integration under P9-03. -->
|
||||
<!-- TODO: Verify privacy, spam protection and notifications before production activation. -->
|
||||
<h3 id="campaign-form-heading">Start with your contact details</h3>
|
||||
<p class="form-note form-intro">Use this form to request a conversation. An AIA team member can explain the next steps for your family.</p>
|
||||
<input disabled type="hidden" name="sourcePage" value={sourcePage} />
|
||||
@@ -53,7 +57,7 @@ const tel = phone.replace(/[^0-9+]/g, '');
|
||||
<div id="form-error" class="form-error" role="alert" tabindex="-1"></div>
|
||||
<button class="button" type="button" disabled>{buttonText}<Icon name="arrow" /></button>
|
||||
<p class="form-note">Questions? <a href={`tel:${tel}`}>Call AIA at {phone}</a>. Review AIA's <a href="/privacy-policy">privacy policy</a>.</p>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
<div class="consultation__details">
|
||||
<div class="consultation__contact">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
import ContactForm from '../ContactForm.astro';
|
||||
import FormAPreview from '../FormAPreview.astro';
|
||||
import { formMessages } from '../../utils/form-messages';
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormMode } from '../../utils/form-integration';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -9,6 +11,8 @@ interface Props {
|
||||
|
||||
const { title, lang } = Astro.props;
|
||||
const text = formMessages(lang);
|
||||
const formConfig = resolveFormMode(import.meta.env.PUBLIC_AIA_FORMS_MODE, import.meta.env.PUBLIC_AIA_FORMS_LIVE_APPROVED);
|
||||
const formPreview = formAPreviewEnabled(import.meta.env.PUBLIC_AIA_FORM_A_PREVIEW, import.meta.env.PUBLIC_ALLOW_INDEXING, formModeForRuntime(formConfig));
|
||||
---
|
||||
|
||||
<section class="contact-page">
|
||||
@@ -16,7 +20,7 @@ const text = formMessages(lang);
|
||||
<div class="contact-page__copy">
|
||||
<slot />
|
||||
</div>
|
||||
<ContactForm title={title} lang={lang} />
|
||||
{formPreview ? <FormAPreview lang={lang as 'en' | 'es'} variant="full" submitText={lang === 'es' ? 'Enviar' : 'Submit'} /> : <ContactForm title={title} lang={lang} />}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -21,6 +21,21 @@ export function formModeForRuntime(config: FormModeConfig): 'off' | 'mock' {
|
||||
return config.mode === 'mock' ? 'mock' : 'off';
|
||||
}
|
||||
|
||||
/**
|
||||
* The Form A meeting preview is deliberately narrower than the general mock
|
||||
* mode: it is available only for an explicitly requested, non-indexing mock
|
||||
* build. This keeps the review UI out of default and production-shaped output.
|
||||
*/
|
||||
export function formAPreviewEnabled(
|
||||
rawPreview: string | undefined,
|
||||
allowIndexing: string | undefined,
|
||||
runtimeMode: 'off' | 'mock'
|
||||
): boolean {
|
||||
return rawPreview?.trim().toLowerCase() === 'true'
|
||||
&& allowIndexing?.trim().toLowerCase() !== 'true'
|
||||
&& runtimeMode === 'mock';
|
||||
}
|
||||
|
||||
export function resolveMockFormResult(rawResult: string | undefined): MockFormResult {
|
||||
const result = rawResult?.trim().toLowerCase();
|
||||
return result === 'failed' || result === 'unavailable' ? result : 'success';
|
||||
|
||||
@@ -10,7 +10,20 @@ const messages = {
|
||||
mock: 'Demo only: this local fixture never sends information.', mockSuccess: 'Demo only: the local fixture completed without sending anything.', mockFailed: 'Demo only: the local fixture simulated a failed submission.', mockUnavailable: 'Demo only: the local fixture simulated an unavailable service.',
|
||||
map: 'Map showing Arizona Institute for Autism in Scottsdale',
|
||||
tile: 'OpenStreetMap tile showing area around Arizona Institute for Autism in Scottsdale, AZ',
|
||||
zoomIn: 'Zoom in', zoomOut: 'Zoom out', marker: 'Arizona Institute for Autism'
|
||||
zoomIn: 'Zoom in', zoomOut: 'Zoom out', marker: 'Arizona Institute for Autism',
|
||||
formAPreview: {
|
||||
internalPreview: 'Internal meeting preview', heading: 'General enquiries preview',
|
||||
name: 'Your name', phone: 'Phone number', email: 'Email address',
|
||||
preferredContact: 'Preferred contact method', phoneOption: 'Phone', emailOption: 'Email',
|
||||
message: 'How can we help?', caution: 'Please avoid entering detailed medical, diagnostic, or insurance-member information here. An AIA team member can gather any needed details through the appropriate follow-up process.',
|
||||
submit: 'Submit', optional: 'optional', emailRequired: 'required for email contact',
|
||||
choose: 'Choose one', previewNote: 'Internal preview only: nothing is sent, saved or emailed.',
|
||||
previewOnly: 'Preview only — nothing is submitted.', callAia: 'Call AIA at (480) 687-7099', privacyPolicy: 'Privacy policy',
|
||||
noScript: 'This local preview needs JavaScript for its demonstration controls. No information is sent.',
|
||||
requiredError: 'Please complete your name, phone number and preferred contact method.',
|
||||
emailError: 'Please enter an email address when Email is your preferred contact method.',
|
||||
success: 'Demo only: Form A preview completed locally. No information was sent or saved.'
|
||||
}
|
||||
},
|
||||
es: {
|
||||
heading: 'Programe su consulta gratuita', eyebrow: 'Comience aquí',
|
||||
@@ -22,7 +35,20 @@ const messages = {
|
||||
mock: 'Solo demostración: esta prueba local nunca envía información.', mockSuccess: 'Solo demostración: la prueba local terminó sin enviar información.', mockFailed: 'Solo demostración: la prueba local simuló un envío fallido.', mockUnavailable: 'Solo demostración: la prueba local simuló un servicio no disponible.',
|
||||
map: 'Mapa de la ubicación de Arizona Institute for Autism en Scottsdale',
|
||||
tile: 'Mapa de OpenStreetMap de los alrededores de Arizona Institute for Autism en Scottsdale, Arizona',
|
||||
zoomIn: 'Acercar', zoomOut: 'Alejar', marker: 'Arizona Institute for Autism'
|
||||
zoomIn: 'Acercar', zoomOut: 'Alejar', marker: 'Arizona Institute for Autism',
|
||||
formAPreview: {
|
||||
internalPreview: 'Vista previa para reunión interna', heading: 'Vista previa de consultas generales',
|
||||
name: 'Su nombre', phone: 'Número de teléfono', email: 'Correo electrónico',
|
||||
preferredContact: 'Método de contacto preferido', phoneOption: 'Teléfono', emailOption: 'Correo electrónico',
|
||||
message: '¿Cómo podemos ayudarle?', caution: 'Evite ingresar aquí información médica o diagnóstica detallada, o información del miembro del seguro. Un miembro del equipo de AIA puede recopilar los detalles necesarios mediante el proceso de seguimiento adecuado.',
|
||||
submit: 'Enviar', optional: 'opcional', emailRequired: 'obligatorio para contacto por correo',
|
||||
choose: 'Elija una opción', previewNote: 'Solo para revisión interna: no se envía, guarda ni comparte información.',
|
||||
previewOnly: 'Solo vista previa: no se envía nada.', callAia: 'Llame a AIA al (480) 687-7099', privacyPolicy: 'Política de privacidad',
|
||||
noScript: 'Esta vista previa local necesita JavaScript para sus controles de demostración. No se envía información.',
|
||||
requiredError: 'Complete su nombre, número de teléfono y método de contacto preferido.',
|
||||
emailError: 'Ingrese un correo electrónico cuando el correo sea su método de contacto preferido.',
|
||||
success: 'Solo demostración: la vista previa del Formulario A terminó localmente. No se envió ni guardó información.'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user