From e3cecf9866696eaddc44768451b24a221f0d521c Mon Sep 17 00:00:00 2001 From: abrichr Date: Sun, 26 Jul 2026 15:28:52 -0400 Subject: [PATCH] feat(site): comparison pages, partners program, remote-mode diagram, intake polish (Sections 18+21) - /compare/ pages for UiPath, Power Automate, computer-use agents, record-and-replay tools, browser-agent platforms, and hand-rolled scripts, driven by data/comparisons.js under enforced honesty rules: credit real competitor strengths, differentiate only on independent effect verification, explicit outcomes, the external zero-install remote lane, customer-controlled data, deterministic model-free healthy runs, the MIT runtime, and qualification evidence; never on commoditized recording/visual-targeting/Citrix/self-healing. - /partners page with four tracks (vertical software/OEM, RCM+BPO, integration/services, MSP/deployment), each stating model and qualification/support/packs boundaries, plus a dedicated Netlify partner-inquiry intake with a track selector registered in public/form.html. Status-honest: application-based, no self-serve portal. - Remote execution modes figure on /how-it-works: in-session vs external black-box, labeled per current qualification status (deterministic stand-in qualified; real ICA/HDX per customer). - Nav/footer: Partners & OEM in Solutions dropdown and footer Company column; Contribute workflows in footer Connect column. - Qualification intake: required privacy-consent checkbox linking the Privacy Notice, captured leadSegment field, method=POST fallback, and node + Cypress tests asserting no lead field ever appears in a URL. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM --- components/Footer.js | 6 + components/NavHeader.js | 1 + components/PartnerInquiryForm.js | 251 ++++++++++++++++++++++++ components/RemoteModesFigure.js | 184 +++++++++++++++++ components/WorkflowQualificationForm.js | 36 +++- cypress/e2e/lead-privacy.cy.js | 99 ++++++++++ data/comparisons.js | 199 +++++++++++++++++++ pages/compare.js | 36 ++++ pages/compare/[slug].js | 235 ++++++++++++++++++++++ pages/how-it-works.js | 5 + pages/partners.js | 202 +++++++++++++++++++ public/form.html | 15 ++ public/llms.txt | 7 + public/sitemap.xml | 42 ++++ tests/comparisonPages.test.js | 150 ++++++++++++++ tests/leadPrivacy.test.js | 110 +++++++++++ tests/partnersProgram.test.js | 98 +++++++++ 17 files changed, 1674 insertions(+), 2 deletions(-) create mode 100644 components/PartnerInquiryForm.js create mode 100644 components/RemoteModesFigure.js create mode 100644 cypress/e2e/lead-privacy.cy.js create mode 100644 data/comparisons.js create mode 100644 pages/compare/[slug].js create mode 100644 pages/partners.js create mode 100644 tests/comparisonPages.test.js create mode 100644 tests/leadPrivacy.test.js create mode 100644 tests/partnersProgram.test.js diff --git a/components/Footer.js b/components/Footer.js index dffa64c5..01f39207 100644 --- a/components/Footer.js +++ b/components/Footer.js @@ -171,6 +171,7 @@ const CONNECT_COLUMN = [ BLOG_LINK, byLabel('Discord'), { label: 'GitHub', href: OPENADAPT_REPOSITORY_URL }, + { label: 'Contribute workflows', href: '/contribute' }, ] export default function Footer({ @@ -360,6 +361,11 @@ export default function Footer({
  • About
  • +
  • + + Partners & OEM + +
  • Hosted dashboard diff --git a/components/NavHeader.js b/components/NavHeader.js index bcdf7524..6d6ad6ed 100644 --- a/components/NavHeader.js +++ b/components/NavHeader.js @@ -26,6 +26,7 @@ const SOLUTIONS_LINKS = [ { label: 'Healthcare', href: '/solutions/healthcare' }, { label: 'Lending', href: '/solutions/lending' }, { label: 'Insurance', href: '/solutions/insurance' }, + { label: 'Partners & OEM', href: '/partners' }, ] const PRODUCT_LINKS = [ diff --git a/components/PartnerInquiryForm.js b/components/PartnerInquiryForm.js new file mode 100644 index 00000000..ea92b2d3 --- /dev/null +++ b/components/PartnerInquiryForm.js @@ -0,0 +1,251 @@ +import { useState } from 'react' +import Link from 'next/link' + +import { trackEmailCapture } from 'utils/conversion' + +// Dedicated intake for the partner program. +// +// Posts to the durable Netlify Forms lead path (`/form.html`) under its own +// form name, `partner-inquiry`, so partner leads land in their own Netlify +// submissions bucket instead of the generic `contact` queue. The hidden form +// definition lives in public/form.html; Netlify's build-time bots read it +// from there. The `track` selector mirrors the partner tracks on /partners. +// Lead data always travels in the POST body, never in a URL. + +export const PARTNER_TRACKS = [ + { value: 'vertical_oem', label: 'Vertical software / OEM' }, + { value: 'rcm_bpo', label: 'RCM or BPO operator' }, + { value: 'integration_services', label: 'Integration / services' }, + { value: 'msp_deployment', label: 'MSP / deployment' }, +] + +const INITIAL_FORM = { + name: '', + email: '', + company: '', + role: '', + track: '', + systems: '', + message: '', + botField: '', +} + +const fieldClass = + 'rounded-lg border border-ink/30 bg-panel px-3 py-2 text-ink placeholder-ink-3/60 focus:border-accent focus:outline-none' + +export default function PartnerInquiryForm() { + const [form, setForm] = useState(INITIAL_FORM) + const [isSubmitting, setIsSubmitting] = useState(false) + const [isSubmitted, setIsSubmitted] = useState(false) + const [error, setError] = useState('') + + const handleChange = (event) => { + const { name, value } = event.target + setForm((prev) => ({ ...prev, [name]: value })) + } + + const handleSubmit = async (event) => { + event.preventDefault() + setError('') + setIsSubmitting(true) + + try { + const formData = new URLSearchParams() + formData.set('form-name', 'partner-inquiry') + formData.set('name', form.name) + formData.set('email', form.email) + formData.set('company', form.company) + formData.set('role', form.role) + formData.set('track', form.track) + formData.set('systems', form.systems) + formData.set('message', form.message) + formData.set('bot-field', form.botField) + + const response = await fetch('/form.html', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: formData.toString(), + }) + + if (!response.ok) { + throw new Error( + `Form submission failed with status ${response.status}` + ) + } + + // Qualified-lead conversion: partner interest captured. Carries + // first-touch utm_* attribution; never any form contents. + trackEmailCapture({ location: 'partner_inquiry_form' }) + setIsSubmitted(true) + } catch (submitError) { + console.error(submitError) + setError( + 'Submission failed. Please email hello@openadapt.ai directly.' + ) + } finally { + setIsSubmitting(false) + } + } + + return ( +
    + {!isSubmitted ? ( +
    + +

    + +

    + +
    + + + + +
    + + + + + +