diff --git a/src/js/components/ManageMenu/ManageMenu.tsx b/src/js/components/ManageMenu/ManageMenu.tsx index 064ee9a94..48486f293 100644 --- a/src/js/components/ManageMenu/ManageMenu.tsx +++ b/src/js/components/ManageMenu/ManageMenu.tsx @@ -3,12 +3,8 @@ import { __ } from '@wordpress/i18n' import { createInterpolateElement } from '@wordpress/element' import { fetchConstQueryParam, fetchQueryParam, updateQueryParams } from '../../utils/urls' import { DismissibleNotice, type NoticeType } from '../common/Notice' -import { SUBPAGES, Toolbar } from '../common/Toolbar' -import { AiAgentDemo } from './AiAgentDemo/AiAgentDemo' -import { BlueprintsDemo } from './BlueprintsDemo/BlueprintsDemo' -import { CloudLibraryDemo } from './CloudLibraryDemo/CloudLibraryDemo' -import { CommunityCloud } from './CommunityCloud/CommunityCloud' -import { SnippetsTable } from './SnippetsTable' +import { Toolbar } from '../common/Toolbar' +import { SUBPAGES, SUBPAGE_ENTRIES, type SubpageName } from './subpages' const repositionTableOptionsSettings = () => { const screenOptionsForm = document.getElementById('adv-settings') @@ -71,26 +67,13 @@ const PageNotices = () => { } interface PageContentParams { - subpage: typeof SUBPAGES[number] + subpage: SubpageName } const PageContent: React.FC = ({ subpage }) => { - switch (subpage) { - case 'snippets': - return + const { Component } = SUBPAGE_ENTRIES[subpage] - case 'cloud-community': - return - - case 'ai-agent': - return - - case 'blueprints': - return - - case 'cloud-library': - return - } + return } export const ManageMenu = () => { diff --git a/src/js/components/ManageMenu/subpages.tsx b/src/js/components/ManageMenu/subpages.tsx new file mode 100644 index 000000000..a052b744d --- /dev/null +++ b/src/js/components/ManageMenu/subpages.tsx @@ -0,0 +1,41 @@ +import { AiAgentDemo } from './AiAgentDemo/AiAgentDemo' +import { BlueprintsDemo } from './BlueprintsDemo/BlueprintsDemo' +import { CloudLibraryDemo } from './CloudLibraryDemo/CloudLibraryDemo' +import { CommunityCloud } from './CommunityCloud/CommunityCloud' +import { SnippetsTable } from './SnippetsTable' +import type React from 'react' + +export const SUBPAGES = ['snippets', 'blueprints', 'cloud-community', 'cloud-library', 'ai-agent'] as const + +export type SubpageName = typeof SUBPAGES[number] + +export interface Subpage { + Component: React.FC + + /** + * Marks a walkthrough tab. An 'announce' tab advertises itself as new until + * its walkthrough has been watched, which only a tab with a recorded demo + * can do; a 'quiet' tab is only ever labelled as a demo. + */ + demo?: 'announce' | 'quiet' + + /** + * Marks a premium tab, which is chipped as such without a licence. + */ + isPro?: boolean +} + +/** + * What each subpage of the manage screen renders, and how its toolbar tab + * advertises itself. + * + * The toolbar and the page body read the same entry, so a subpage is described + * once here rather than being kept in step across the two. + */ +export const SUBPAGE_ENTRIES: Record = { + 'snippets': { Component: SnippetsTable }, + 'blueprints': { Component: BlueprintsDemo, demo: 'announce' }, + 'cloud-community': { Component: CommunityCloud }, + 'cloud-library': { Component: CloudLibraryDemo, demo: 'quiet' }, + 'ai-agent': { Component: AiAgentDemo, demo: 'announce' }, +} diff --git a/src/js/components/common/Toolbar.tsx b/src/js/components/common/Toolbar.tsx index 9504a1e9b..3912ff783 100644 --- a/src/js/components/common/Toolbar.tsx +++ b/src/js/components/common/Toolbar.tsx @@ -2,7 +2,8 @@ import { __, _x } from '@wordpress/i18n' import classnames from 'classnames' import React, { useMemo, useState } from 'react' import { isLicensed, shouldShowUpsell } from '../../utils/screen' -import { buildUrl } from '../../utils/urls' +import { buildUrl, fetchConstQueryParam } from '../../utils/urls' +import { SUBPAGES, SUBPAGE_ENTRIES, type SubpageName } from '../ManageMenu/subpages' import { hasSeenDemo } from './demo/useDemoSeen' import { AiAgentIcon } from './icons/AiAgentIcon' import { BlueprintIcon } from './icons/BlueprintIcon' @@ -11,13 +12,21 @@ import { LibraryIcon } from './icons/LibraryIcon' import { SettingsIcon } from './icons/SettingsIcon' import { SnippetsIcon } from './icons/SnippetsIcon' import { UpsellDialog } from './UpsellDialog' -import type { DemoName } from './demo/useDemoSeen' import type { SVGProps } from 'react' -export const SUBPAGES = ['snippets', 'blueprints', 'cloud-community', 'cloud-library', 'ai-agent'] as const - const searchParams = new URLSearchParams(window.location.search) +const managePageSlug = window.CODE_SNIPPETS?.urls.manage + ? new URL(window.CODE_SNIPPETS.urls.manage).searchParams.get('page') + : null + +// The manage screen resolves an absent or unrecognised `subpage` to the first subpage, so +// mirror that fallback here — comparing against the raw param would leave every lower-nav +// tab inactive on the default Snippets view, which is where the page opens. +const activeSubpage = searchParams.get('page') === managePageSlug + ? fetchConstQueryParam('subpage', SUBPAGES) ?? SUBPAGES[0] + : null + interface UpperNavItemProps { name: string url: string @@ -133,31 +142,21 @@ const UpperNav: React.FC = () => { ) } -interface SubpageItemBaseProps { +interface SubpageItemProps { + subpage: SubpageName label: string Icon: React.FC> - isPro?: boolean } -/** - * Marks a walkthrough tab. New features announce themselves until the - * walkthrough has been watched, which only a tab with a recorded demo can do; - * existing ones are only ever labelled as a demo. - */ -type SubpageItemDemoProps = - | { subpage: DemoName, demo: 'announce' } - | { subpage: typeof SUBPAGES[number], demo?: 'quiet' } - -export type SubpageItemProps = SubpageItemBaseProps & SubpageItemDemoProps - -const SubpageItem: React.FC = ({ subpage, label, Icon, isPro, demo }) => { +const SubpageItem: React.FC = ({ subpage, label, Icon }) => { + const { demo, isPro } = SUBPAGE_ENTRIES[subpage] const isNew = 'announce' === demo && !hasSeenDemo(subpage) return (