feat(forms): add isolated Form A iframe adapter
This commit is contained in:
18 files changed
+1345
-170
No files matched your search
@@ -0,0 +1,149 @@
|
||||
---
|
||||
import type { FormAIframeConfig } from '../utils/form-integration';
|
||||
|
||||
interface Props {
|
||||
config: FormAIframeConfig;
|
||||
heading: string;
|
||||
description?: string;
|
||||
variant?: 'standalone' | 'embedded';
|
||||
}
|
||||
|
||||
const { config, heading, description, variant = 'standalone' } = Astro.props;
|
||||
|
||||
if (!config.enabled || !config.url || !config.title || !config.fallbackText || !config.fallbackLinkText) {
|
||||
throw new Error('JotformIframe requires an enabled, fully validated Form A iframe configuration.');
|
||||
}
|
||||
|
||||
const escapeHtml = (value: string) => value.replace(/[&<>"']/g, (character) => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
})[character] ?? character);
|
||||
|
||||
// Astro treats iframe descendants as raw text. set:html emits standards-based
|
||||
// iframe fallback markup while preserving the fixed, server-validated URL.
|
||||
const fallbackMarkup = `<p class="jotform-iframe__fallback">${escapeHtml(config.fallbackText)} <a href="${escapeHtml(config.url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(config.fallbackLinkText)}</a></p>`;
|
||||
---
|
||||
|
||||
<section
|
||||
class:list={['jotform-iframe', { 'jotform-iframe--embedded': variant === 'embedded' }]}
|
||||
role="group"
|
||||
aria-labelledby="jotform-iframe-heading"
|
||||
data-jotform-form-a
|
||||
data-form-mode="isolated-test"
|
||||
data-form-environment="isolated-test"
|
||||
data-logical-form="general"
|
||||
data-form-context={config.context}
|
||||
data-jotform-source="approved-form-a"
|
||||
>
|
||||
<h3 id="jotform-iframe-heading" class="jotform-iframe__heading">{heading}</h3>
|
||||
{description && <p class="jotform-iframe__description">{description}</p>}
|
||||
<iframe
|
||||
class="jotform-iframe__frame"
|
||||
src={config.url}
|
||||
title={config.title}
|
||||
loading="eager"
|
||||
referrerpolicy="no-referrer"
|
||||
sandbox="allow-forms allow-scripts allow-same-origin"
|
||||
set:html={fallbackMarkup}
|
||||
></iframe>
|
||||
</section>
|
||||
|
||||
<!-- Keep adapter styling route-local: the campaign and main-site bundles must not share campaign CSS. -->
|
||||
<style is:inline>
|
||||
.jotform-iframe {
|
||||
/*
|
||||
* Fixed heights keep the parent independent of cross-origin Jotform
|
||||
* messaging. The fixed allowance is measured from the rendered Builder
|
||||
* form at desktop widths, then includes explicit room for contact
|
||||
* validation errors and Spanish wrapping. The extra final allowance
|
||||
* prevents a Jotform blur-validation message from creating an internal
|
||||
* scrollbar. Landing uses only compact contact fields, so it retains a
|
||||
* smaller allowance.
|
||||
*/
|
||||
--jotform-frame-height: 60rem;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 4px rgb(81 81 81 / 4%), 0 4px 16px rgb(81 81 81 / 8%);
|
||||
color: var(--color-primary);
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
padding: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.jotform-iframe__heading {
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
margin: -1.5rem -1.5rem 1.25rem;
|
||||
padding: 1.2rem 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.jotform-iframe__frame {
|
||||
border: 0;
|
||||
display: block;
|
||||
height: var(--jotform-frame-height);
|
||||
max-width: 100%;
|
||||
min-height: var(--jotform-frame-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.jotform-iframe__fallback {
|
||||
color: var(--color-muted);
|
||||
font-size: .9rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
.jotform-iframe__description {
|
||||
color: var(--color-muted);
|
||||
font-size: .9rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
.jotform-iframe__fallback a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: .15em;
|
||||
}
|
||||
|
||||
.jotform-iframe__fallback a:focus-visible {
|
||||
outline: 3px solid var(--color-focus, #115bbd);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.jotform-iframe--embedded {
|
||||
--jotform-frame-height: 42rem;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jotform-iframe--embedded .jotform-iframe__heading {
|
||||
border: 0;
|
||||
margin: 0 0 1rem;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.jotform-iframe__frame {
|
||||
height: 68rem;
|
||||
min-height: 68rem;
|
||||
}
|
||||
|
||||
.jotform-iframe--embedded .jotform-iframe__frame {
|
||||
height: 50rem;
|
||||
min-height: 50rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
import Icon from './Icon.astro';
|
||||
import FormAPreview from '../FormAPreview.astro';
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormMode } from '../../utils/form-integration';
|
||||
import JotformIframe from '../JotformIframe.astro';
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormAIframe, resolveFormMode } from '../../utils/form-integration';
|
||||
interface Props {
|
||||
sourcePage: 'general' | 'commercial' | 'ahcccs' | 'private_pay';
|
||||
title: string;
|
||||
@@ -13,6 +14,23 @@ 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));
|
||||
const campaignRoute = sourcePage === 'general'
|
||||
? '/lp/aba-therapy'
|
||||
: sourcePage === 'commercial'
|
||||
? '/lp/aba-insurance'
|
||||
: sourcePage === 'ahcccs'
|
||||
? '/lp/aba-ahcccs'
|
||||
: '/lp/aba-private-pay';
|
||||
const iframeDescription = 'Use this form to request a conversation. An AIA team member can explain the next steps for your family.';
|
||||
const formAIframe = resolveFormAIframe({
|
||||
rawMode: import.meta.env.PUBLIC_AIA_FORMS_MODE,
|
||||
liveApproved: import.meta.env.PUBLIC_AIA_FORMS_LIVE_APPROVED,
|
||||
isolatedTestApproved: import.meta.env.PUBLIC_AIA_FORMS_ISOLATED_TEST_APPROVED,
|
||||
environmentRole: import.meta.env.PUBLIC_AIA_FORMS_ENVIRONMENT,
|
||||
allowIndexing: import.meta.env.PUBLIC_ALLOW_INDEXING,
|
||||
route: campaignRoute,
|
||||
locale: 'en',
|
||||
});
|
||||
---
|
||||
<section class="section consultation" id="consultation" aria-labelledby="consultation-heading">
|
||||
<div class="container consultation__panel grid-2">
|
||||
@@ -22,7 +40,9 @@ const formPreview = formAPreviewEnabled(import.meta.env.PUBLIC_AIA_FORM_A_PREVIE
|
||||
<p class="lede">{intro}</p>
|
||||
</div>
|
||||
<div class="form-wrap">
|
||||
{formPreview ? <FormAPreview lang="en" variant="compact" submitText={buttonText} /> : <div id="lead-form" role="group" aria-labelledby="campaign-form-heading" data-forms-mode="off">
|
||||
{formAIframe.enabled
|
||||
? <JotformIframe config={formAIframe} heading="Start with your contact details" description={iframeDescription} variant="embedded" />
|
||||
: 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>
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
---
|
||||
import JotformIframe from '../JotformIframe.astro';
|
||||
import { resolveFormAIframe } from '../../utils/form-integration';
|
||||
|
||||
const formAIframe = resolveFormAIframe({
|
||||
rawMode: import.meta.env.PUBLIC_AIA_FORMS_MODE,
|
||||
liveApproved: import.meta.env.PUBLIC_AIA_FORMS_LIVE_APPROVED,
|
||||
isolatedTestApproved: import.meta.env.PUBLIC_AIA_FORMS_ISOLATED_TEST_APPROVED,
|
||||
environmentRole: import.meta.env.PUBLIC_AIA_FORMS_ENVIRONMENT,
|
||||
allowIndexing: import.meta.env.PUBLIC_ALLOW_INDEXING,
|
||||
route: '/lp/early-intervention',
|
||||
locale: 'en',
|
||||
});
|
||||
---
|
||||
|
||||
<!-- Source: public /lp/early-intervention, 2026-10-20 (cache-qualified). -->
|
||||
<!-- TODO: Approved Jotform integration and spam protection require separate authorization. -->
|
||||
<!-- TODO: Production Jotform activation and spam protection require separate authorization. -->
|
||||
<div class="qr-landing">
|
||||
<section class="qr-hero" aria-labelledby="qr-hero-heading">
|
||||
<div class="qr-hero-copy">
|
||||
@@ -23,17 +38,19 @@
|
||||
</div>
|
||||
</section>
|
||||
<div class="qr-process-band">
|
||||
<section class="qr-section qr-contact-section" aria-label="Call AIA about early intervention"><div id="consultation" class="lead-form" aria-labelledby="consultation-heading" role="group" data-forms-mode="off">
|
||||
<section class="qr-section qr-contact-section" aria-label="Contact AIA about early intervention"><div id="consultation" class="lead-form" aria-labelledby="consultation-heading" role="group" data-forms-mode={formAIframe.enabled ? 'isolated-test' : 'off'}>
|
||||
|
||||
<div class="form-heading">
|
||||
<h2 id="consultation-heading">Call AIA about early intervention</h2>
|
||||
<h2 id="consultation-heading">Contact AIA about early intervention</h2>
|
||||
<p>Speak with an AIA team member about your questions and possible next steps.</p>
|
||||
</div>
|
||||
|
||||
{formAIframe.enabled ? <JotformIframe config={formAIframe} heading="Start with your contact details" variant="embedded" /> : <>
|
||||
<div class="field"><label for="parent-name">Parent or caregiver name</label><input id="parent-name" autocomplete="name" required="" type="text" disabled=""></div>
|
||||
<div class="field"><label for="phone">Phone number</label><input id="phone" autocomplete="tel" required="" type="tel" disabled=""></div>
|
||||
<div class="field"><label for="email">Email address<span class="field-optional"> (optional)</span></label><input id="email" autocomplete="email" type="email" disabled=""></div>
|
||||
<button class="button button-primary" type="button" disabled="">Request a consultation</button></div>
|
||||
<button class="button button-primary" type="button" disabled="">Request a consultation</button></>}
|
||||
</div>
|
||||
<aside class="qr-call-card">
|
||||
<p class="eyebrow">Prefer to talk?</p>
|
||||
<h2>Call AIA</h2>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
import ContactForm from '../ContactForm.astro';
|
||||
import FormAPreview from '../FormAPreview.astro';
|
||||
import JotformIframe from '../JotformIframe.astro';
|
||||
import { formMessages } from '../../utils/form-messages';
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormMode } from '../../utils/form-integration';
|
||||
import { formAPreviewEnabled, formModeForRuntime, resolveFormAIframe, resolveFormMode } from '../../utils/form-integration';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -13,6 +14,16 @@ 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));
|
||||
const route = lang === 'es' ? '/es/contact' : '/contact';
|
||||
const formAIframe = resolveFormAIframe({
|
||||
rawMode: import.meta.env.PUBLIC_AIA_FORMS_MODE,
|
||||
liveApproved: import.meta.env.PUBLIC_AIA_FORMS_LIVE_APPROVED,
|
||||
isolatedTestApproved: import.meta.env.PUBLIC_AIA_FORMS_ISOLATED_TEST_APPROVED,
|
||||
environmentRole: import.meta.env.PUBLIC_AIA_FORMS_ENVIRONMENT,
|
||||
allowIndexing: import.meta.env.PUBLIC_ALLOW_INDEXING,
|
||||
route,
|
||||
locale: lang,
|
||||
});
|
||||
---
|
||||
|
||||
<section class="contact-page">
|
||||
@@ -20,7 +31,11 @@ const formPreview = formAPreviewEnabled(import.meta.env.PUBLIC_AIA_FORM_A_PREVIE
|
||||
<div class="contact-page__copy">
|
||||
<slot />
|
||||
</div>
|
||||
{formPreview ? <FormAPreview lang={lang as 'en' | 'es'} variant="full" submitText={lang === 'es' ? 'Enviar' : 'Submit'} /> : <ContactForm title={title} lang={lang} />}
|
||||
{formAIframe.enabled
|
||||
? <JotformIframe config={formAIframe} heading={title} />
|
||||
: formPreview
|
||||
? <FormAPreview lang={lang as 'en' | 'es'} variant="full" submitText={lang === 'es' ? 'Enviar' : 'Submit'} />
|
||||
: <ContactForm title={title} lang={lang} />}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,26 +1,156 @@
|
||||
export type FormMode = 'off' | 'mock' | 'live';
|
||||
export type FormMode = 'off' | 'mock' | 'isolated-test' | 'live';
|
||||
export type LogicalForm = 'general' | 'service' | 'referral';
|
||||
export type MockFormResult = 'success' | 'failed' | 'unavailable';
|
||||
|
||||
export interface FormModeConfig {
|
||||
mode: FormMode;
|
||||
reason: 'default-off' | 'mock' | 'live-approved' | 'invalid-mode' | 'live-not-approved';
|
||||
reason: 'default-off' | 'mock' | 'isolated-test' | 'live-approved' | 'invalid-mode' | 'live-not-approved';
|
||||
}
|
||||
|
||||
export function resolveFormMode(rawMode: string | undefined, liveApproved: string | undefined): FormModeConfig {
|
||||
const mode = rawMode?.trim().toLowerCase();
|
||||
if (!mode) return { mode: 'off', reason: 'default-off' };
|
||||
if (mode === 'mock') return { mode: 'mock', reason: 'mock' };
|
||||
if (mode === 'isolated-test') return { mode: 'isolated-test', reason: 'isolated-test' };
|
||||
if (mode !== 'live') return { mode: 'off', reason: 'invalid-mode' };
|
||||
if (liveApproved?.trim().toLowerCase() !== 'true') return { mode: 'off', reason: 'live-not-approved' };
|
||||
return { mode: 'live', reason: 'live-approved' };
|
||||
}
|
||||
|
||||
export function formModeForRuntime(config: FormModeConfig): 'off' | 'mock' {
|
||||
// A live adapter and approved form IDs do not exist yet. Keep live fail-closed.
|
||||
// The isolated iframe adapter has its own fixed Form A route allowlist.
|
||||
// Keep all other lead-form surfaces and every live setting fail-closed.
|
||||
return config.mode === 'mock' ? 'mock' : 'off';
|
||||
}
|
||||
|
||||
export type FormAIframeReason =
|
||||
| 'enabled'
|
||||
| 'mode-not-isolated-test'
|
||||
| 'isolated-test-not-approved'
|
||||
| 'invalid-environment-role'
|
||||
| 'indexing-enabled'
|
||||
| 'route-not-authorized'
|
||||
| 'locale-not-authorized';
|
||||
|
||||
export interface FormAIframeConfig {
|
||||
enabled: boolean;
|
||||
reason: FormAIframeReason;
|
||||
route: FormAIframeRoute | string;
|
||||
locale: 'en' | 'es' | string;
|
||||
context?: FormAIframeContext;
|
||||
url?: string;
|
||||
title?: string;
|
||||
fallbackText?: string;
|
||||
fallbackLinkText?: string;
|
||||
}
|
||||
|
||||
export interface ResolveFormAIframeOptions {
|
||||
rawMode: string | undefined;
|
||||
liveApproved: string | undefined;
|
||||
isolatedTestApproved: string | undefined;
|
||||
environmentRole: string | undefined;
|
||||
allowIndexing: string | undefined;
|
||||
route: string;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The owner-approved Form A identifier is deliberately only made into an
|
||||
* iframe URL after every isolated-test gate has passed. It is not a
|
||||
* production setting and it is not enabled by an API URL or a live mode.
|
||||
*/
|
||||
export const formAIframeOrigin = 'https://form.jotform.com' as const;
|
||||
export const formAIframeId = '262946078314059' as const;
|
||||
const isolatedTestEnvironmentRole = 'isolated-test';
|
||||
|
||||
export type FormAIframeContext = 'contact' | 'landing';
|
||||
export type FormAIframeRoute =
|
||||
| '/contact'
|
||||
| '/es/contact'
|
||||
| '/lp/aba-therapy'
|
||||
| '/lp/aba-insurance'
|
||||
| '/lp/aba-ahcccs'
|
||||
| '/lp/aba-private-pay'
|
||||
| '/lp/early-intervention';
|
||||
|
||||
interface FormAIframePlacement {
|
||||
route: FormAIframeRoute;
|
||||
locale: 'en' | 'es';
|
||||
context: FormAIframeContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the complete fixed allowlist for the isolated Form A adapter.
|
||||
* Campaign placements are an owner-approved override of their earlier
|
||||
* logical Form B mapping. The shared compact landing presentation uses the
|
||||
* same fixed `landing` context; no route may supply its own context or URL.
|
||||
*/
|
||||
export const formAIframePlacements: ReadonlyArray<FormAIframePlacement> = [
|
||||
{ route: '/contact', locale: 'en', context: 'contact' },
|
||||
{ route: '/es/contact', locale: 'es', context: 'contact' },
|
||||
{ route: '/lp/aba-therapy', locale: 'en', context: 'landing' },
|
||||
{ route: '/lp/aba-insurance', locale: 'en', context: 'landing' },
|
||||
{ route: '/lp/aba-ahcccs', locale: 'en', context: 'landing' },
|
||||
{ route: '/lp/aba-private-pay', locale: 'en', context: 'landing' },
|
||||
{ route: '/lp/early-intervention', locale: 'en', context: 'landing' },
|
||||
];
|
||||
|
||||
const isTrue = (value: string | undefined) => value?.trim().toLowerCase() === 'true';
|
||||
|
||||
function formAIframeCopy(locale: 'en' | 'es') {
|
||||
return locale === 'es'
|
||||
? {
|
||||
title: 'Formulario de consultas generales de AIA',
|
||||
fallbackText: 'Si el formulario integrado no se carga,',
|
||||
fallbackLinkText: 'abra el formulario en una pestaña nueva.',
|
||||
}
|
||||
: {
|
||||
title: 'AIA general enquiries form',
|
||||
fallbackText: 'If the embedded form does not load,',
|
||||
fallbackLinkText: 'open the form in a new tab.',
|
||||
};
|
||||
}
|
||||
|
||||
/** Builds a fixed, allowlisted URL; it accepts neither visitor data nor URL parameters. */
|
||||
export function buildFormAIframeUrl(locale: 'en' | 'es', context: FormAIframeContext): string {
|
||||
const url = new URL(`/${formAIframeId}`, formAIframeOrigin);
|
||||
url.searchParams.set('formContext', context);
|
||||
url.searchParams.set('language', locale);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the real Form A iframe only in an explicitly approved isolated
|
||||
* test build. `live` is intentionally not a valid route to this adapter.
|
||||
*/
|
||||
export function resolveFormAIframe(options: ResolveFormAIframeOptions): FormAIframeConfig {
|
||||
const disabled = (reason: Exclude<FormAIframeReason, 'enabled'>): FormAIframeConfig => ({
|
||||
enabled: false,
|
||||
reason,
|
||||
route: options.route,
|
||||
locale: options.locale,
|
||||
});
|
||||
const config = resolveFormMode(options.rawMode, options.liveApproved);
|
||||
if (config.mode !== 'isolated-test') return disabled('mode-not-isolated-test');
|
||||
if (!isTrue(options.isolatedTestApproved)) return disabled('isolated-test-not-approved');
|
||||
if (options.environmentRole?.trim().toLowerCase() !== isolatedTestEnvironmentRole) return disabled('invalid-environment-role');
|
||||
if (isTrue(options.allowIndexing)) return disabled('indexing-enabled');
|
||||
|
||||
const placement = formAIframePlacements.find((candidate) => candidate.route === options.route);
|
||||
if (!placement) return disabled('route-not-authorized');
|
||||
if (options.locale !== placement.locale) return disabled('locale-not-authorized');
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
reason: 'enabled',
|
||||
route: options.route,
|
||||
locale: placement.locale,
|
||||
context: placement.context,
|
||||
url: buildFormAIframeUrl(placement.locale, placement.context),
|
||||
...formAIframeCopy(placement.locale),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The Form A meeting preview is deliberately narrower than the general mock
|
||||
* mode: it is available only for an explicitly requested, non-indexing mock
|
||||
@@ -55,11 +185,11 @@ export const formRouteMapping: ReadonlyArray<{
|
||||
{ route: '/aba-therapy', logicalForm: 'service', kind: 'native-instance' },
|
||||
{ route: '/autism-evaluations', logicalForm: 'service', kind: 'native-instance' },
|
||||
{ route: '/learner-social-club', logicalForm: 'service', kind: 'native-instance' },
|
||||
{ route: '/lp/aba-therapy', logicalForm: 'service', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-insurance', logicalForm: 'service', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-ahcccs', logicalForm: 'service', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-private-pay', logicalForm: 'service', kind: 'landing-journey' },
|
||||
{ route: '/lp/early-intervention', logicalForm: 'service', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-therapy', logicalForm: 'general', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-insurance', logicalForm: 'general', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-ahcccs', logicalForm: 'general', kind: 'landing-journey' },
|
||||
{ route: '/lp/aba-private-pay', logicalForm: 'general', kind: 'landing-journey' },
|
||||
{ route: '/lp/early-intervention', logicalForm: 'general', kind: 'landing-journey' },
|
||||
];
|
||||
|
||||
export function logicalFormForRoute(route: string): LogicalForm | undefined {
|
||||
|
||||
Reference in new issue
Block a user