create-ailk 0.6.1 → 0.8.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/README.md +87 -14
- package/component-catalog.json +735 -95
- package/component-catalog.md +13 -7
- package/dist/add-module-core.d.ts +22 -0
- package/dist/add-module-core.js +50 -0
- package/dist/add-module.d.ts +89 -0
- package/dist/add-module.js +334 -0
- package/dist/category-profile.d.ts +126 -0
- package/dist/category-profile.js +262 -0
- package/dist/cli.js +89 -29
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -1
- package/dist/lib/extract-module-bundle.d.ts +56 -1
- package/dist/lib/extract-module-bundle.js +170 -14
- package/dist/lib/license-key-files.d.ts +15 -0
- package/dist/lib/license-key-files.js +102 -0
- package/dist/lib/module-license-gate.d.ts +79 -0
- package/dist/lib/module-license-gate.js +160 -12
- package/dist/lib/module-tier.d.ts +45 -16
- package/dist/lib/module-tier.js +76 -0
- package/dist/lib/pro-callout.d.ts +16 -10
- package/dist/lib/pro-callout.js +34 -3
- package/dist/license-set.d.ts +34 -0
- package/dist/license-set.js +194 -0
- package/dist/module-architecture.d.ts +20 -0
- package/dist/module-architecture.js +77 -19
- package/dist/modules/commerce/0.6.1.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
- package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
- package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
- package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
- package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
- package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
- package/dist/modules/workspace/0.6.1.tar.gz +0 -0
- package/dist/parse-args.d.ts +85 -10
- package/dist/parse-args.js +227 -15
- package/dist/programmatic.d.ts +81 -5
- package/dist/programmatic.js +291 -32
- package/dist/site-chrome.d.ts +18 -7
- package/dist/site-chrome.js +32 -14
- package/dist/site-roster.d.ts +127 -0
- package/dist/site-roster.js +499 -0
- package/dist/starter-grid.d.ts +216 -0
- package/dist/starter-grid.js +449 -0
- package/dist/surfaces.d.ts +20 -7
- package/dist/surfaces.js +204 -24
- package/dist/sync-routes.d.ts +9 -0
- package/dist/sync-routes.js +2 -2
- package/dist/template-manifest.d.ts +94 -19
- package/dist/template-manifest.js +170 -289
- package/dist/template-names.d.ts +14 -12
- package/dist/template-names.js +13 -17
- package/package.json +6 -4
- package/site-templates/_base/content/en/BookCall/book-call.mdx +52 -0
- package/site-templates/_base/content/en/Feature/feature-1.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-2.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-3.mdx +69 -0
- package/site-templates/_base/content/en/FeaturesList/features.mdx +91 -0
- package/site-templates/_base/content/en/GlossaryList/glossary.mdx +65 -0
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +67 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-four.mdx +105 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-one.mdx +34 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-three.mdx +94 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-two.mdx +45 -0
- package/site-templates/_base/content/en/PortfolioList/portfolio.mdx +169 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +71 -62
- package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +17 -3
- package/site-templates/_base/content/en/TermsPage/terms.mdx +2 -3
- package/site-templates/_base/content/en/UseCase/persona-1.mdx +69 -0
- package/site-templates/_base/content/en/UseCase/persona-2.mdx +69 -0
- package/site-templates/_base/content/en/UseCase/persona-3.mdx +69 -0
- package/site-templates/designs/minimalism/DESIGN.md +162 -0
- package/site-templates/designs/standard/DESIGN.md +167 -0
- package/site-templates/marketing-site/DESIGN.md +59 -40
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +376 -57
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +230 -90
- package/site-templates/marketing-site/ailk-template.json +1 -0
- package/site-templates/marketing-site-free/DESIGN.md +59 -40
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +323 -63
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +323 -113
- package/site-templates/marketing-site-free/ailk-template.json +3 -2
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +199 -0
- package/site-templates/minimalism-site/DESIGN.md +54 -40
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +246 -19
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +173 -51
- package/site-templates/minimalism-site/ailk-template.json +3 -2
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +3 -37
- package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/saas-product/DESIGN.md +59 -40
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +369 -0
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +302 -0
- package/site-templates/shapes/local-business-service/ailk-template.json +10 -0
- package/site-templates/{simple-service → shapes/local-business-service}/content/en/AboutPage/about.mdx +4 -4
- package/site-templates/shapes/local-business-service/content/en/HomePage/home.mdx +70 -0
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/local-business-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/shapes/local-business-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/ServicesList/services.mdx +61 -0
- package/site-templates/shapes/local-business-service/provenance.json +4 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +362 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +300 -0
- package/site-templates/shapes/organization-service/ailk-template.json +10 -0
- package/site-templates/shapes/organization-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/organization-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/shapes/organization-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/shapes/organization-service/provenance.json +4 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +369 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +299 -0
- package/site-templates/shapes/person-service/ailk-template.json +10 -0
- package/site-templates/shapes/person-service/content/_site.mdx +85 -0
- package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +39 -0
- package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +97 -0
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +169 -0
- package/site-templates/shapes/person-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/{simple-service → shapes/person-service}/content/en/ServicesList/services.mdx +16 -16
- package/site-templates/shapes/person-service/provenance.json +4 -0
- package/templates/.claude/settings.json +15 -0
- package/templates/.claude/skills/README.md +10 -9
- package/templates/.claude/skills/{scaffold-auth → add-auth}/SKILL.md +7 -7
- package/templates/.claude/skills/{scaffold-auth → add-auth}/references/env-vars.md +2 -2
- package/templates/.claude/skills/{scaffold-auth → add-auth}/references/oauth-app-setup.md +3 -3
- package/templates/.claude/skills/{scaffold-page → add-page}/SKILL.md +76 -62
- package/templates/.claude/skills/add-page/references/page-types.md +115 -0
- package/templates/.claude/skills/{scaffold-page → add-page}/templates/content.mdx.tmpl +4 -4
- package/templates/.claude/skills/{scaffold-page → add-page}/templates/route.tsx.tmpl +1 -1
- package/templates/.claude/skills/{audit-site → check-site}/SKILL.md +16 -17
- package/templates/.claude/skills/{audit-site → check-site}/references/common-errors.md +3 -3
- package/templates/.claude/skills/{audit-site → check-site}/references/findings-glossary.md +16 -14
- package/templates/.claude/skills/{audit-site → check-site}/references/severity-levels.md +1 -1
- package/templates/.claude/skills/fill-page/SKILL.md +180 -0
- package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +80 -0
- package/templates/.claude/skills/fill-page/references/section-sources.md +41 -0
- package/templates/.claude/skills/setup-database/SKILL.md +156 -0
- package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +154 -0
- package/templates/.claude/skills/{provision-config → setup-deploy}/SKILL.md +73 -41
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/env-vars-template.md +21 -30
- package/templates/.claude/skills/setup-deploy/references/runbook-template.md +204 -0
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/vercel-json-template.md +1 -1
- package/templates/.claude/skills/upgrade-plan/SKILL.md +117 -0
- package/templates/.claude/skills/upgrade-plan/references/options.md +24 -0
- package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +527 -0
- package/templates/.github/workflows/ci.yml +5 -0
- package/templates/CLAUDE.md +8 -2
- package/templates/DESIGN.md +56 -44
- package/templates/README.md +116 -46
- package/templates/apps/api/.env.example +2 -1
- package/templates/apps/api/CLAUDE.md +3 -2
- package/templates/apps/api/package.json +1 -1
- package/templates/apps/api/src/config/__tests__/index.test.ts +146 -10
- package/templates/apps/api/src/config/index.ts +46 -14
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +30 -0
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +96 -0
- package/templates/apps/api/src/lib/lead-capture-store.ts +12 -0
- package/templates/apps/api/src/lib/lead-delivery.ts +52 -0
- package/templates/apps/api/src/lib/license-key-store.ts +45 -0
- package/templates/apps/api/src/lib/site.ts +5 -3
- package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +97 -0
- package/templates/apps/api/src/middleware/auth.ts +9 -0
- package/templates/apps/api/src/openapi/spec.ts +63 -12
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +127 -5
- package/templates/apps/api/src/routes/content/get-page.ts +86 -2
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +88 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +100 -0
- package/templates/apps/api/src/routes/leads/index.ts +46 -1
- package/templates/apps/api/src/server.ts +13 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +194 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +124 -0
- package/templates/apps/api/src/services/lead-routing.ts +140 -30
- package/templates/apps/api/src/services/leads-inbox.ts +48 -11
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +75 -1
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +57 -9
- package/templates/apps/mcp/__tests__/parity.test.ts +5 -4
- package/templates/apps/mcp/package.json +2 -1
- package/templates/apps/mcp/src/index.ts +7 -0
- package/templates/apps/mcp/src/lib/audit-single-page.ts +36 -10
- package/templates/apps/mcp/src/lib/license-key-store.ts +45 -0
- package/templates/apps/mcp/src/tools/create_project.ts +21 -6
- package/templates/apps/web/__tests__/setup.ts +18 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +10 -2
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +2 -2
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +10 -4
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +22 -14
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +26 -18
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +12 -4
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +35 -4
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +49 -9
- package/templates/apps/web/app/[locale]/book-a-call/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +67 -29
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +94 -11
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +32 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +129 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +228 -86
- package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +50 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +73 -20
- package/templates/apps/web/app/[locale]/contact/page.tsx +115 -7
- package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +64 -0
- package/templates/apps/web/app/[locale]/features/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/glossary/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/how-to/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/layout.tsx +146 -52
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +75 -0
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +163 -0
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +66 -0
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +142 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +59 -0
- package/templates/apps/web/app/[locale]/portfolio/page.tsx +49 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/{cancellation → purchase-policy}/page.tsx +15 -10
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +14 -1
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/software/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/use-cases/[slug]/page.tsx +63 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +15 -2
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/whitepapers/page.tsx +25 -15
- package/templates/apps/web/app/error-fallback.tsx +17 -45
- package/templates/apps/web/app/not-found.tsx +14 -38
- package/templates/apps/web/app/og/route.tsx +6 -0
- package/templates/apps/web/eslint.config.mjs +3 -0
- package/templates/apps/web/instrumentation.ts +14 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +38 -0
- package/templates/apps/web/lib/license-key-store.ts +52 -0
- package/templates/apps/web/lib/nav-config.ts +82 -18
- package/templates/apps/web/lib/prisma-for-auth.ts +9 -6
- package/templates/apps/web/lib/site-brand.tsx +1 -1
- package/templates/apps/web/lib/site-chrome.ts +3 -3
- package/templates/apps/web/lib/site-identity.ts +148 -0
- package/templates/apps/web/lib/site-theme.ts +11 -0
- package/templates/apps/web/messages/en.json +78 -37
- package/templates/apps/web/public/android-chrome-192x192.png +0 -0
- package/templates/apps/web/public/android-chrome-512x512.png +0 -0
- package/templates/apps/web/public/apple-touch-icon.png +0 -0
- package/templates/apps/web/public/favicon-16x16.png +0 -0
- package/templates/apps/web/public/favicon-32x32.png +0 -0
- package/templates/apps/web/public/favicon.ico +0 -0
- package/templates/apps/web/public/favicon.svg +3 -3
- package/templates/apps/web/public/logomark.svg +6 -3
- package/templates/apps/web/public/logos/ai-launch-kit-light.svg +4 -0
- package/templates/apps/web/public/logos/ai-launch-kit.svg +4 -0
- package/templates/apps/web/public/logos/atomic-agent-light.svg +4 -0
- package/templates/apps/web/public/logos/atomic-agent.svg +4 -0
- package/templates/apps/web/public/logos/salessmyth-light.svg +4 -0
- package/templates/apps/web/public/logos/salessmyth.svg +4 -0
- package/templates/apps/web/public/site.webmanifest +4 -4
- package/templates/apps/web/public/wordmark-light.svg +4 -0
- package/templates/apps/web/public/wordmark.svg +3 -3
- package/templates/apps/web/security-headers.config.ts +5 -1
- package/templates/content/_site.mdx +25 -0
- package/templates/content/en/AboutPage/about.mdx +150 -144
- package/templates/content/en/BlogList/blog.mdx +126 -0
- package/templates/content/en/ContactPage/contact.mdx +21 -92
- package/templates/content/en/FAQPage/faq.mdx +86 -57
- package/templates/content/en/HomePage/home.mdx +268 -217
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +181 -0
- package/templates/content/en/UseCasesList/use-cases.mdx +170 -0
- package/templates/database/CHANGELOG.md +14 -0
- package/templates/database/package.json +1 -1
- package/templates/package.json +3 -1
- package/templates/tools/lint-architecture.ts +315 -0
- package/templates/tools/tsconfig.json +14 -0
- package/versions.json +33 -33
- package/site-templates/simple-service/DESIGN.md +0 -148
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +0 -240
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -278
- package/site-templates/simple-service/ailk-template.json +0 -10
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -67
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +0 -56
- package/site-templates/simple-service/content/en/HomePage/home.mdx +0 -85
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +0 -40
- package/site-templates/simple-service/provenance.json +0 -4
- package/templates/.claude/skills/add-schema/SKILL.md +0 -277
- package/templates/.claude/skills/add-schema/references/conflict-handling.md +0 -200
- package/templates/.claude/skills/add-schema/references/file-finding.md +0 -123
- package/templates/.claude/skills/add-schema/references/schema-types.md +0 -234
- package/templates/.claude/skills/provision-config/references/runbook-template.md +0 -248
- package/templates/.claude/skills/scaffold-page/references/page-types.md +0 -169
- package/templates/.claude/skills/suggest-site-pages/SKILL.md +0 -242
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +0 -287
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +0 -150
- package/templates/apps/web/app/[locale]/returns/page.tsx +0 -44
- package/templates/apps/web/app/[locale]/shipping/page.tsx +0 -44
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +0 -108
- package/templates/content/en/LandingPage/blog.mdx +0 -83
- package/templates/content/en/LandingPage/features.mdx +0 -103
- package/templates/content/en/LandingPage/how-it-works.mdx +0 -143
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +0 -108
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +0 -108
|
@@ -50,7 +50,7 @@ describe("careers/page — snapshot regression", () => {
|
|
|
50
50
|
});
|
|
51
51
|
const { container } = render(element);
|
|
52
52
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
53
|
-
`"<main><h1 class="text-heading-1
|
|
53
|
+
`"<main id="main"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Careers</h1><p data-testid="section-empty-state">Career listings coming soon.</p></main>"`,
|
|
54
54
|
);
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -71,7 +71,7 @@ describe("careers/page — snapshot regression", () => {
|
|
|
71
71
|
});
|
|
72
72
|
const { container } = render(element);
|
|
73
73
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
74
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Careers at AILK","description":"Join the team building the AX-first platform.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"JobPosting"}]}}</script><main><h1>Careers at AILK</h1><p data-testid="careers-description">Join the team building the AX-first platform.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
74
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Careers at AILK","description":"Join the team building the AX-first platform.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"JobPosting"}]}}</script><main id="main"><h1>Careers at AILK</h1><p data-testid="careers-description">Join the team building the AX-first platform.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
75
75
|
);
|
|
76
76
|
});
|
|
77
77
|
});
|
|
@@ -96,7 +96,7 @@ describe("careers/[slug]/page — snapshot regression", () => {
|
|
|
96
96
|
});
|
|
97
97
|
const { container } = render(element);
|
|
98
98
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
99
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Senior Engineer","description":"Build the AILK platform.","mainEntity":{"@context":"https://schema.org","@type":"JobPosting","title":"Senior Engineer","description":"Build the AILK platform."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Careers","item":"http://localhost:3000/en/careers"},{"@type":"ListItem","position":3,"name":"Senior Engineer","item":"http://localhost:3000/en/careers/senior-engineer"}]}]}</script><main><h1>Senior Engineer</h1><p data-testid="senior-engineer-description">Build the AILK platform.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
99
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Senior Engineer","description":"Build the AILK platform.","mainEntity":{"@context":"https://schema.org","@type":"JobPosting","title":"Senior Engineer","description":"Build the AILK platform."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Careers","item":"http://localhost:3000/en/careers"},{"@type":"ListItem","position":3,"name":"Senior Engineer","item":"http://localhost:3000/en/careers/senior-engineer"}]}]}</script><main id="main"><h1>Senior Engineer</h1><p data-testid="senior-engineer-description">Build the AILK platform.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
100
100
|
);
|
|
101
101
|
});
|
|
102
102
|
|
package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx
CHANGED
|
@@ -46,7 +46,7 @@ describe("case-studies/page — snapshot regression", () => {
|
|
|
46
46
|
});
|
|
47
47
|
const { container } = render(element);
|
|
48
48
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
49
|
-
`"<main><h1 class="text-heading-1
|
|
49
|
+
`"<main id="main"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Case Studies</h1><p data-testid="section-empty-state">Case studies listing coming soon.</p></main>"`,
|
|
50
50
|
);
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -67,7 +67,7 @@ describe("case-studies/page — snapshot regression", () => {
|
|
|
67
67
|
});
|
|
68
68
|
const { container } = render(element);
|
|
69
69
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
70
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Case Studies","description":"Real-world accounts of teams using AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Article"}]}}</script><main><h1>Case Studies</h1><p data-testid="case-studies-description">Real-world accounts of teams using AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
70
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Case Studies","description":"Real-world accounts of teams using AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Article"}]}}</script><main id="main"><h1>Case Studies</h1><p data-testid="case-studies-description">Real-world accounts of teams using AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
71
71
|
);
|
|
72
72
|
});
|
|
73
73
|
});
|
|
@@ -92,7 +92,7 @@ describe("case-studies/[slug]/page — snapshot regression", () => {
|
|
|
92
92
|
});
|
|
93
93
|
const { container } = render(element);
|
|
94
94
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
95
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"AILK AEO Rollout","description":"How a team shipped AEO with AI Launch Kit.","mainEntity":{"@context":"https://schema.org","@type":"Article","headline":"AILK AEO Rollout","description":"How a team shipped AEO with AI Launch Kit."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Case Studies","item":"http://localhost:3000/en/case-studies"},{"@type":"ListItem","position":3,"name":"AILK AEO Rollout","item":"http://localhost:3000/en/case-studies/ailk-aeo-rollout"}]}]}</script><main><h1>AILK AEO Rollout</h1><p data-testid="ailk-aeo-rollout-description">How a team shipped AEO with AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
95
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"AILK AEO Rollout","description":"How a team shipped AEO with AI Launch Kit.","mainEntity":{"@context":"https://schema.org","@type":"Article","headline":"AILK AEO Rollout","description":"How a team shipped AEO with AI Launch Kit."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Case Studies","item":"http://localhost:3000/en/case-studies"},{"@type":"ListItem","position":3,"name":"AILK AEO Rollout","item":"http://localhost:3000/en/case-studies/ailk-aeo-rollout"}]}]}</script><main id="main"><h1>AILK AEO Rollout</h1><p data-testid="ailk-aeo-rollout-description">How a team shipped AEO with AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
96
96
|
);
|
|
97
97
|
});
|
|
98
98
|
|
|
@@ -24,8 +24,7 @@ jest.mock("@working-theory/ui", () => ({
|
|
|
24
24
|
}: {
|
|
25
25
|
children: React.ReactNode;
|
|
26
26
|
level: number;
|
|
27
|
-
}): React.ReactElement =>
|
|
28
|
-
React.createElement(`h${level}`, null, children),
|
|
27
|
+
}): React.ReactElement => React.createElement(`h${level}`, null, children),
|
|
29
28
|
SectionRenderer: ({ sections }: { sections: unknown[] }) =>
|
|
30
29
|
React.createElement("div", {
|
|
31
30
|
"data-testid": "section-renderer",
|
|
@@ -45,7 +44,7 @@ describe("ClientsPage — snapshot regression", () => {
|
|
|
45
44
|
});
|
|
46
45
|
const { container } = render(element);
|
|
47
46
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
48
|
-
`"<main><h1>Clients</h1><p data-testid="section-empty-state">Clients listing coming soon.</p></main>"`,
|
|
47
|
+
`"<main id="main"><h1>Clients</h1><p data-testid="section-empty-state">Clients listing coming soon.</p></main>"`,
|
|
49
48
|
);
|
|
50
49
|
});
|
|
51
50
|
|
|
@@ -67,7 +66,7 @@ describe("ClientsPage — snapshot regression", () => {
|
|
|
67
66
|
});
|
|
68
67
|
const { container } = render(element);
|
|
69
68
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
70
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Our Clients","description":"Companies that trust us.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Organization"}]}}</script><main><h1>Our Clients</h1><p data-testid="clients-description">Companies that trust us.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-remote" data-source=""></div></article></div></div></main>"`,
|
|
69
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Our Clients","description":"Companies that trust us.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Organization"}]}}</script><main id="main"><h1>Our Clients</h1><p data-testid="clients-description">Companies that trust us.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-remote" data-source=""></div></article></div></div></main>"`,
|
|
71
70
|
);
|
|
72
71
|
});
|
|
73
72
|
});
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* conversation logic, only:
|
|
9
9
|
*
|
|
10
10
|
* 1. Submission — `onComplete` hands the finished answers to the SAME
|
|
11
|
-
* `captureLeadAction` (`./contact.actions.ts`) the
|
|
12
|
-
* already uses, built as a `FormData` (imperatively, not a real
|
|
11
|
+
* `captureLeadAction` (`./contact.actions.ts`) the shared
|
|
12
|
+
* `LeadCaptureForm` on `/contact` already uses, built as a `FormData` (imperatively, not a real
|
|
13
13
|
* `<form>` submit — React 19 Server Actions are plain async functions).
|
|
14
14
|
* That action posts through the EXISTING `POST /v1/leads/submit` route
|
|
15
15
|
* (`apps/api/src/routes/leads`, consumed via `@working-theory/api-client`,
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* second web surface, not a new one. `contact.actions.ts`'s own
|
|
22
22
|
* doc-comment covers the additive `source` FormData field this file
|
|
23
23
|
* sets to distinguish intake-originated leads.
|
|
24
|
-
* 2. Skip fallback — `onSkip` swaps to the
|
|
24
|
+
* 2. Skip fallback — `onSkip` swaps to the shared `LeadCaptureForm` block
|
|
25
|
+
* (#6730 — the one contact form), bound to the same action,
|
|
25
26
|
* carrying over whatever answers were already captured as its
|
|
26
27
|
* `initialValues` prefill. A failed auto-submit on `onComplete` falls
|
|
27
28
|
* back the same way, so a lead is never silently lost.
|
|
@@ -39,15 +40,20 @@
|
|
|
39
40
|
*/
|
|
40
41
|
|
|
41
42
|
import {
|
|
43
|
+
Container,
|
|
42
44
|
ConversationalIntake,
|
|
45
|
+
type ConversationalIntakeProps,
|
|
46
|
+
InlineMessage,
|
|
47
|
+
LeadCaptureForm,
|
|
48
|
+
localeHref,
|
|
43
49
|
THINKING_DELAY_MAX_MS,
|
|
44
50
|
THINKING_DELAY_MIN_MS,
|
|
45
51
|
} from "@working-theory/ui";
|
|
46
52
|
import { useLocale, useTranslations } from "next-intl";
|
|
47
53
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
48
54
|
|
|
55
|
+
import { contactFormText } from "./contact-form-text";
|
|
49
56
|
import { captureLeadAction } from "./contact.actions";
|
|
50
|
-
import { ContactForm, type ContactFormInitialValues } from "./ContactForm";
|
|
51
57
|
import { recordIntakeTurn } from "./intake-analytics";
|
|
52
58
|
import { buildContactIntakeScript } from "./intake-script";
|
|
53
59
|
|
|
@@ -63,6 +69,18 @@ function pickThinkingDelayMs(): number {
|
|
|
63
69
|
return THINKING_DELAY_MIN_MS + Math.floor(Math.random() * (span + 1));
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Fires `recordIntakeTurn` on a later microtask, swallowing any failure — a
|
|
74
|
+
* synchronous throw from the Server Action reference or a rejected promise
|
|
75
|
+
* alike — so analytics can never stall a turn, the Skip fallback, or the lead
|
|
76
|
+
* submit (#6465).
|
|
77
|
+
*/
|
|
78
|
+
function fireIntakeTurn(input: Parameters<typeof recordIntakeTurn>[0]): void {
|
|
79
|
+
Promise.resolve()
|
|
80
|
+
.then(() => recordIntakeTurn(input))
|
|
81
|
+
.catch(() => {});
|
|
82
|
+
}
|
|
83
|
+
|
|
66
84
|
/** Builds the FormData captureLeadAction expects from the engine's answers. */
|
|
67
85
|
function answersToFormData(answers: Record<string, string>): FormData {
|
|
68
86
|
const formData = new FormData();
|
|
@@ -76,7 +94,17 @@ function answersToFormData(answers: Record<string, string>): FormData {
|
|
|
76
94
|
|
|
77
95
|
type Surface = "intake" | "form";
|
|
78
96
|
|
|
79
|
-
|
|
97
|
+
/** The terminal engine state `ConversationalIntake` hands `onComplete`/`onSkip`. */
|
|
98
|
+
type IntakeState = Parameters<
|
|
99
|
+
NonNullable<ConversationalIntakeProps["onComplete"]>
|
|
100
|
+
>[0];
|
|
101
|
+
|
|
102
|
+
// NO_HAND_IMPLEMENT_OK: operator-directed live Free-template reference pass.
|
|
103
|
+
// `inline` drops the page-level Container so the intake can sit inside the
|
|
104
|
+
// hero's right column (the /contact two-column layout).
|
|
105
|
+
export function ConversationalIntakeWrapper({
|
|
106
|
+
inline = false,
|
|
107
|
+
}: { inline?: boolean } = {}) {
|
|
80
108
|
const t = useTranslations("contact");
|
|
81
109
|
const locale = useLocale();
|
|
82
110
|
const script = useMemo(
|
|
@@ -95,7 +123,7 @@ export function ConversationalIntakeWrapper() {
|
|
|
95
123
|
);
|
|
96
124
|
|
|
97
125
|
const [surface, setSurface] = useState<Surface>("intake");
|
|
98
|
-
const [initialValues, setInitialValues] = useState<
|
|
126
|
+
const [initialValues, setInitialValues] = useState<Record<string, string>>(
|
|
99
127
|
{},
|
|
100
128
|
);
|
|
101
129
|
const [submitError, setSubmitError] = useState<string | undefined>(
|
|
@@ -105,7 +133,7 @@ export function ConversationalIntakeWrapper() {
|
|
|
105
133
|
|
|
106
134
|
const getThinkingDelayMs = useCallback(() => {
|
|
107
135
|
turnIndexRef.current += 1;
|
|
108
|
-
|
|
136
|
+
fireIntakeTurn({
|
|
109
137
|
turnIndex: turnIndexRef.current,
|
|
110
138
|
status: "in_progress",
|
|
111
139
|
locale,
|
|
@@ -128,8 +156,8 @@ export function ConversationalIntakeWrapper() {
|
|
|
128
156
|
}, []);
|
|
129
157
|
|
|
130
158
|
const handleComplete = useCallback(
|
|
131
|
-
(answers:
|
|
132
|
-
|
|
159
|
+
({ answers }: IntakeState) => {
|
|
160
|
+
fireIntakeTurn({
|
|
133
161
|
turnIndex: turnIndexRef.current,
|
|
134
162
|
status: "completed",
|
|
135
163
|
locale,
|
|
@@ -146,8 +174,8 @@ export function ConversationalIntakeWrapper() {
|
|
|
146
174
|
);
|
|
147
175
|
|
|
148
176
|
const handleSkip = useCallback(
|
|
149
|
-
(answers:
|
|
150
|
-
|
|
177
|
+
({ answers }: IntakeState) => {
|
|
178
|
+
fireIntakeTurn({
|
|
151
179
|
turnIndex: turnIndexRef.current,
|
|
152
180
|
status: "skipped",
|
|
153
181
|
locale,
|
|
@@ -159,30 +187,40 @@ export function ConversationalIntakeWrapper() {
|
|
|
159
187
|
|
|
160
188
|
if (surface === "form") {
|
|
161
189
|
return (
|
|
162
|
-
|
|
190
|
+
<Container className={inline ? "p-0" : "py-section"}>
|
|
163
191
|
{submitError ? (
|
|
164
|
-
<
|
|
192
|
+
<InlineMessage
|
|
193
|
+
variant="error"
|
|
194
|
+
data-testid="contact-intake-submit-error"
|
|
195
|
+
>
|
|
165
196
|
{submitError}
|
|
166
|
-
</
|
|
197
|
+
</InlineMessage>
|
|
167
198
|
) : null}
|
|
168
|
-
<
|
|
169
|
-
|
|
199
|
+
<LeadCaptureForm
|
|
200
|
+
action={captureLeadAction}
|
|
201
|
+
text={contactFormText(t)}
|
|
202
|
+
termsHref={localeHref(locale, "/terms")}
|
|
203
|
+
initialValues={initialValues}
|
|
204
|
+
/>
|
|
205
|
+
</Container>
|
|
170
206
|
);
|
|
171
207
|
}
|
|
172
208
|
|
|
173
209
|
return (
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
<Container className={inline ? "p-0" : "py-section"}>
|
|
211
|
+
<ConversationalIntake
|
|
212
|
+
script={script}
|
|
213
|
+
onComplete={handleComplete}
|
|
214
|
+
onSkip={handleSkip}
|
|
215
|
+
getThinkingDelayMs={getThinkingDelayMs}
|
|
216
|
+
labels={{
|
|
217
|
+
guidedBadge: t("intake.guidedBadge"),
|
|
218
|
+
skip: t("intake.skip"),
|
|
219
|
+
send: t("intake.send"),
|
|
220
|
+
freeTextPlaceholder: t("intake.freeTextPlaceholder"),
|
|
221
|
+
}}
|
|
222
|
+
data-testid="contact-conversational-intake"
|
|
223
|
+
/>
|
|
224
|
+
</Container>
|
|
187
225
|
);
|
|
188
226
|
}
|
package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* the EXISTING lead-capture endpoint. Only the outermost network
|
|
7
7
|
* boundary is mocked (`@working-theory/api-client`'s `leads.submit`) —
|
|
8
8
|
* the REAL `ConversationalIntake` block (#4543) and its engine (#4532),
|
|
9
|
-
* the REAL `captureLeadAction` (the SAME action
|
|
10
|
-
* uses), and the REAL `submitLeadInputSchema` validation all run, proving
|
|
9
|
+
* the REAL `captureLeadAction` (the SAME action the shared
|
|
10
|
+
* `LeadCaptureForm` on `/contact` uses), and the REAL `submitLeadInputSchema` validation all run, proving
|
|
11
11
|
* the whole chain end to end. No file under apps/api is imported, mocked,
|
|
12
12
|
* or touched by this test or by the wiring it exercises.
|
|
13
13
|
* AC2 — Skip renders the existing form, carrying over answers already
|
|
@@ -36,13 +36,14 @@ jest.mock("@working-theory/analytics/server", () => ({
|
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
38
|
// ConversationalIntakeWrapper's own responsibility under test here is the
|
|
39
|
-
// submission/skip/analytics wiring around the block — not
|
|
40
|
-
// rendering, which
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
jest.mock("
|
|
45
|
-
|
|
39
|
+
// submission/skip/analytics wiring around the block — not the shared
|
|
40
|
+
// LeadCaptureForm's own rendering, which its block suite covers
|
|
41
|
+
// (packages/ui/src/blocks/__tests__/LeadCaptureForm.test.tsx). The real
|
|
42
|
+
// `@working-theory/ui` runs (the ConversationalIntake block included); only
|
|
43
|
+
// the form block is stubbed, so the carried-over `initialValues` can be read.
|
|
44
|
+
jest.mock("@working-theory/ui", () => ({
|
|
45
|
+
...jest.requireActual("@working-theory/ui"),
|
|
46
|
+
LeadCaptureForm: (props: { initialValues?: Record<string, string> }) =>
|
|
46
47
|
require("react").createElement("div", {
|
|
47
48
|
"data-testid": "contact-form-stub",
|
|
48
49
|
"data-initial-values": JSON.stringify(props.initialValues ?? {}),
|
|
@@ -53,6 +54,7 @@ import { leads } from "@working-theory/api-client";
|
|
|
53
54
|
import { track } from "@working-theory/analytics/server";
|
|
54
55
|
|
|
55
56
|
import { ConversationalIntakeWrapper } from "../ConversationalIntakeWrapper.js";
|
|
57
|
+
import * as intakeAnalytics from "../intake-analytics";
|
|
56
58
|
|
|
57
59
|
const mockSubmit = leads.submit as jest.Mock;
|
|
58
60
|
const mockTrack = track as jest.Mock;
|
|
@@ -184,7 +186,7 @@ describe("ConversationalIntakeWrapper — Skip (AC2)", () => {
|
|
|
184
186
|
});
|
|
185
187
|
|
|
186
188
|
describe("ConversationalIntakeWrapper — per-turn abandonment analytics (AC3)", () => {
|
|
187
|
-
it("fires one in_progress event per turn, then a terminal completed event, with increasing turnIndex", () => {
|
|
189
|
+
it("fires one in_progress event per turn, then a terminal completed event, with increasing turnIndex", async () => {
|
|
188
190
|
mockSubmit.mockResolvedValue({
|
|
189
191
|
ok: true,
|
|
190
192
|
body: { id: "lead_abc123", persisted: "db" },
|
|
@@ -198,6 +200,7 @@ describe("ConversationalIntakeWrapper — per-turn abandonment analytics (AC3)",
|
|
|
198
200
|
advanceThinking();
|
|
199
201
|
answer("jane@example.com");
|
|
200
202
|
advanceThinking();
|
|
203
|
+
await flushMicrotasks(); // recordIntakeTurn is dispatched on a later microtask (#6465)
|
|
201
204
|
|
|
202
205
|
const calls = mockTrack.mock.calls.map(([name, props]) => ({
|
|
203
206
|
name,
|
|
@@ -222,7 +225,7 @@ describe("ConversationalIntakeWrapper — per-turn abandonment analytics (AC3)",
|
|
|
222
225
|
}
|
|
223
226
|
});
|
|
224
227
|
|
|
225
|
-
it("tags an abandoned (skipped) session with the turn it stopped at", () => {
|
|
228
|
+
it("tags an abandoned (skipped) session with the turn it stopped at", async () => {
|
|
226
229
|
render(<ConversationalIntakeWrapper />);
|
|
227
230
|
advanceThinking();
|
|
228
231
|
answer("Jane Smith");
|
|
@@ -230,6 +233,7 @@ describe("ConversationalIntakeWrapper — per-turn abandonment analytics (AC3)",
|
|
|
230
233
|
|
|
231
234
|
fireEvent.click(screen.getByTestId(`${TEST_ID}-skip`));
|
|
232
235
|
advanceThinking();
|
|
236
|
+
await flushMicrotasks(); // recordIntakeTurn is dispatched on a later microtask (#6465)
|
|
233
237
|
|
|
234
238
|
const calls = mockTrack.mock.calls.map(([, props]) => props) as Array<{
|
|
235
239
|
status: string;
|
|
@@ -240,3 +244,82 @@ describe("ConversationalIntakeWrapper — per-turn abandonment analytics (AC3)",
|
|
|
240
244
|
expect(terminal?.turnIndex).toBeGreaterThan(0);
|
|
241
245
|
});
|
|
242
246
|
});
|
|
247
|
+
|
|
248
|
+
describe("ConversationalIntakeWrapper — a failing analytics call never strands the intake (#6465)", () => {
|
|
249
|
+
let recordSpy: jest.SpyInstance | undefined;
|
|
250
|
+
|
|
251
|
+
afterEach(() => {
|
|
252
|
+
recordSpy?.mockRestore();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
/** The Server Action reference throws synchronously at the call site. */
|
|
256
|
+
function spyRecordIntakeTurnThrowing(): void {
|
|
257
|
+
recordSpy = jest
|
|
258
|
+
.spyOn(intakeAnalytics, "recordIntakeTurn")
|
|
259
|
+
.mockImplementation(() => {
|
|
260
|
+
throw new Error("boom");
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
it("drives the intake from mount to its first question with Skip enabled", async () => {
|
|
265
|
+
spyRecordIntakeTurnThrowing();
|
|
266
|
+
|
|
267
|
+
render(<ConversationalIntakeWrapper />);
|
|
268
|
+
expect(screen.getByTestId(`${TEST_ID}-skip`)).toBeDisabled();
|
|
269
|
+
|
|
270
|
+
advanceThinking(); // turn 1 (name) revealed
|
|
271
|
+
await flushMicrotasks();
|
|
272
|
+
|
|
273
|
+
expect(screen.queryByTestId(`${TEST_ID}-thinking`)).toBeNull();
|
|
274
|
+
expect(screen.getByText("intake.namePrompt")).toBeInTheDocument();
|
|
275
|
+
expect(screen.getByTestId(`${TEST_ID}-skip`)).not.toBeDisabled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("dispatches the per-turn analytics call off the delay callback's synchronous path", async () => {
|
|
279
|
+
recordSpy = jest
|
|
280
|
+
.spyOn(intakeAnalytics, "recordIntakeTurn")
|
|
281
|
+
.mockResolvedValue(undefined);
|
|
282
|
+
|
|
283
|
+
render(<ConversationalIntakeWrapper />);
|
|
284
|
+
expect(recordSpy).toHaveBeenCalledTimes(0);
|
|
285
|
+
|
|
286
|
+
await flushMicrotasks();
|
|
287
|
+
expect(recordSpy).toHaveBeenCalledTimes(1);
|
|
288
|
+
expect(recordSpy.mock.calls[0]?.[0]).toMatchObject({
|
|
289
|
+
turnIndex: 1,
|
|
290
|
+
status: "in_progress",
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("still falls back to the form on Skip", async () => {
|
|
295
|
+
spyRecordIntakeTurnThrowing();
|
|
296
|
+
|
|
297
|
+
render(<ConversationalIntakeWrapper />);
|
|
298
|
+
advanceThinking();
|
|
299
|
+
fireEvent.click(screen.getByTestId(`${TEST_ID}-skip`));
|
|
300
|
+
advanceThinking(); // skip message revealed, onSkip fires
|
|
301
|
+
await flushMicrotasks();
|
|
302
|
+
|
|
303
|
+
expect(screen.getByTestId("contact-form-stub")).toBeInTheDocument();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("still submits the lead on completion", async () => {
|
|
307
|
+
spyRecordIntakeTurnThrowing();
|
|
308
|
+
mockSubmit.mockResolvedValue({
|
|
309
|
+
ok: true,
|
|
310
|
+
body: { id: "lead_abc123", persisted: "db" },
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
render(<ConversationalIntakeWrapper />);
|
|
314
|
+
advanceThinking();
|
|
315
|
+
answer("Jane Smith");
|
|
316
|
+
advanceThinking();
|
|
317
|
+
answer("I'd like a demo.");
|
|
318
|
+
advanceThinking();
|
|
319
|
+
answer("jane@example.com");
|
|
320
|
+
advanceThinking(); // completion revealed, onComplete fires
|
|
321
|
+
await flushMicrotasks();
|
|
322
|
+
|
|
323
|
+
expect(mockSubmit).toHaveBeenCalledTimes(1);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contactFormText (#6730 D8) — the `contact` namespace mapped onto the shared
|
|
3
|
+
* form block's copy, with `unavailableBody`'s `{email}` placeholder kept for
|
|
4
|
+
* the block to fill.
|
|
5
|
+
*/
|
|
6
|
+
import messages from "../../../../messages/en.json";
|
|
7
|
+
import { contactFormText } from "../contact-form-text";
|
|
8
|
+
|
|
9
|
+
/** A translator over the English catalog that interpolates `{name}` values, as ICU does. */
|
|
10
|
+
function translator(key: string, values?: Record<string, string>): string {
|
|
11
|
+
const template = (messages.contact as Record<string, unknown>)[key];
|
|
12
|
+
if (typeof template !== "string") throw new Error(`missing contact.${key}`);
|
|
13
|
+
return template.replace(/\{(\w+)\}/g, (m, name: string) => values?.[name] ?? m);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("contactFormText", () => {
|
|
17
|
+
it("maps every block string from the English catalog, keeping {email} for the block", () => {
|
|
18
|
+
expect(contactFormText(translator)).toEqual({
|
|
19
|
+
nameLabel: "Name",
|
|
20
|
+
emailLabel: "Email",
|
|
21
|
+
messageLabel: "Message",
|
|
22
|
+
submit: "Send message",
|
|
23
|
+
sending: "Sending…",
|
|
24
|
+
success: messages.contact.successMessage,
|
|
25
|
+
genericError: messages.contact.genericError,
|
|
26
|
+
consentStatement: messages.contact.consentStatement,
|
|
27
|
+
unavailableHeading: messages.contact.unavailableHeading,
|
|
28
|
+
unavailableBody: "Email us at {email} and we'll reply.",
|
|
29
|
+
unavailableNoEmail: messages.contact.unavailableNoEmail,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|