// Use-case pages — the /use-cases/ hub and the ten pages beneath it. // // There is no body copy in this file on purpose. Every word lives in usecases-content.php, // which renders the pages server-side AND emits them as window.AM_USECASES for this component // — the same arrangement pages/legal.jsx uses, and for a stronger reason: these pages exist to // be found in a search, so the server-rendered copy is the one that gets indexed. If the text // were written twice the indexed version and the read version would drift, and the drift would // be invisible to anyone testing in a browser. // // Keep the sections here in step with AM_usecaseHTML() in usecases-content.php. A visitor // should not be able to tell whether they got the PHP render or this one. const { useState: useStateUC, useEffect: useEffectUC } = React; function UcCheck() { return ( ); } function UcParts({ parts }) { return (
Built from
{parts.map(p => {p})}
); } // Breadcrumbs are real links as well as SPA navigations — they are mirrored in the // BreadcrumbList structured data index.php emits, and a crumb a crawler cannot follow is worse // than no crumb at all. function UcCrumbs({ current, onNavigate, onNavigateUseCase }) { return ( ); } /* ============================================================================= DETAIL — one use case ============================================================================= */ function UseCaseDetailPage({ uc, slug, all, onNavigate, onNavigateUseCase }) { return (
{uc.tag}

{uc.h1}

{uc.intro}

{uc.said}
onNavigate("contact"))}> Talk about this one onNavigate("platform"))}>See the platform

You are probably here because

    {uc.symptoms.map((s, i) =>
  • {s}
  • )}

What is actually going wrong

{uc.wrong.map((p, i) =>

{p}

)}

What we build

{uc.build.map((p, i) =>

{p}

)}

How it goes together

    {uc.steps.map(s => (
  1. {s.n}

    {s.h}

    {s.p}

  2. ))}

The platform features doing the work

Nothing here is written specially for this use case — it is the same platform every ActiveManage application is built from.{" "} onNavigate("platform"))}>The full feature list is on the platform page.

{uc.parts.map(p => ( ))}
FeatureWhat it doesWhy it matters here
{p.name} {p.what} {p.why}

What you end up with

    {uc.proof.map((p, i) =>
  • {p}
  • )}

Whether this is for you

A good fit when

    {uc.fit.for.map((f, i) =>
  • {f}
  • )}

Probably not, if

    {uc.fit.not.map((f, i) =>
  • {f}
  • )}

How a project like this runs

{uc.shape.map(s => (

{s.h}

{s.p}

))}

Questions we get asked

{uc.faqs.map((f, i) => (

{f.q}

{f.a}

))}
{uc.related && uc.related.length > 0 && (

Related use cases

{uc.related.filter(r => all[r]).map(r => ( onNavigateUseCase(r))} > {all[r].tag} {all[r].said} Read more ))}

onNavigateUseCase(null))}>All use cases

)}

Sound like your week?

Tell us what you are trying to fix and we will tell you whether this is the right shape for it — including when it is not.

onNavigate("contact"))}> Start a conversation onNavigate("bespoke"))}>How we work
); } /* ============================================================================= HUB — /use-cases/ ============================================================================= */ // The heading and intro are duplicated from the 'usecases' entry in routes.php, which is what // the server renders. That duplication is the standing cost of prerendering without a build // step (see the header of routes.php) — if you change the wording, change it in both places. function UseCaseIndexPage({ all, onNavigate, onNavigateUseCase }) { return (
Where it fits

What people actually build with us

Nobody goes shopping for a development platform. They go looking for a fix to something specific that is costing them time, money or sleep. These are the ten problems we are asked about most often, each written up properly: what tends to be going wrong, what we build instead, which parts of the platform do the work, and — because it saves everyone time — when you would be better off with something else.

{Object.keys(all).map(slug => { const uc = all[slug]; return ( onNavigateUseCase(slug))} > {uc.tag}

{uc.said}

{uc.intro}

{uc.nav}
); })}

Not on the list?

These are the ten we are asked for most often, not the limit of what the platform builds. If your problem is not here, describe it to us in plain English and we will tell you honestly whether it fits.

onNavigate("contact"))}> Describe your problem onNavigate("platform"))}>See the platform
); } /* ============================================================================= SHELL — picks hub or detail, and sources the payload ============================================================================= */ // index.php inlines two payloads on a use-case route, and neither anywhere else: // // window.AM_USECASES_INDEX all ten, four fields each — enough for every card // window.AM_USECASES the one document being viewed, in full // // So a direct hit on /use-cases/crm/ re-renders with no fetch and no flash, which is the load // that matters, because it is the one arriving from a search result. Anything else — reaching // a use case by clicking through from the homepage, or moving between two of them without a // reload — finds what it needs missing and fetches the full set once, cached back onto window. // // Fetching is therefore the uncommon path, and it is the only one that ever shows a skeleton. function UseCasesPage({ slug, onNavigate, onNavigateUseCase }) { const [all, setAll] = useStateUC(() => (typeof window !== "undefined" ? window.AM_USECASES : null) || null); const [index, setIndex] = useStateUC(() => (typeof window !== "undefined" ? window.AM_USECASES_INDEX : null) || null); const [failed, setFailed] = useStateUC(false); // The hub only needs the index; a detail page needs its own full document. Asking for // exactly what this render requires is what keeps the common case free of a round trip. const hasIndex = !!(index && Object.keys(index).length); const needsFetch = slug ? !(all && all[slug]) : !hasIndex; useEffectUC(() => { if (!needsFetch || failed) return; let alive = true; fetch("/usecases-data.php", { headers: { Accept: "application/json" } }) .then(r => (r.ok ? r.json() : Promise.reject(r.status))) .then(j => { if (!alive) return; if (j && typeof j === "object" && Object.keys(j).length) { // The full set supersedes the partial payload for both purposes — it is a superset // of the index, so derive the index from it rather than keeping two sources. window.AM_USECASES = j; setAll(j); setIndex(j); } else { setFailed(true); } }) .catch(() => { if (alive) setFailed(true); }); return () => { alive = false; }; }, [needsFetch, failed]); if (needsFetch && !failed) { return (
); } // Genuinely unavailable — the fetch failed too. Say so, and still offer a way forward, // rather than rendering an empty page that looks like the content. if (needsFetch) { return (

Use cases

We couldn't load this page. Please reload, or{" "} onNavigate("contact"))}>tell us what you're trying to fix{" "} and we'll answer directly.

); } const uc = slug && all ? all[slug] : null; // A slug nothing knows about. The server 404s these before React ever loads, so this only // happens if the route table and the content file have drifted — fall back to the hub rather // than a blank page. if (slug && !uc) { return ; } // `index` rather than `all` for the cards: it is always present and always sufficient, where // the full set is only loaded when something actually needed it. return uc ? : ; } Object.assign(window, { UseCasesPage, UseCaseDetailPage, UseCaseIndexPage });