From 13298a4e1494a60f9f8b7bdec7aed096ca6a2df0 Mon Sep 17 00:00:00 2001 From: Dimitrie Hoekstra Date: Thu, 28 May 2026 15:25:58 +0200 Subject: [PATCH 1/2] Add Use Cases section (structure only) Skeleton structure for a new Use Cases content shape on the marketing site: - /use-cases/ hub (renders an empty grid until use-case pages land). - New use-case layout with the canonical page anatomy (hero, operational gap, why existing systems did not solve it, the operational workflow, how it is operationalized with FlowFuse, outcomes, related resources). - _template.njk (non-rendered) as a copy-and-rename starting point for per-pattern content PRs. - Reusable related-resources block wired to a usecase:slug tag namespace using existing collection logic. Structure only. No use-case pages yet. No nav changes. No Expert dock. Follow-up PRs targeting this branch will add the dock, the nav entry, and the per-pattern pages with content from sales. --- .eleventy.js | 1 + .../components/related-resources.njk | 56 ++++++ src/_includes/layouts/use-case.njk | 165 ++++++++++++++++++ src/use-cases/_template.njk | 36 ++++ src/use-cases/index.njk | 57 ++++++ 5 files changed, 315 insertions(+) create mode 100644 src/_includes/components/related-resources.njk create mode 100644 src/_includes/layouts/use-case.njk create mode 100644 src/use-cases/_template.njk create mode 100644 src/use-cases/index.njk diff --git a/.eleventy.js b/.eleventy.js index f9b28d6f71..ac251b91fb 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -221,6 +221,7 @@ module.exports = function(eleventyConfig) { eleventyConfig.addLayoutAlias('page', 'layouts/page.njk'); eleventyConfig.addLayoutAlias('nohero', 'layouts/nohero.njk'); eleventyConfig.addLayoutAlias('solution', 'layouts/solution.njk'); + eleventyConfig.addLayoutAlias('use-case', 'layouts/use-case.njk'); eleventyConfig.addLayoutAlias('catalog', 'layouts/catalog.njk'); eleventyConfig.addLayoutAlias('redirect', 'layouts/redirect.njk'); diff --git a/src/_includes/components/related-resources.njk b/src/_includes/components/related-resources.njk new file mode 100644 index 0000000000..33984b912d --- /dev/null +++ b/src/_includes/components/related-resources.njk @@ -0,0 +1,56 @@ +{# ============================================================ + RELATED RESOURCES (skeleton) + Three columns: Case studies / White papers / Blog. + Driven by a tag passed in as `relatedTag` (e.g. "industry:food-beverage" + or "usecase:track-and-trace"). Resources opt in later by adding that tag; + the matching tag collection is then split by section. + No existing customer stories are tagged, so every column renders the + empty "More coming soon" state. + ============================================================ #} +{% set relatedTag = relatedTag or "" %} +{% set tagged = collections[relatedTag] or [] %} + +{% set caseStudies = [] %} +{% set whitepapers = [] %} +{% set blogPosts = [] %} +{% for item in tagged %} + {% if "/customer-stories/" in item.url %} + {% set caseStudies = (caseStudies.push(item), caseStudies) %} + {% elif "/whitepaper/" in item.url %} + {% set whitepapers = (whitepapers.push(item), whitepapers) %} + {% elif "/blog/" in item.url %} + {% set blogPosts = (blogPosts.push(item), blogPosts) %} + {% endif %} +{% endfor %} + +{% set columns = [ + {heading: "Case studies", items: caseStudies}, + {heading: "White papers", items: whitepapers}, + {heading: "Blog", items: blogPosts} +] %} + +
+
+

Related resources

+
+ {% for column in columns %} +
+

{{ column.heading }}

+ {% if column.items.length %} + + {% else %} +
+

More coming soon

+
+ {% endif %} +
+ {% endfor %} +
+
+
diff --git a/src/_includes/layouts/use-case.njk b/src/_includes/layouts/use-case.njk new file mode 100644 index 0000000000..87644f0dbb --- /dev/null +++ b/src/_includes/layouts/use-case.njk @@ -0,0 +1,165 @@ +--- +layout: layouts/base.njk +sitemapPriority: 0.7 +--- +{# ============================================================ + USE CASE LAYOUT (skeleton, generic workflow pattern) + Per-page front-matter: problem, gap[], workflow[], outcomes[]. + Sections 3 and 5 are fixed generic platform positioning. + No customer names, no quotes, no real numbers. + ============================================================ #} + +
+ + +
+
+

Use case

+

{{ title }}

+

{{ problem }}

+ +
+
+ + +
+
+

The operational gap

+
+ {% for paragraph in gap %} +

{{ paragraph }}

+ {% endfor %} +
+

Placeholder copy. Generic operational framing for this workflow pattern.

+
+
+ + +
+
+

Why existing systems did not solve it

+ {% set reasons = [ + {title: "MES is too rigid", detail: "Fixed workflows are slow and costly to change as operations evolve."}, + {title: "Dashboards do not act", detail: "Visibility alone does not trigger or coordinate a response."}, + {title: "Historians store but do not orchestrate", detail: "Data is captured for later, not turned into action in the moment."}, + {title: "ERP does not execute", detail: "Business systems plan work but do not run the operational workflow."}, + {title: "AI builds but does not operationalize", detail: "Generated logic still needs governance, deployment and scale."}, + {title: "Scripts do not scale", detail: "One-off scripts are hard to manage, repeat and roll out across sites."} + ] %} +
+ {% for reason in reasons %} +
+
{% include "components/icons/x-circle.svg" %}
+

{{ reason.title }}

+

{{ reason.detail }}

+
+ {% endfor %} +
+
+
+ + +
+
+

The operational workflow

+

A pattern that turns industrial events into action.

+ {% set steps = [ + {label: "Event", icon: "components/icons/bolt.svg", detail: "An industrial event triggers the workflow from a machine, sensor, broker or system signal."}, + {label: "Decision", icon: "components/icons/arrows-right-left.svg", detail: "Event-driven orchestration applies logic, context and rules to decide what should happen."}, + {label: "Action", icon: "components/icons/cursor-arrow-rays.svg", detail: "Guided operator actions and automated steps carry out the response where work happens."} + ] %} +
+ {% for step in steps %} +
+
+
+ {% include step.icon %} +
+ {{ loop.index }} +
+

{{ step.label }}

+

{{ step.detail }}

+
+ {% endfor %} +
+ {% if workflow %} +
    + {% for item in workflow %} +
  • + {% include "components/icons/check.svg" %} + {{ item }} +
  • + {% endfor %} +
+ {% endif %} +
+
+ + +
+
+

How it is operationalized with FlowFuse

+

Generic platform capabilities. No named customer or attributed quotes.

+ {% set ops = [ + {title: "Deployment", detail: "Ship the workflow to cloud, on-premise or edge from one platform."}, + {title: "Governance", detail: "Keep humans in control with approvals and audit trails."}, + {title: "Role-based access", detail: "Scope who can view, edit and deploy across teams."}, + {title: "Version control", detail: "Track changes and roll back safely."}, + {title: "CI/CD pipelines", detail: "Promote from development to test to production."}, + {title: "Multi-site rollout", detail: "Roll the same workflow out to every site in a controlled way."}, + {title: "Reuse", detail: "Package the pattern as a reusable building block."} + ] %} +
+ {% for op in ops %} +
+ + {% include "components/icons/check.svg" %} + +
+
{{ op.title }}
+

{{ op.detail }}

+
+
+ {% endfor %} +
+
+
+ + +
+
+

Outcomes

+
+ {% for outcome in outcomes %} +
+ {% include "components/icons/arrow-trending-up.svg" %} +

{{ outcome }}

+
+ {% endfor %} +
+

Placeholder outcomes. No customer names or real figures.

+
+
+ + + {% set relatedTag = "usecase:" + page.fileSlug %} + {% include "components/related-resources.njk" %} + + +
diff --git a/src/use-cases/_template.njk b/src/use-cases/_template.njk new file mode 100644 index 0000000000..11d6aee0c7 --- /dev/null +++ b/src/use-cases/_template.njk @@ -0,0 +1,36 @@ +--- +# =================================================================== +# Use-case page TEMPLATE. +# +# This file is intentionally NOT rendered (permalink: false) and is +# NOT included in the `use-case` collection. It exists as a copy-and- +# rename starting point for new use-case pages. +# +# To create a new use-case page: +# 1. Copy this file to `src/use-cases/.njk` +# 2. Replace the placeholder values below +# 3. Make sure `tags` keeps `use-case` so the page appears in the +# `/use-cases/` hub grid +# =================================================================== +permalink: false +eleventyExcludeFromCollections: true +layout: use-case +tags: + - use-case +title: "[Use case title]" +meta: + title: "[Use case title] | Use Cases | FlowFuse" + description: "[One-sentence summary of the workflow pattern. Placeholder.]" +problem: "[One short sentence stating the operational problem this pattern addresses. Placeholder.]" +gap: + - "[First paragraph describing the operational gap. Placeholder.]" + - "[Second paragraph. Placeholder.]" +workflow: + - "[First concrete step in the workflow. Placeholder.]" + - "[Second step. Placeholder.]" + - "[Third step. Placeholder.]" +outcomes: + - "[First outcome bullet. Placeholder.]" + - "[Second outcome bullet. Placeholder.]" + - "[Third outcome bullet. Placeholder.]" +--- diff --git a/src/use-cases/index.njk b/src/use-cases/index.njk new file mode 100644 index 0000000000..c7dfb199ec --- /dev/null +++ b/src/use-cases/index.njk @@ -0,0 +1,57 @@ +--- +layout: layouts/base.njk +sitemapPriority: 0.8 +title: "Use Cases" +meta: + title: "Use Cases | FlowFuse" + description: "Generic operational workflow patterns for industrial teams. Turn events into action with FlowFuse. Placeholder skeleton hub." +--- +
+ + +
+
+

Use cases

+

Turn industrial events into action

+

Operational workflow patterns that follow Event, Decision, Action. Explore how each one is operationalized with FlowFuse.

+ +
+
+ + +
+
+
+ {% for uc in collections["use-case"] | sort(false, true, "data.title") %} + +

{{ uc.data.title }}

+

{{ uc.data.problem }}

+ + View use case + {% include "components/icons/arrow-right.svg" %} + +
+ {% endfor %} +
+
+
+ + + {% set relatedTag = "" %} + {% include "components/related-resources.njk" %} + + +
+
+

See it on your workflow

+

Talk to an expert, or get started with FlowFuse today.

+ +
+
+ +
From 99bcf9e38eb2d35effca3841c102f999f6b2f273 Mon Sep 17 00:00:00 2001 From: Dimitrie Hoekstra Date: Mon, 1 Jun 2026 19:42:08 +0200 Subject: [PATCH 2/2] Use-case layout: content-driven sections + sticky nav; rich template --- src/_includes/layouts/use-case.njk | 289 ++++++++++++++++++++--------- src/use-cases/_template.njk | 92 +++++++-- 2 files changed, 279 insertions(+), 102 deletions(-) diff --git a/src/_includes/layouts/use-case.njk b/src/_includes/layouts/use-case.njk index 87644f0dbb..0996911bc4 100644 --- a/src/_includes/layouts/use-case.njk +++ b/src/_includes/layouts/use-case.njk @@ -3,12 +3,30 @@ layout: layouts/base.njk sitemapPriority: 0.7 --- {# ============================================================ - USE CASE LAYOUT (skeleton, generic workflow pattern) - Per-page front-matter: problem, gap[], workflow[], outcomes[]. - Sections 3 and 5 are fixed generic platform positioning. - No customer names, no quotes, no real numbers. + USE CASE LAYOUT (content-driven, pain-led narrative) + + Per-page front-matter (rich, preferred): + problem hero sub-headline (the pain in one line) + customerPain { heading, intro[], cards[ {icon,title,detail} ] } + outcomeFirst { heading, intro, dimensions[ {label,title,detail} ] } + whyItMatters { heading, intro, points[ {title,detail} ] } + competition { heading, intro, traps[ {label,title,detail} ] } + comparison { without[ {title,detail} ], with[ {title,detail} ] } + + Legacy fall-back front-matter (simple skeleton, still supported): + gap[], workflow[], outcomes[] + + No customer names and no attributed quotes on the public page. ============================================================ #} +{# --- build the sticky section nav from the blocks that exist --- #} +{% set nav = [] %} +{% if customerPain %}{% set nav = nav.concat([{ id: "customer-pain", label: "Customer Pain" }]) %}{% endif %} +{% if outcomeFirst %}{% set nav = nav.concat([{ id: "outcome-first", label: "Outcome First" }]) %}{% endif %} +{% if whyItMatters %}{% set nav = nav.concat([{ id: "why-it-matters", label: "Why It Matters" }]) %}{% endif %} +{% if competition %}{% set nav = nav.concat([{ id: "competition", label: "Why Off-the-Shelf Fails" }]) %}{% endif %} +{% if comparison %}{% set nav = nav.concat([{ id: "with-without", label: "With / Without FlowFuse" }]) %}{% endif %} +
+ + {% endif %} + + {% if customerPain %} -
+
-

The operational gap

-
- {% for paragraph in gap %} +

01 · Customer pain

+

{{ customerPain.heading }}

+
+ {% for paragraph in customerPain.intro %}

{{ paragraph }}

{% endfor %}
-

Placeholder copy. Generic operational framing for this workflow pattern.

+
+ {% for card in customerPain.cards %} +
+
+ {% include "components/icons/" + card.icon + ".svg" %} +
+

{{ card.title }}

+

{{ card.detail }}

+
+ {% endfor %} +
+ {% endif %} + {% if outcomeFirst %} -
+
-

Why existing systems did not solve it

- {% set reasons = [ - {title: "MES is too rigid", detail: "Fixed workflows are slow and costly to change as operations evolve."}, - {title: "Dashboards do not act", detail: "Visibility alone does not trigger or coordinate a response."}, - {title: "Historians store but do not orchestrate", detail: "Data is captured for later, not turned into action in the moment."}, - {title: "ERP does not execute", detail: "Business systems plan work but do not run the operational workflow."}, - {title: "AI builds but does not operationalize", detail: "Generated logic still needs governance, deployment and scale."}, - {title: "Scripts do not scale", detail: "One-off scripts are hard to manage, repeat and roll out across sites."} - ] %} -
- {% for reason in reasons %} +

02 · Outcome first

+

{{ outcomeFirst.heading }}

+

{{ outcomeFirst.intro }}

+
+ {% for dim in outcomeFirst.dimensions %}
-
{% include "components/icons/x-circle.svg" %}
-

{{ reason.title }}

-

{{ reason.detail }}

+ {{ dim.label }} +

{{ dim.title }}

+

{{ dim.detail }}

{% endfor %}
+ {% endif %} + {% if whyItMatters %} -
+
-

The operational workflow

-

A pattern that turns industrial events into action.

- {% set steps = [ - {label: "Event", icon: "components/icons/bolt.svg", detail: "An industrial event triggers the workflow from a machine, sensor, broker or system signal."}, - {label: "Decision", icon: "components/icons/arrows-right-left.svg", detail: "Event-driven orchestration applies logic, context and rules to decide what should happen."}, - {label: "Action", icon: "components/icons/cursor-arrow-rays.svg", detail: "Guided operator actions and automated steps carry out the response where work happens."} - ] %} -
- {% for step in steps %} -
-
-
- {% include step.icon %} -
- {{ loop.index }} +

03 · Why this is important

+

{{ whyItMatters.heading }}

+

{{ whyItMatters.intro }}

+
+ {% for point in whyItMatters.points %} +
+ {% if loop.index < 10 %}0{% endif %}{{ loop.index }} +
+

{{ point.title }}

+

{{ point.detail }}

-

{{ step.label }}

-

{{ step.detail }}

{% endfor %}
- {% if workflow %} -
    - {% for item in workflow %} -
  • - {% include "components/icons/check.svg" %} - {{ item }} -
  • - {% endfor %} -
- {% endif %}
+ {% endif %} + {% if competition %} -
+
-

How it is operationalized with FlowFuse

-

Generic platform capabilities. No named customer or attributed quotes.

- {% set ops = [ - {title: "Deployment", detail: "Ship the workflow to cloud, on-premise or edge from one platform."}, - {title: "Governance", detail: "Keep humans in control with approvals and audit trails."}, - {title: "Role-based access", detail: "Scope who can view, edit and deploy across teams."}, - {title: "Version control", detail: "Track changes and roll back safely."}, - {title: "CI/CD pipelines", detail: "Promote from development to test to production."}, - {title: "Multi-site rollout", detail: "Roll the same workflow out to every site in a controlled way."}, - {title: "Reuse", detail: "Package the pattern as a reusable building block."} - ] %} -
- {% for op in ops %} -
- - {% include "components/icons/check.svg" %} - -
-
{{ op.title }}
-

{{ op.detail }}

+

04 · Why off-the-shelf doesn't work

+

{{ competition.heading }}

+

{{ competition.intro }}

+
+ {% for trap in competition.traps %} +
+
+ {% include "components/icons/x-circle.svg" %} + {{ trap.label }}
+

{{ trap.title }}

+

{{ trap.detail }}

{% endfor %}
+ {% endif %} + {% if comparison %} -
+
-

Outcomes

-
- {% for outcome in outcomes %} -
- {% include "components/icons/arrow-trending-up.svg" %} -

{{ outcome }}

+

05 · With / without FlowFuse

+
+
+

Without FlowFuse

+
+ {% for row in comparison.without %} +
+ {% include "components/icons/x-mark.svg" %} +
+
{{ row.title }}
+

{{ row.detail }}

+
+
+ {% endfor %} +
+
+
+

With FlowFuse

+
+ {% for row in comparison.with %} +
+ {% include "components/icons/check-circle.svg" %} +
+
{{ row.title }}
+

{{ row.detail }}

+
+
+ {% endfor %} +
- {% endfor %}
-

Placeholder outcomes. No customer names or real figures.

+ {% endif %} + + {# ============================================================ + LEGACY SKELETON FALL-BACK (only when no rich blocks defined) + ============================================================ #} + {% if not nav.length %} + {% if gap %} +
+
+

The operational gap

+
+ {% for paragraph in gap %} +

{{ paragraph }}

+ {% endfor %} +
+

Placeholder copy. Generic operational framing for this workflow pattern.

+
+
+ {% endif %} + {% if workflow %} +
+
+

The operational workflow

+
    + {% for item in workflow %} +
  • + {% include "components/icons/check.svg" %} + {{ item }} +
  • + {% endfor %} +
+
+
+ {% endif %} + {% if outcomes %} +
+
+

Outcomes

+
+ {% for outcome in outcomes %} +
+ {% include "components/icons/arrow-trending-up.svg" %} +

{{ outcome }}

+
+ {% endfor %} +
+

Placeholder outcomes. No customer names or real figures.

+
+
+ {% endif %} + {% endif %} {% set relatedTag = "usecase:" + page.fileSlug %} {% include "components/related-resources.njk" %} - + +
+
+

See it on your operation

+

Talk to an expert, or get started with FlowFuse today.

+ +
+
+
diff --git a/src/use-cases/_template.njk b/src/use-cases/_template.njk index 11d6aee0c7..a4240e01d7 100644 --- a/src/use-cases/_template.njk +++ b/src/use-cases/_template.njk @@ -11,6 +11,14 @@ # 2. Replace the placeholder values below # 3. Make sure `tags` keeps `use-case` so the page appears in the # `/use-cases/` hub grid +# +# The layout renders a pain-led narrative from the rich blocks below +# (customerPain, outcomeFirst, whyItMatters, competition, comparison) +# plus a sticky section nav. Every block is optional: a section only +# renders when its block is present. Keep copy generic, with no +# customer names and no attributed quotes. +# +# Icon names refer to files in src/_includes/components/icons/. # =================================================================== permalink: false eleventyExcludeFromCollections: true @@ -20,17 +28,75 @@ tags: title: "[Use case title]" meta: title: "[Use case title] | Use Cases | FlowFuse" - description: "[One-sentence summary of the workflow pattern. Placeholder.]" -problem: "[One short sentence stating the operational problem this pattern addresses. Placeholder.]" -gap: - - "[First paragraph describing the operational gap. Placeholder.]" - - "[Second paragraph. Placeholder.]" -workflow: - - "[First concrete step in the workflow. Placeholder.]" - - "[Second step. Placeholder.]" - - "[Third step. Placeholder.]" -outcomes: - - "[First outcome bullet. Placeholder.]" - - "[Second outcome bullet. Placeholder.]" - - "[Third outcome bullet. Placeholder.]" + description: "[One-sentence summary of the use case. Placeholder.]" +problem: "[The pain in one line. This is the hero sub-headline. Placeholder.]" + +# 01 -- Customer pain +customerPain: + heading: "[One-line statement of the core pain.]" + intro: + - "[First framing paragraph.]" + - "[Second framing paragraph.]" + cards: + - icon: "clock" + title: "[Pain symptom.]" + detail: "[One or two sentences.]" + - icon: "clip-list" + title: "[Pain symptom.]" + detail: "[One or two sentences.]" + +# 02 -- Outcome first +outcomeFirst: + heading: "[Reframe around the outcome the buyer defines.]" + intro: "[Why there is no one-size-fits-all answer.]" + dimensions: + - label: "Operational" + title: "[Outcome.]" + detail: "[One or two sentences.]" + - label: "Financial" + title: "[Outcome.]" + detail: "[One or two sentences.]" + +# 03 -- Why this is important +whyItMatters: + heading: "[Why the gap compounds if left unaddressed.]" + intro: "[One framing sentence.]" + points: + - title: "[Reason.]" + detail: "[One or two sentences.]" + - title: "[Reason.]" + detail: "[One or two sentences.]" + +# 04 -- Why off-the-shelf / competition does not work +competition: + heading: "[Why generic tools fit this operation poorly.]" + intro: "[One framing sentence.]" + traps: + - label: "Trap 01" + title: "[Trap.]" + detail: "[One or two sentences.]" + - label: "Trap 02" + title: "[Trap.]" + detail: "[One or two sentences.]" + +# 05 -- With / without FlowFuse +comparison: + without: + - title: "[Status quo pain.]" + detail: "[One or two sentences.]" + with: + - title: "[FlowFuse capability.]" + detail: "[One or two sentences.]" + +# --------------------------------------------------------------- +# Legacy minimal fall-back (still supported). If you only set these +# and omit the rich blocks above, the layout renders a simple +# gap / workflow / outcomes skeleton instead. +# --------------------------------------------------------------- +# gap: +# - "[Paragraph.]" +# workflow: +# - "[Step.]" +# outcomes: +# - "[Outcome.]" ---