diff --git a/internal b/internal index 66ebb051..f76aac66 160000 --- a/internal +++ b/internal @@ -1 +1 @@ -Subproject commit 66ebb0511e50832fd7f0735fd60a3ab77061850b +Subproject commit f76aac66ad0d3d0a40bc4ef0a8c68e0f678fc17e diff --git a/src/Web/autopilot-monitor-web/app/buy/layout.tsx b/src/Web/autopilot-monitor-web/app/buy/layout.tsx index ed5eec5c..6e03601f 100644 --- a/src/Web/autopilot-monitor-web/app/buy/layout.tsx +++ b/src/Web/autopilot-monitor-web/app/buy/layout.tsx @@ -4,7 +4,7 @@ import { SITE_URL } from "@/utils/config"; export const metadata: Metadata = { title: "Get Pro – Purchase Options", description: - "How to purchase Autopilot Monitor Pro: direct online purchase and the Microsoft commercial marketplace — both coming soon. Pricing will be announced; Community stays free.", + "How to purchase Autopilot Monitor Pro: through Microsoft Marketplace or online via Cleverbridge — or try it first with a free 30-day trial. Community stays free.", keywords: [ "Autopilot Monitor Pro", "Autopilot Monitor buy", @@ -15,13 +15,13 @@ export const metadata: Metadata = { openGraph: { title: "Get Pro – Autopilot Monitor", description: - "How to purchase Autopilot Monitor Pro: direct online purchase and the Microsoft commercial marketplace — both coming soon.", + "How to purchase Autopilot Monitor Pro: through Microsoft Marketplace or online via Cleverbridge.", url: `${SITE_URL}/buy`, }, twitter: { title: "Get Pro – Autopilot Monitor", description: - "How to purchase Autopilot Monitor Pro: direct online purchase and the Microsoft commercial marketplace — both coming soon.", + "How to purchase Autopilot Monitor Pro: through Microsoft Marketplace or online via Cleverbridge.", }, alternates: { canonical: `${SITE_URL}/buy`, diff --git a/src/Web/autopilot-monitor-web/app/buy/page.tsx b/src/Web/autopilot-monitor-web/app/buy/page.tsx index 3f016b0a..0d07d674 100644 --- a/src/Web/autopilot-monitor-web/app/buy/page.tsx +++ b/src/Web/autopilot-monitor-web/app/buy/page.tsx @@ -1,26 +1,32 @@ import Link from "next/link"; import { LandingNavbar } from "../../components/landing/LandingNavbar"; import { SiteFooter } from "../../components/SiteFooter"; +import { DOCS_URL } from "@/utils/config"; /** - * Purchase page for Pro. Linked from the public /plans page and from the - * portal's Plan section (absolute www URL — /buy is public surface). - * - * Both purchase channels are honest "coming soon" placeholders until the - * checkout links exist. When they do: the provider host must be registered in - * utils/config.ts, the CSP in staticwebapp.config.json, the swaConfig guard - * test, and the dev CSP in next.config.ts — see tasks/todo.md follow-up. + * Purchase page for Pro: hands the buyer off to one of the two purchase channels. Linked from + * the public /plans page and from the portal's Plan section (absolute www URL — /buy is public + * surface). It names no price on purpose: the plan cards carry the list price, the channel's + * checkout the binding one. Plain outbound links, so neither host needs a CSP entry. */ +const PURCHASE_GUIDE_URL = `${DOCS_URL}/troubleshooting-and-support/how-to-purchase`; + const CHANNELS = [ { label: "Microsoft Marketplace", description: - "Purchase through the Microsoft commercial marketplace using your organization's existing Microsoft billing relationship.", + "Subscribe through the Microsoft commercial marketplace and pay through your organization's existing Azure billing.", + href: "https://marketplace.microsoft.com/en-us/product/saas/glueckkanja-gabag.autopilot-monitor-transactable-prod?tab=Overview", + cta: "Buy on Microsoft Marketplace", + guide: `${PURCHASE_GUIDE_URL}/microsoft-marketplace`, }, { label: "Cleverbridge", description: - "Buy Pro online with a credit card or on invoice — a fast, self-service checkout for a single organization.", + "Buy Pro online by credit card, PayPal or bank transfer — a fast, self-service checkout for a single organization.", + href: "https://www.cleverbridge.com/306/purl-Autopilot-Monitor-Buy-Y", + cta: "Buy with Cleverbridge", + guide: `${PURCHASE_GUIDE_URL}/cleverbridge`, }, ]; @@ -35,9 +41,8 @@ export default function BuyPage() { Get Autopilot Monitor Pro

- Pro will be available through two purchase channels. Neither is open yet — pricing - and availability will be announced. Until then, Community is the full product, free - for everyone. + Pick the channel that fits how your organization buys software. Both lead to the same + Pro plan on your existing tenant — your data stays where it is.

@@ -48,34 +53,42 @@ export default function BuyPage() { {CHANNELS.map((channel) => (

{channel.label}

-

{channel.description}

- + {channel.cta} + + + Purchase guide: prerequisites, payment and cancellation +
))} - {/* Interim path */} + {/* Trial + questions */}
-

Questions About Pro?

+

Try Pro First

- Interested in Pro for your organization or an MSP scenario, or want to know when it - opens? Reach out through any channel on the{" "} + A tenant administrator can start a one-time, free 30-day Pro trial in the portal under + Settings → Tenant → Plan. When it ends, the tenant returns to Community automatically. + Questions about Pro or an MSP scenario? Reach out through any channel on the{" "} Help & Support {" "} - page — you'll talk directly to the person building the platform. And if you - haven't compared the plans yet, the{" "} + page, or compare the plans in the{" "} plans overview - {" "} - shows exactly what Pro adds. + + .

diff --git a/src/Web/autopilot-monitor-web/app/get-started/page.tsx b/src/Web/autopilot-monitor-web/app/get-started/page.tsx index 9fa8998d..fc770ebc 100644 --- a/src/Web/autopilot-monitor-web/app/get-started/page.tsx +++ b/src/Web/autopilot-monitor-web/app/get-started/page.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; import { LandingNavbar } from "../../components/landing/LandingNavbar"; -import { LoginButton } from "../../components/landing/LoginButton"; +import { SignupConsentCta } from "../../components/landing/SignupConsentCta"; import { SiteFooter } from "../../components/SiteFooter"; import { DOCS_URL, SITE_URL } from "@/utils/config"; @@ -85,11 +85,8 @@ export default function GetStartedPage() { - {/* CTA */} -
- - Sign in to get started - + {/* CTA — sign-in waits for the Terms + DPA tick */} + Read the docs -
+

When you need more later: the{" "} diff --git a/src/Web/autopilot-monitor-web/app/plans/page.tsx b/src/Web/autopilot-monitor-web/app/plans/page.tsx index 5f758232..96525c64 100644 --- a/src/Web/autopilot-monitor-web/app/plans/page.tsx +++ b/src/Web/autopilot-monitor-web/app/plans/page.tsx @@ -39,7 +39,7 @@ export default function PlansPage() { } proBadge={ - Coming soon + Free 30-day trial } proCta={ @@ -53,7 +53,8 @@ export default function PlansPage() { />

- Pricing and availability for Pro will be announced. Community stays free.{" "} + Pro is sold through Microsoft Marketplace and Cleverbridge; a tenant administrator can + try it free for 30 days from the portal. Community stays free.{" "} For a feature-by-feature reference, see the{" "} - ) : ( + ) : editionInfo.trialAvailable ? ( - Coming soon + Free 30-day trial - ); + ) : undefined; // CTA — only meaningful while the tenant is on Community const proCta = !isPro ? ( <> {trialConsumed ? ( +

This tenant has already used its Pro trial.

+ ) : !canStartTrial ? (

- This tenant has already used its Pro trial. To move to Pro,{" "} - - get in touch - - . + A tenant administrator can start the one-time 30-day Pro trial here.

- ) : !TRIAL_SELF_SERVICE_ENABLED ? ( - - ) : canStartTrial && missingProfile.length > 0 ? ( + ) : missingProfile.length > 0 ? ( // Pro-requires-contact-profile gate (backend enforces the same via 409 // ContactProfileRequired — this branch just makes the path obvious).
@@ -97,7 +73,7 @@ export function SectionPlan() { {" "} so we can reach and identify you for service or security matters — then start your trial here.
- ) : canStartTrial && !confirming ? ( + ) : !confirming ? ( - ) : canStartTrial && confirming ? ( + ) : (
One-time trial — start now?
- ) : null} + )}

- Curious how Pro will be sold?{" "} + Ready to buy? Pro is sold through Microsoft Marketplace and Cleverbridge —{" "} - View purchase options + view purchase options .

@@ -167,7 +143,7 @@ export function SectionPlan() {

{isPro && editionInfo.isTrial ? "When the trial ends, the tenant returns to Community automatically." - : "Scope, pricing and timeline for Pro will be announced. Community stays free."} + : "After a purchase this tenant moves to Pro and keeps its data. Community stays free."}

diff --git a/src/Web/autopilot-monitor-web/components/landing/LoginButton.tsx b/src/Web/autopilot-monitor-web/components/landing/LoginButton.tsx index 39f1b5b2..94c238eb 100644 --- a/src/Web/autopilot-monitor-web/components/landing/LoginButton.tsx +++ b/src/Web/autopilot-monitor-web/components/landing/LoginButton.tsx @@ -8,9 +8,12 @@ export function LoginButton({ className, children, signup = false, + disabled = false, }: { className?: string; children: React.ReactNode; + /** Inert until the caller's precondition holds (get-started: Terms + DPA accepted). */ + disabled?: boolean; /** * Signup-funnel CTA (get-started "Sign in to get started"): routes the login through the * PRIMARY app registration so brand-new tenants consent the NEW app as part of the expected @@ -43,7 +46,7 @@ export function LoginButton({ }; return ( - ); diff --git a/src/Web/autopilot-monitor-web/components/landing/SignupConsentCta.tsx b/src/Web/autopilot-monitor-web/components/landing/SignupConsentCta.tsx new file mode 100644 index 00000000..0fb4361a --- /dev/null +++ b/src/Web/autopilot-monitor-web/components/landing/SignupConsentCta.tsx @@ -0,0 +1,56 @@ +"use client"; + +import { useState } from "react"; +import { LoginButton } from "./LoginButton"; +import { DOCS_URL } from "@/utils/config"; + +const LINK_CLASS = "text-[var(--lp-accent-ink)] hover:opacity-80 underline"; + +/** + * Get-started CTA: the signup sign-in stays inert until the visitor ticks that they accept the + * Terms of Use and the Data Processing Agreement. A client-side gate only — nothing is recorded + * here. Both links open in a new tab so the tick survives reading them. + */ +export function SignupConsentCta({ children }: { children?: React.ReactNode }) { + const [accepted, setAccepted] = useState(false); + + return ( +
+ + +
+ + Sign in to get started + + {children} +
+
+ ); +} diff --git a/src/Web/autopilot-monitor-web/components/plans/PlanCards.tsx b/src/Web/autopilot-monitor-web/components/plans/PlanCards.tsx index 93330df8..da5346c3 100644 --- a/src/Web/autopilot-monitor-web/components/plans/PlanCards.tsx +++ b/src/Web/autopilot-monitor-web/components/plans/PlanCards.tsx @@ -128,17 +128,10 @@ export function PlanCards({ surface, highlight = null, communityBadge, proBadge,
{proPrice ?? ( - PRO_PRICE ? ( - <> - {PRO_PRICE.amount} - {PRO_PRICE.suffix} - - ) : ( - <> - Pricing - — announced soon - - ) + <> + {PRO_PRICE.amount} + {PRO_PRICE.suffix} + )}
diff --git a/src/Web/autopilot-monitor-web/components/plans/planData.ts b/src/Web/autopilot-monitor-web/components/plans/planData.ts index b32e8287..6eaacbe7 100644 --- a/src/Web/autopilot-monitor-web/components/plans/planData.ts +++ b/src/Web/autopilot-monitor-web/components/plans/planData.ts @@ -39,9 +39,11 @@ export const proExtras = [ ]; /** - * Pro price. Stays null until pricing is announced — PlanCards then renders the - * "Pricing — announced soon" teaser. Once the price is final, set it here (e.g. - * `{ amount: "€X", suffix: "per device / month" }`) and every surface — landing - * and portal — updates in the same one-line change. + * Pro list price, shown on the plan cards only (public /plans and the portal Plan section). + * The /buy page deliberately names no price — the checkout of the purchase channel carries + * the binding one. */ -export const PRO_PRICE: { amount: string; suffix: string } | null = null; +export const PRO_PRICE: { amount: string; suffix: string } = { + amount: "€149", + suffix: "/ month, excl. VAT", +};