From a153b77fa597ca351ac6702b40ac27c6ac12eb67 Mon Sep 17 00:00:00 2001 From: Jeremy Ruppel Date: Tue, 30 Jun 2026 12:02:06 -0400 Subject: [PATCH] feat(site/src/pages/TemplateBuilder): add alternatives box to base template step (#26878) Adds an "Alternatives to create a template" section below the navigation controls on the base infrastructure selection step of the Template Builder wizard. The box is only shown on the first step and contains four outline buttons linking to other template creation paths: - **Start from scratch** -> Coder docs tutorial (external) - **Upload an existing template** -> `/starter-templates` (internal) - **Browse community templates** -> Registry templates (external) - **Use template agent skill** -> Registry skills (external) External links open in a new tab with an external-link icon. Closes [DEVEX-564](https://linear.app/codercom/issue/DEVEX-564) > [!NOTE] > This PR was authored by Coder Agents on behalf of @jeremyruppel.
Implementation plan ### 1. `TemplateAlternatives.tsx` (new) - Presentational component rendering a bordered container with label and four `Button` components (variant `outline`, size `sm`) - External links use `ExternalLinkIcon` from lucide-react and open in `_blank` - Internal link uses React Router `Link` ### 2. `TemplateBuilderPageView.tsx` (modified) - Import and render `` below navigation controls, conditionally when `currentStep.id === "base-infra"` ### 3. `TemplateAlternatives.stories.tsx` (new) - Default Storybook story for the component
--- .../TemplateAlternatives.stories.tsx | 12 ++++ .../TemplateBuilder/TemplateAlternatives.tsx | 59 +++++++++++++++++++ .../TemplateBuilderPageView.tsx | 3 + 3 files changed, 74 insertions(+) create mode 100644 site/src/pages/TemplateBuilder/TemplateAlternatives.stories.tsx create mode 100644 site/src/pages/TemplateBuilder/TemplateAlternatives.tsx diff --git a/site/src/pages/TemplateBuilder/TemplateAlternatives.stories.tsx b/site/src/pages/TemplateBuilder/TemplateAlternatives.stories.tsx new file mode 100644 index 0000000000..ee98c427d7 --- /dev/null +++ b/site/src/pages/TemplateBuilder/TemplateAlternatives.stories.tsx @@ -0,0 +1,12 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { TemplateAlternatives } from "./TemplateAlternatives"; + +const meta: Meta = { + title: "pages/TemplateBuilder/TemplateAlternatives", + component: TemplateAlternatives, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/site/src/pages/TemplateBuilder/TemplateAlternatives.tsx b/site/src/pages/TemplateBuilder/TemplateAlternatives.tsx new file mode 100644 index 0000000000..b1c2d3139d --- /dev/null +++ b/site/src/pages/TemplateBuilder/TemplateAlternatives.tsx @@ -0,0 +1,59 @@ +import { ExternalLinkIcon } from "lucide-react"; +import type { FC } from "react"; +import { Link } from "react-router"; +import { Button } from "#/components/Button/Button"; + +interface AlternativeLink { + label: string; + href: string; + external: boolean; +} + +const alternatives: readonly AlternativeLink[] = [ + { + label: "Start from scratch", + href: "https://coder.com/docs/tutorials/template-from-scratch", + external: true, + }, + { + label: "Upload an existing template", + href: "/starter-templates", + external: false, + }, + { + label: "Browse community templates", + href: "https://registry.coder.com/templates", + external: true, + }, + { + label: "Use template agent skill", + href: "https://registry.coder.com/skills", + external: true, + }, +]; + +export const TemplateAlternatives: FC = () => { + return ( +
+

+ Alternatives to create a template: +

+
+ {alternatives.map((alt) => + alt.external ? ( + + ) : ( + + ), + )} +
+
+ ); +}; diff --git a/site/src/pages/TemplateBuilder/TemplateBuilderPageView.tsx b/site/src/pages/TemplateBuilder/TemplateBuilderPageView.tsx index 728a12094d..73b098b6e9 100644 --- a/site/src/pages/TemplateBuilder/TemplateBuilderPageView.tsx +++ b/site/src/pages/TemplateBuilder/TemplateBuilderPageView.tsx @@ -34,6 +34,7 @@ import { type StepId, WIZARD_STEPS, } from "./steps"; +import { TemplateAlternatives } from "./TemplateAlternatives"; import { TemplateCustomizationsStep } from "./TemplateCustomizationsStep"; import { initialWizardState, @@ -152,6 +153,8 @@ export const TemplateBuilderPageView: FC = ({ {isLastStep ? "Create Template" : "Continue"} + + {currentStep.id === "base-infra" && } {/* Sidebar */}