From 6d88a6668603fed677908cd811753ddfe572303f Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 22 Jun 2026 15:31:58 -0300 Subject: [PATCH 01/13] feat(onboarding): add the verify terminal (#7766) A prop-driven sdk console: amber LISTENING with an unchecked checklist and a blinking cursor while waiting, green LIVE with a connection receipt once connected. Always dark and reduced-motion aware, with stories for both states. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../onboarding/OnboardingTerminal.stories.tsx | 30 ++++ .../OnboardingTerminal.scss | 144 ++++++++++++++++++ .../OnboardingTerminal/OnboardingTerminal.tsx | 88 +++++++++++ .../onboarding/OnboardingTerminal/index.ts | 5 + 4 files changed, 267 insertions(+) create mode 100644 frontend/documentation/pages/onboarding/OnboardingTerminal.stories.tsx create mode 100644 frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.scss create mode 100644 frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.tsx create mode 100644 frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts diff --git a/frontend/documentation/pages/onboarding/OnboardingTerminal.stories.tsx b/frontend/documentation/pages/onboarding/OnboardingTerminal.stories.tsx new file mode 100644 index 000000000000..84af48f4006f --- /dev/null +++ b/frontend/documentation/pages/onboarding/OnboardingTerminal.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from 'storybook' + +import OnboardingTerminal from 'components/pages/onboarding/OnboardingTerminal' + +const meta: Meta = { + args: { + featureName: 'show_demo_button', + status: 'listening', + }, + component: OnboardingTerminal, + parameters: { + docs: { + description: { + component: + 'The onboarding verify console. Driven by `status`: amber LISTENING with an unchecked checklist and a blinking cursor while waiting, green LIVE with a connection receipt once the first evaluation arrives. Always dark, since a terminal reads the same in light and dark mode.', + }, + }, + layout: 'padded', + }, + title: 'Pages/Onboarding/OnboardingTerminal', +} +export default meta + +type Story = StoryObj + +export const Listening: Story = {} + +export const Connected: Story = { + args: { status: 'connected' }, +} diff --git a/frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.scss b/frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.scss new file mode 100644 index 000000000000..d68e989942f2 --- /dev/null +++ b/frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.scss @@ -0,0 +1,144 @@ +// The onboarding verify console. Always dark (a terminal reads the same in +// light and dark mode), so it uses the design's literal console palette rather +// than the theme tokens. Colours match the Pencil "sdk console" frame. +.onboarding-terminal { + overflow: hidden; + border-radius: 12px; + background: #0d1117; + font-family: var(--font-family-mono, monospace); + font-size: 13px; + line-height: 1.6; + + &__bar { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + background: #161b22; + } + + &__dot { + flex-shrink: 0; + width: 12px; + height: 12px; + border-radius: 999px; + + &--red { + background: #ff5f57; + } + &--amber { + background: #ffbd2e; + } + &--green { + background: #28c840; + } + } + + &__title { + color: #8b949e; + font-size: 12px; + } + + &__badge { + display: inline-flex; + align-items: center; + gap: 4px; + margin-left: auto; + padding: 3px 8px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.5px; + + &--listening { + background: #3d2b00; + color: #f0883e; + } + &--live { + background: #0d4429; + color: #3fb950; + } + } + + &__badge-dot { + width: 6px; + height: 6px; + border-radius: 999px; + background: currentColor; + } + + &__badge--listening &__badge-dot { + animation: onboarding-terminal-pulse 1.4s var(--easing-standard, ease-in-out) + infinite; + } + + &__body { + display: flex; + flex-direction: column; + gap: 8px; + padding: 20px 24px; + } + + // Default line = pending checklist grey. + &__line { + margin: 0; + color: #6e7781; + + &--ok { + color: #3fb950; + } + &--strong { + font-weight: 600; + } + &--dim { + color: #484f58; + } + &--muted { + color: #8b949e; + } + &--current { + color: #f0883e; + } + &--prompt { + color: #8b949e; + } + } + + // Blinking cursor block after the prompt - the "typewriter" cue. + &__cursor { + display: inline-block; + width: 8px; + height: 1em; + vertical-align: text-bottom; + background: #3fb950; + animation: onboarding-terminal-blink 1.1s steps(1) infinite; + } +} + +@keyframes onboarding-terminal-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.3; + } +} + +@keyframes onboarding-terminal-blink { + 0%, + 50% { + opacity: 1; + } + 50.01%, + 100% { + opacity: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + .onboarding-terminal__badge-dot, + .onboarding-terminal__cursor { + animation: none; + } +} diff --git a/frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.tsx b/frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.tsx new file mode 100644 index 000000000000..32f4a93968e7 --- /dev/null +++ b/frontend/web/components/pages/onboarding/OnboardingTerminal/OnboardingTerminal.tsx @@ -0,0 +1,88 @@ +import React, { FC } from 'react' +import classNames from 'classnames' +import './OnboardingTerminal.scss' + +export type OnboardingTerminalStatus = 'listening' | 'connected' + +export type OnboardingTerminalProps = { + status: OnboardingTerminalStatus + // The flag the SDK is expected to evaluate; drives the checklist and receipt. + featureName: string +} + +// Verify console for the onboarding flow, mirroring the design's "sdk console": +// amber LISTENING with an unchecked checklist and a blinking cursor while +// waiting, green LIVE with a connection receipt once the first evaluation +// arrives. Driven entirely by `status` (the real connection signal lands in +// #7767/#7623). Always dark - it's a terminal - so it carries its own palette +// rather than the theme tokens. +const OnboardingTerminal: FC = ({ + featureName, + status, +}) => { + const connected = status === 'connected' + return ( +
+
+ + + + + flagsmith — sdk console + + + + {connected ? 'LIVE' : 'LISTENING'} + +
+ +
+ {connected ? ( + <> +

+ [✓] Copy install command +

+

+ [✓] Copy code snippet +

+

+ [✓] First evaluation of '{featureName}' +

+

+ SDK initialized · flags loaded · {featureName}: true +

+

+ ✓ Connected +

+

+ ✓ {featureName} is live +

+ + ) : ( + <> +

+ awaiting first request +

+

+ [ ] Copy install command +

+

[ ] Copy code snippet

+

+ [ ] First evaluation of '{featureName}'… +

+

+ $ +

+ + )} +
+
+ ) +} + +export default OnboardingTerminal diff --git a/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts b/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts new file mode 100644 index 000000000000..4b5c4ac288a4 --- /dev/null +++ b/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts @@ -0,0 +1,5 @@ +export { default } from './OnboardingTerminal' +export type { + OnboardingTerminalProps, + OnboardingTerminalStatus, +} from './OnboardingTerminal' From d1925752d9ffda1ea4399d5826c11eb6f157ae9b Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 22 Jun 2026 15:32:00 -0300 Subject: [PATCH 02/13] feat(onboarding): add the flags table (#7766) The 'Your flags' card reusing the product FeatureName / Tag / Switch, prop driven with waiting and connected states. Connected lifts it with the accent border and glow; waiting dims it. Stories cover the states and the toggle. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../OnboardingFlagsTable.stories.tsx | 49 +++++++++++ .../OnboardingFlagsTable.scss | 88 +++++++++++++++++++ .../OnboardingFlagsTable.tsx | 82 +++++++++++++++++ .../onboarding/OnboardingFlagsTable/index.ts | 6 ++ 4 files changed, 225 insertions(+) create mode 100644 frontend/documentation/pages/onboarding/OnboardingFlagsTable.stories.tsx create mode 100644 frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss create mode 100644 frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.tsx create mode 100644 frontend/web/components/pages/onboarding/OnboardingFlagsTable/index.ts diff --git a/frontend/documentation/pages/onboarding/OnboardingFlagsTable.stories.tsx b/frontend/documentation/pages/onboarding/OnboardingFlagsTable.stories.tsx new file mode 100644 index 000000000000..bf37298058af --- /dev/null +++ b/frontend/documentation/pages/onboarding/OnboardingFlagsTable.stories.tsx @@ -0,0 +1,49 @@ +import type { Meta, StoryObj } from 'storybook' + +import OnboardingFlagsTable, { + OnboardingFlagRow, +} from 'components/pages/onboarding/OnboardingFlagsTable' + +const demoFlag: OnboardingFlagRow = { + description: 'Controls the demo button shown to your users', + enabled: true, + name: 'show_demo_button', +} + +const meta: Meta = { + args: { + flags: [demoFlag], + onToggle: () => {}, + status: 'connected', + }, + component: OnboardingFlagsTable, + parameters: { + docs: { + description: { + component: + 'The "Your flags" card from the onboarding flow, reusing the product FeatureName / Tag / Switch. Prop-driven: the page owns the flag data and the persisted Dev toggle. `connected` lifts the card with the accent border and glow; `waiting` dims it until the first evaluation arrives.', + }, + }, + layout: 'padded', + }, + title: 'Pages/Onboarding/OnboardingFlagsTable', +} +export default meta + +type Story = StoryObj + +export const Connected: Story = {} + +export const Waiting: Story = { + args: { status: 'waiting' }, +} + +export const Off: Story = { + args: { flags: [{ ...demoFlag, enabled: false }] }, +} + +export const WithTag: Story = { + args: { + flags: [{ ...demoFlag, tags: [{ color: '#6837FC', label: 'demo' }] }], + }, +} diff --git a/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss new file mode 100644 index 000000000000..2c3f0f2cada8 --- /dev/null +++ b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss @@ -0,0 +1,88 @@ +// "Your flags" card. A normal light/dark surface (unlike the terminal), so it +// uses the semantic tokens. Connected lifts it with the action/purple border + +// glow to draw the eye; waiting dims it until the first evaluation lands. +.onboarding-flags { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + + &__title { + margin: 0; + color: var(--color-text-default); + font-size: 16px; + font-weight: 700; + } + + &__table { + width: 100%; + max-width: 760px; + overflow: hidden; + border-radius: 12px; + background: var(--color-surface-default); + border: 1px solid var(--color-border-action); + box-shadow: var(--shadow-lg); + transition: opacity var(--duration-fast) var(--easing-standard), + box-shadow var(--duration-fast) var(--easing-standard), + border-color var(--duration-fast) var(--easing-standard); + + // Until the SDK connects the flag is real but quiet: no accent, dimmed. + &--waiting { + border-color: var(--color-border-default); + box-shadow: none; + opacity: 0.7; + } + } + + &__head { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 20px; + border-bottom: 1px solid var(--color-border-default); + } + + &__col { + color: var(--color-text-secondary); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.5px; + + &--feature { + flex: 1; + } + &--enabled { + width: 96px; + } + } + + &__row { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 20px; + } + + &__feature { + display: flex; + flex: 1; + flex-direction: column; + gap: 4px; + } + + &__name-row { + display: flex; + align-items: center; + gap: 8px; + } + + &__desc { + margin: 0; + color: var(--color-text-secondary); + font-size: 12px; + } + + &__toggle { + width: 56px; + } +} diff --git a/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.tsx b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.tsx new file mode 100644 index 000000000000..b10cc80f3821 --- /dev/null +++ b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.tsx @@ -0,0 +1,82 @@ +import React, { FC } from 'react' +import classNames from 'classnames' +import { Tag as TTag } from 'common/types/responses' +import FeatureName from 'components/feature-summary/FeatureName' +import Tag from 'components/tags/Tag' +import Switch from 'components/Switch' +import './OnboardingFlagsTable.scss' + +export type OnboardingFlagsTableStatus = 'waiting' | 'connected' + +export type OnboardingFlagRow = { + name: string + description?: string + tags?: Partial[] + enabled: boolean +} + +export type OnboardingFlagsTableProps = { + status: OnboardingFlagsTableStatus + flags: OnboardingFlagRow[] + onToggle: (flag: OnboardingFlagRow, enabled: boolean) => void + // Name of the flag whose toggle is mid-flight, so its Switch disables. + togglingFlag?: string | null +} + +// The "Your flags" card from the onboarding design: the pre-created flag(s) in a +// real-looking table that reuses the product FeatureName / Tag / Switch. Prop +// driven (the page owns the data and the persisted toggle, see +// useUpdateFeatureStateMutation). `connected` lifts the card with the accent +// border + glow and enables the toggle; `waiting` dims it until the first +// evaluation arrives. +const OnboardingFlagsTable: FC = ({ + flags, + onToggle, + status, + togglingFlag, +}) => { + const waiting = status === 'waiting' + return ( +
+

Your flags

+
+
+ + FEATURE + + + ENABLED + +
+ {flags.map((flag) => ( +
+
+
+ + {flag.tags?.map((tag) => ( + + ))} +
+ {flag.description && ( +

{flag.description}

+ )} +
+
+ onToggle(flag, enabled)} + /> +
+
+ ))} +
+
+ ) +} + +export default OnboardingFlagsTable diff --git a/frontend/web/components/pages/onboarding/OnboardingFlagsTable/index.ts b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/index.ts new file mode 100644 index 000000000000..435ba4d35415 --- /dev/null +++ b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/index.ts @@ -0,0 +1,6 @@ +export { default } from './OnboardingFlagsTable' +export type { + OnboardingFlagRow, + OnboardingFlagsTableProps, + OnboardingFlagsTableStatus, +} from './OnboardingFlagsTable' From cdd1cb7f6154c9e13c8730d956d189f8f4c44b04 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 22 Jun 2026 15:32:02 -0300 Subject: [PATCH 03/13] feat(onboarding): render terminal + flags table with a real Dev toggle (#7766) OnboardingFlow renders both below the connect panel. useOnboardingFlag resolves the demo flag's Development feature state and toggles it via updateFeatureState (persisted, no simulated output). Status is the pre-connection state for now; the real first-evaluation signal lands in #7767. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../OnboardingFlow/OnboardingFlow.tsx | 32 ++++++++++++- .../onboarding/hooks/useOnboardingFlag.ts | 48 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 frontend/web/components/pages/onboarding/hooks/useOnboardingFlag.ts diff --git a/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx b/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx index ab5c26ea3354..5a3d696b75a4 100644 --- a/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx +++ b/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx @@ -5,8 +5,15 @@ import Icon from 'components/icons/Icon' import OnboardingHeader from 'components/pages/onboarding/OnboardingHeader' import ThemeToggle from 'components/pages/onboarding/ThemeToggle' import OnboardingConnectPanel from 'components/pages/onboarding/OnboardingConnectPanel' +import OnboardingTerminal, { + OnboardingTerminalStatus, +} from 'components/pages/onboarding/OnboardingTerminal' +import OnboardingFlagsTable, { + OnboardingFlagsTableStatus, +} from 'components/pages/onboarding/OnboardingFlagsTable' import { useEnsureOnboardingResources } from 'components/pages/onboarding/hooks/useEnsureOnboardingResources' import { useOnboardingFlagRename } from 'components/pages/onboarding/hooks/useOnboardingFlagRename' +import { useOnboardingFlag } from 'components/pages/onboarding/hooks/useOnboardingFlag' import { useUpdateOrganisationMutation } from 'common/services/useOrganisation' import { useUpdateProjectMutation } from 'common/services/useProject' import './OnboardingFlow.scss' @@ -16,7 +23,7 @@ import './OnboardingFlow.scss' // // Resources (org / project / Dev + Prod / first flag) are bootstrapped // idempotently by useEnsureOnboardingResources, and the inline header chips -// persist renames. TODO(#7766): the verify console / flags table land on top. +// persist renames; the verify terminal and flags table render below (#7766). const OnboardingFlow: FC = () => { const { caseSensitive, @@ -59,6 +66,16 @@ const OnboardingFlow: FC = () => { projectId, }) + // Verify terminal + flags table. They stay in the pre-connection state until + // the first-evaluation signal lands (#7767); the Dev toggle is real now. + const terminalStatus: OnboardingTerminalStatus = 'listening' + const flagsStatus: OnboardingFlagsTableStatus = 'waiting' + const { + enabled: flagEnabled, + isToggling, + toggle: toggleFlag, + } = useOnboardingFlag(environment, projectId, featureName) + // Org/project are single-field PATCHes; the shell nav adopts the new names on // its next load. const renameOrganisation = async (name: string) => { @@ -150,6 +167,19 @@ const OnboardingFlow: FC = () => { environmentKey={environmentKey} featureName={featureName} /> + + toggleFlag(next)} + togglingFlag={isToggling ? featureName : null} + />
diff --git a/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx b/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx index d7d131b6a6e5..09c7189196aa 100644 --- a/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx +++ b/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx @@ -63,10 +63,13 @@ const OnboardingFlow: FC = () => { projectId, }) - // Verify terminal + flags table. The connection status is stubbed until the + // Verify terminal + flags table. The checklist ticks as the user copies the + // install / wire snippets; the connection step is stubbed until the // first-evaluation signal lands (#7767, behind useOnboardingConnection); the // Dev toggle is real now. const connection = useOnboardingConnection() + const [installCopied, setInstallCopied] = useState(false) + const [snippetCopied, setSnippetCopied] = useState(false) const { enabled: flagEnabled, isToggling, @@ -164,8 +167,15 @@ const OnboardingFlow: FC = () => { setInstallCopied(true)} + onCopyWire={() => setSnippetCopied(true)} + /> + - = ({ + connected, featureName, - status, + installCopied, + snippetCopied, }) => { - const connected = status === 'connected' + const steps = [ + { done: installCopied, label: 'Copy install command' }, + { done: snippetCopied, label: 'Copy code snippet' }, + { done: connected, label: `First evaluation of '${featureName}'` }, + ] + // The first unfinished step is the active one (amber). + const currentIndex = steps.findIndex((step) => !step.done) + return (
@@ -42,17 +50,26 @@ const OnboardingTerminal: FC = ({
+ {!connected && ( +

+ awaiting first request +

+ )} + {steps.map((step, index) => ( +

+ {step.done ? '[✓]' : '[ ]'} {step.label} + {!step.done && index === steps.length - 1 ? '…' : ''} +

+ ))} {connected ? ( <> -

- [✓] Copy install command -

-

- [✓] Copy code snippet -

-

- [✓] First evaluation of '{featureName}' -

SDK initialized · flags loaded · {featureName}: true

@@ -64,21 +81,9 @@ const OnboardingTerminal: FC = ({

) : ( - <> -

- awaiting first request -

-

- [ ] Copy install command -

-

[ ] Copy code snippet

-

- [ ] First evaluation of '{featureName}'… -

-

- $ -

- +

+ $ +

)}
diff --git a/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts b/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts index 4b5c4ac288a4..38296d63455e 100644 --- a/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts +++ b/frontend/web/components/pages/onboarding/OnboardingTerminal/index.ts @@ -1,5 +1,2 @@ export { default } from './OnboardingTerminal' -export type { - OnboardingTerminalProps, - OnboardingTerminalStatus, -} from './OnboardingTerminal' +export type { OnboardingTerminalProps } from './OnboardingTerminal' From fe9913b7ef7814979b9d2ab5f33a4ecd84e4b4b5 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 22 Jun 2026 16:50:25 -0300 Subject: [PATCH 09/13] style(onboarding): align the flags toggle under the ENABLED header (#7766) The toggle cell was 56px while the ENABLED header column is 96px (the design's 40px action cell filled the gap, which we dropped), so the toggle sat right of the header. Widen the toggle cell to match, lining it up under ENABLED. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss index 3e08e0203046..3af80a41dbc8 100644 --- a/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss +++ b/frontend/web/components/pages/onboarding/OnboardingFlagsTable/OnboardingFlagsTable.scss @@ -85,7 +85,8 @@ font-size: 12px; } + // Match the ENABLED header column width so the toggle lines up under it. &__toggle { - width: 56px; + width: 96px; } } From 96e125e29677fb2a214599e4fe80ceb131de9330 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 23 Jun 2026 10:18:07 -0300 Subject: [PATCH 10/13] refactor(onboarding): use a tag-palette colour for the Onboarding tag (#7766) The Onboarding tag's colour was an off-palette hex; switch to a green from the product tag palette (Constants.tagColors) so it's consistent with how tags are coloured everywhere else. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/pages/onboarding/hooks/bootstrapOnboarding.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/web/components/pages/onboarding/hooks/bootstrapOnboarding.ts b/frontend/web/components/pages/onboarding/hooks/bootstrapOnboarding.ts index f03e16590087..4e0e57d72dd1 100644 --- a/frontend/web/components/pages/onboarding/hooks/bootstrapOnboarding.ts +++ b/frontend/web/components/pages/onboarding/hooks/bootstrapOnboarding.ts @@ -27,8 +27,9 @@ const DEFAULT_PROJECT_NAME = 'My first project' const DEV_ENVIRONMENT_NAME = 'Development' const PROD_ENVIRONMENT_NAME = 'Production' // Attached to the demo flag so the flags table shows an "Onboarding" badge. +// Green from the product tag palette (Constants.tagColors), not an arbitrary hex. const ONBOARDING_TAG = { - color: '#057A55', + color: '#3cb371', description: 'Created during onboarding', label: 'Onboarding', } From d2474f99f76fee37c65049b6c87f23aa72f8eb0b Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 23 Jun 2026 16:26:46 -0300 Subject: [PATCH 11/13] fix(forms): disable browser autofill on GhostInput Chrome offered contact autofill on the ghost inline-edit field, dropping a person/chevron icon that overlapped the trailing edit pencil. A ghost field is never a contact or credential input, so opt out of autofill and the password-manager overlays (1Password, LastPass). Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/web/components/base/forms/GhostInput/GhostInput.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/web/components/base/forms/GhostInput/GhostInput.tsx b/frontend/web/components/base/forms/GhostInput/GhostInput.tsx index 2b4b7b381f04..8b1128702a5a 100644 --- a/frontend/web/components/base/forms/GhostInput/GhostInput.tsx +++ b/frontend/web/components/base/forms/GhostInput/GhostInput.tsx @@ -63,6 +63,11 @@ const GhostInput = ({ onKeyDown={onKeyDown} aria-label={ariaLabel} spellCheck={false} + // Opt out of browser autofill + password-manager overlays (1Password, + // LastPass); their icons would overlap the trailing edit pencil. + autoComplete='off' + data-1p-ignore + data-lpignore='true' style={{ width: inputWidth }} /> From 63459ee28e9c6cfc69ae7cd926d07d8a1db1516b Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 23 Jun 2026 17:32:26 -0300 Subject: [PATCH 12/13] fix(onboarding): toast on flag toggle failure The flags-table toggle fired updateFeatureState fire-and-forget, so a failed toggle just snapped back silently on the next refetch. Await + unwrap and toast on failure, matching the header rename UX. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../onboarding/hooks/useOnboardingFlag.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/frontend/web/components/pages/onboarding/hooks/useOnboardingFlag.ts b/frontend/web/components/pages/onboarding/hooks/useOnboardingFlag.ts index 9969d634577c..dcec3e11a825 100644 --- a/frontend/web/components/pages/onboarding/hooks/useOnboardingFlag.ts +++ b/frontend/web/components/pages/onboarding/hooks/useOnboardingFlag.ts @@ -38,15 +38,22 @@ export const useOnboardingFlag = ( const [updateFeatureState, { isLoading }] = useUpdateFeatureStateMutation() - const toggle = (enabled: boolean) => { + // Persisted toggle. Not optimistic: the Switch reflects the RTK-cached state + // and is disabled mid-flight, so a failure just leaves the old value. Toast on + // failure, matching the header rename UX, rather than failing silently. + const toggle = async (enabled: boolean) => { if (!environment || !state) { return } - updateFeatureState({ - body: { enabled }, - environmentFlagId: state.id, - environmentId: environment.api_key, - }) + try { + await updateFeatureState({ + body: { enabled }, + environmentFlagId: state.id, + environmentId: environment.api_key, + }).unwrap() + } catch { + toast('Couldn’t update your flag. Please try again.', 'danger') + } } return { From 815647d978806bd41be4170da5ef66c01d80fd77 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 23 Jun 2026 17:32:29 -0300 Subject: [PATCH 13/13] docs(onboarding): note the verify checklist is session-only Co-Authored-By: Claude Opus 4.8 (1M context) --- .../pages/onboarding/OnboardingFlow/OnboardingFlow.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx b/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx index 09c7189196aa..068e3609df12 100644 --- a/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx +++ b/frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx @@ -68,6 +68,8 @@ const OnboardingFlow: FC = () => { // first-evaluation signal lands (#7767, behind useOnboardingConnection); the // Dev toggle is real now. const connection = useOnboardingConnection() + // Session-only and one-way: a reload resets them, so the checklist reflects + // what the user did this visit, not durable progress. Fine for onboarding. const [installCopied, setInstallCopied] = useState(false) const [snippetCopied, setSnippetCopied] = useState(false) const {