diff --git a/README.md b/README.md index e49508368..56b119668 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,24 @@ The Platform documentation is versioned and lives in the `platform-enterprise_ve We have a script which can select a commit (or ideally release tag) to be used for publishing a new version on the docs website. +### Build modes + +The site build is controlled by `DOCS_SITE_MODE`: + +- `main` builds `docs.seqera.io` +- `enterprise-archive` builds `docs-archive.seqera.io` + +The main site serves the current Platform Enterprise docs (`24.1+`) together with the active product docs. The archive site serves legacy Platform Enterprise versions (`23.4`, `23.3`, `23.2`, and `23.1`) only. + +For local archive preview and builds: + +- `DOCS_SITE_MODE=enterprise-archive npm run dev` +- `npm run dev:archive` +- `DOCS_SITE_MODE=enterprise-archive npm run build` +- `npm run build:archive` + +In archive mode, the startup/build preparation step skips fetching the external OSS doc repos because they are not required for the legacy Enterprise-only site. + ## Write and edit content ### Install pre-commit @@ -168,8 +186,17 @@ This works using the following principles: - Based on the presence or absence of named environment variables, they are included in the Docuaurus config or not - By defining these ENV vars in your build environment, you can selectively skip chunks in the build -Deployment works because we have two Netlify sites: `seqera-docs` and `seqera-docs-api`. -They're the same except that they have different environment variable set in their configuration. -This means that whenever you push to `master`, both deploy and both sites update. +Deployment works because we split large docsets into separate Netlify sites with different build-time configuration. + +Today this includes: + +- `seqera-docs` for the main docs site +- `seqera-docs-api` for the API docs split +- `docs-archive.seqera.io` for legacy Platform Enterprise docs + +The main site's `netlify.toml` includes redirects for these split deployments: + +- API docs use `200` proxy-style redirects so they continue to resolve under `docs.seqera.io` +- Legacy Platform Enterprise docs use cross-domain `301` redirects so old versioned URLs canonicalize to `docs-archive.seqera.io` -The site's `netlify.toml` includes some redirects with `200` statuses that take links to missing content on the primary deployment to fetch data from the secondary deployment, without affecting the browser bar URL. +The archive deployment is intended to be mostly frozen. Normal `master` changes should not rebuild it. If a legacy Enterprise fix is required, apply that change to the dedicated archive branch/site rather than treating it like a normal main-site update. diff --git a/docusaurus.config.js b/docusaurus.config.js index 75f67728e..c9f0d2fdc 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -1,6 +1,4 @@ -const path = require("path"); import "dotenv/config"; -import platform_enterprise_latest_version from "./platform-enterprise_latest_version.js"; import { createSeqeraConfig, getSeqeraThemeConfig, @@ -8,6 +6,65 @@ import { } from "@seqera/docusaurus-preset-seqera"; export default async function createConfigAsync() { + const DOCS_SITE_MODE = process.env.DOCS_SITE_MODE ?? "main"; + const MAIN_SITE_MODE = "main"; + const ENTERPRISE_ARCHIVE_SITE_MODE = "enterprise-archive"; + const isEnterpriseArchiveSite = + DOCS_SITE_MODE === ENTERPRISE_ARCHIVE_SITE_MODE; + + const enterpriseVersionsBySiteMode = { + [MAIN_SITE_MODE]: ["25.3", "25.2", "25.1", "24.2", "24.1"], + [ENTERPRISE_ARCHIVE_SITE_MODE]: ["23.4", "23.3", "23.2", "23.1"], + }; + const activeEnterpriseVersions = + enterpriseVersionsBySiteMode[DOCS_SITE_MODE] ?? + enterpriseVersionsBySiteMode[MAIN_SITE_MODE]; + const activeEnterpriseLastVersion = activeEnterpriseVersions[0]; + const docsSiteUrl = isEnterpriseArchiveSite + ? "https://docs-archive.seqera.io" + : "https://docs.seqera.io"; + const supportedEnterpriseDocsUrl = + "https://docs.seqera.io/platform-enterprise/"; + const legacyEnterpriseArchiveUrl = + "https://docs-archive.seqera.io/platform-enterprise/"; + + const mainNavbarItems = [ + { + label: "Cloud", + href: "/platform-cloud/", + }, + { + label: "Enterprise", + href: "/platform-enterprise/", + }, + { + label: "Nextflow", + href: "/nextflow/", + }, + { + label: "MultiQC", + href: "/multiqc/", + }, + { + label: "Wave", + href: "/wave/", + }, + { + label: "Fusion", + href: "/fusion/", + }, + ]; + + const archiveNavbarItems = [ + { + label: "Enterprise", + href: "/platform-enterprise/", + }, + { + label: "Supported versions", + href: supportedEnterpriseDocsUrl, + }, + ]; const changelog = { blogTitle: "Seqera Changelog", @@ -35,8 +92,10 @@ export default async function createConfigAsync() { routeBasePath: "/platform-enterprise", path: "platform-enterprise_docs", // For PR Previews we want to see the latest doc-set with expected changes. - includeCurrentVersion: process.env.INCLUDE_NEXT ? true : false, - lastVersion: platform_enterprise_latest_version, + includeCurrentVersion: + !isEnterpriseArchiveSite && process.env.INCLUDE_NEXT ? true : false, + lastVersion: activeEnterpriseLastVersion, + onlyIncludeVersions: activeEnterpriseVersions, remarkPlugins: [ (await import("remark-code-import")).default, (await require("remark-math")).default, @@ -156,6 +215,7 @@ export default async function createConfigAsync() { ]; console.log( + "\n DOCS_SITE_MODE: " + DOCS_SITE_MODE, "\n EXCLUDE_CHANGELOG: " + (process.env.EXCLUDE_CHANGELOG ? true : false), "\n EXCLUDE_PLATFORM_ENTERPRISE: " + (process.env.EXCLUDE_PLATFORM_ENTERPRISE ? true : false), @@ -171,12 +231,16 @@ export default async function createConfigAsync() { "\n EXCLUDE_FUSION: " + (process.env.EXCLUDE_FUSION ? true : false), "\n EXCLUDE_WAVE: " + (process.env.EXCLUDE_WAVE ? true : false), "\n INCLUDE_NEXT: " + (process.env.INCLUDE_NEXT ? true : false), + "\n ACTIVE_PLATFORM_ENTERPRISE_VERSIONS: " + + activeEnterpriseVersions.join(", "), ); return createSeqeraConfig({ - title: "Seqera Docs", - tagline: "Documentation for Seqera products", - url: "https://docs.seqera.io", + title: isEnterpriseArchiveSite ? "Seqera Docs Archive" : "Seqera Docs", + tagline: isEnterpriseArchiveSite + ? "Archived documentation for Seqera Platform Enterprise" + : "Documentation for Seqera products", + url: docsSiteUrl, /* * Enable faster Docusaurus optimizations (experimental v4 features) * Reference: https://github.com/facebook/docusaurus/issues/10556 @@ -218,12 +282,17 @@ export default async function createConfigAsync() { }, customFields: { - // Put your custom environment here + docsSiteMode: DOCS_SITE_MODE, + activeEnterpriseVersions, + supportedEnterpriseDocsUrl, + legacyEnterpriseArchiveUrl, }, clientModules: [ - require.resolve('./src/client-modules/cross-site-nav.js'), - require.resolve('./src/client-modules/posthog-search.js'), + require.resolve("./src/client-modules/cross-site-nav.js"), + ...(isEnterpriseArchiveSite + ? [] + : [require.resolve("./src/client-modules/posthog-search.js")]), ], @@ -231,12 +300,16 @@ export default async function createConfigAsync() { [ "@seqera/docusaurus-preset-seqera", await getSeqeraPresetOptions({ - blog: process.env.EXCLUDE_CHANGELOG ? false : changelog, + blog: + isEnterpriseArchiveSite || process.env.EXCLUDE_CHANGELOG + ? false + : changelog, docs: false, theme: { customCss: require.resolve("./src/custom.css"), }, - openapi: process.env.EXCLUDE_PLATFORM_OPENAPI + openapi: + isEnterpriseArchiveSite || process.env.EXCLUDE_PLATFORM_OPENAPI ? false : { id: "api", @@ -262,13 +335,23 @@ export default async function createConfigAsync() { ], ], plugins: [ - process.env.EXCLUDE_PLATFORM_ENTERPRISE ? null : docs_platform_enterprise, - process.env.EXCLUDE_PLATFORM_CLOUD ? null : docs_platform_cloud, - process.env.EXCLUDE_PLATFORM_API ? null : docs_platform_api, - process.env.EXCLUDE_PLATFORM_CLI ? null : docs_platform_cli, - process.env.EXCLUDE_MULTIQC ? null : docs_multiqc, - process.env.EXCLUDE_FUSION ? null : docs_fusion, - process.env.EXCLUDE_WAVE ? null : docs_wave, + docs_platform_enterprise, + isEnterpriseArchiveSite || process.env.EXCLUDE_PLATFORM_CLOUD + ? null + : docs_platform_cloud, + isEnterpriseArchiveSite || process.env.EXCLUDE_PLATFORM_API + ? null + : docs_platform_api, + isEnterpriseArchiveSite || process.env.EXCLUDE_PLATFORM_CLI + ? null + : docs_platform_cli, + isEnterpriseArchiveSite || process.env.EXCLUDE_MULTIQC + ? null + : docs_multiqc, + isEnterpriseArchiveSite || process.env.EXCLUDE_FUSION + ? null + : docs_fusion, + isEnterpriseArchiveSite || process.env.EXCLUDE_WAVE ? null : docs_wave, // Disable expensive bundler options. // https://github.com/facebook/docusaurus/pull/11176 @@ -287,30 +370,41 @@ export default async function createConfigAsync() { ], themeConfig: getSeqeraThemeConfig({ + announcementBar: isEnterpriseArchiveSite + ? { + id: "enterprise-archive-notice", + content: `Legacy Seqera Platform Enterprise documentation. For supported releases, visit docs.seqera.io/platform-enterprise/.`, + backgroundColor: "#f3f4f6", + textColor: "#111827", + isCloseable: false, + } + : undefined, typesense: { - typesenseCollectionName: 'seqera_docs', - searchPagePath: '/search', + typesenseCollectionName: "seqera_docs", + searchPagePath: isEnterpriseArchiveSite ? false : "/search", typesenseServerConfig: { nodes: [ { - host: 'uk4gflrza0d8yx5sp-1.a1.typesense.net', + host: "uk4gflrza0d8yx5sp-1.a1.typesense.net", port: 443, - protocol: 'https', + protocol: "https", }, ], - apiKey: 'KZsuSjc7jPqDm7pkl1kN8TkoHH9b3dwY', + apiKey: "KZsuSjc7jPqDm7pkl1kN8TkoHH9b3dwY", connectionTimeoutSeconds: 2, }, typesenseSearchParameters: { - query_by: 'content,hierarchy.lvl0,hierarchy.lvl1,hierarchy.lvl2,hierarchy.lvl3', - group_by: 'url_without_anchor', + query_by: + "content,hierarchy.lvl0,hierarchy.lvl1,hierarchy.lvl2,hierarchy.lvl3", + group_by: "url_without_anchor", group_limit: 1, num_typos: 1, prioritize_exact_match: true, - filter_by: 'docusaurus_tag:!=[default,doc_tag_doc_list,blog_posts_list,blog_tags_posts,doc_tags_list,blog_tags_list]', // TODO Remove once the scraper is updated + filter_by: + "docusaurus_tag:!=[default,doc_tag_doc_list,blog_posts_list,blog_tags_posts,doc_tags_list,blog_tags_list]", // TODO Remove once the scraper is updated }, contextualSearch: false, - placeholder: 'Search Seqera docs...', + placeholder: "Search Seqera docs...", }, prism: { additionalLanguages: [ @@ -332,32 +426,16 @@ export default async function createConfigAsync() { ], }, navbar: { - items: [ - { - label: 'Cloud', - href: '/platform-cloud/', - }, - { - label: 'Enterprise', - href: '/platform-enterprise/', - }, - { - label: 'Nextflow', - href: '/nextflow/', - }, - { - label: 'MultiQC', - href: '/multiqc/', - }, - { - label: 'Wave', - href: '/wave/', - }, - { - label: 'Fusion', - href: '/fusion/', + items: isEnterpriseArchiveSite ? archiveNavbarItems : mainNavbarItems, + }, + seqera: { + docs: { + versionDropdown: { + "platform-enterprise": { + enabled: true, + }, }, - ], + }, }, }), }); diff --git a/internal/prepare-site.mjs b/internal/prepare-site.mjs new file mode 100644 index 000000000..b767aac50 --- /dev/null +++ b/internal/prepare-site.mjs @@ -0,0 +1,20 @@ +import {spawn} from "node:child_process"; + +const docsSiteMode = process.env.DOCS_SITE_MODE ?? "main"; + +if (docsSiteMode === "enterprise-archive") { + console.log( + "DOCS_SITE_MODE=enterprise-archive: skipping OSS docs fetch for local startup/build.", + ); + process.exit(0); +} + +const child = spawn("npm", ["run", "fetch-docs-oss"], { + stdio: "inherit", + shell: true, + env: process.env, +}); + +child.on("exit", (code) => { + process.exit(code ?? 1); +}); diff --git a/netlify.toml b/netlify.toml index c82a92d15..edd099fde 100644 --- a/netlify.toml +++ b/netlify.toml @@ -53,6 +53,46 @@ to = "https://seqera-docs-api.netlify.app/platform-api/:splat" status = 200 +[[redirects]] + from = "/platform-enterprise/23.4" + to = "https://docs-archive.seqera.io/platform-enterprise/" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.4/*" + to = "https://docs-archive.seqera.io/platform-enterprise/:splat" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.3" + to = "https://docs-archive.seqera.io/platform-enterprise/23.3/" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.3/*" + to = "https://docs-archive.seqera.io/platform-enterprise/23.3/:splat" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.2" + to = "https://docs-archive.seqera.io/platform-enterprise/23.2/" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.2/*" + to = "https://docs-archive.seqera.io/platform-enterprise/23.2/:splat" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.1" + to = "https://docs-archive.seqera.io/platform-enterprise/23.1/" + status = 301 + +[[redirects]] + from = "/platform-enterprise/23.1/*" + to = "https://docs-archive.seqera.io/platform-enterprise/23.1/:splat" + status = 301 + [[redirects]] from = "/nextflow/*" to = "https://docs-migration.netlify.app/nextflow/:splat" diff --git a/package.json b/package.json index a059c21e2..59d6baed6 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,14 @@ }, "scripts": { "docusaurus": "docusaurus", - "prestart": "npm run fetch-docs-oss", + "prepare-site": "node internal/prepare-site.mjs", + "prestart": "npm run prepare-site", "start": "docusaurus start", "dev": "npm run start", - "prebuild": "npm run fetch-docs-oss", + "dev:archive": "DOCS_SITE_MODE=enterprise-archive npm run dev", + "prebuild": "npm run prepare-site", "build": "docusaurus build", + "build:archive": "DOCS_SITE_MODE=enterprise-archive npm run build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", diff --git a/platform-enterprise_versioned_sidebars/version-23.1-sidebars.json b/platform-enterprise_versioned_sidebars/version-23.1-sidebars.json index fe50554f8..82d922b16 100644 --- a/platform-enterprise_versioned_sidebars/version-23.1-sidebars.json +++ b/platform-enterprise_versioned_sidebars/version-23.1-sidebars.json @@ -169,8 +169,7 @@ "label": "Developer tools", "collapsed": true, "items": [ - "api/overview", - "cli/overview" + "api/overview" ] }, { diff --git a/platform-enterprise_versioned_sidebars/version-23.2-sidebars.json b/platform-enterprise_versioned_sidebars/version-23.2-sidebars.json index c0ffd73b6..66c3938e2 100644 --- a/platform-enterprise_versioned_sidebars/version-23.2-sidebars.json +++ b/platform-enterprise_versioned_sidebars/version-23.2-sidebars.json @@ -176,8 +176,7 @@ "label": "Developer tools", "collapsed": true, "items": [ - "api/overview", - "cli/overview" + "api/overview" ] }, { diff --git a/platform-enterprise_versions.json b/platform-enterprise_versions.json index 7298405d0..eba585591 100644 --- a/platform-enterprise_versions.json +++ b/platform-enterprise_versions.json @@ -1,2 +1 @@ -["25.3", "25.2", "25.1", "24.2", "24.1", "23.4", "23.3"] - +["25.3", "25.2", "25.1", "24.2", "24.1", "23.4", "23.3", "23.2", "23.1"] diff --git a/src/theme/DocSidebar/Desktop/Content/VersionSwitcher/index.tsx b/src/theme/DocSidebar/Desktop/Content/VersionSwitcher/index.tsx new file mode 100644 index 000000000..96f330515 --- /dev/null +++ b/src/theme/DocSidebar/Desktop/Content/VersionSwitcher/index.tsx @@ -0,0 +1,179 @@ +import React, {type Dispatch, type SetStateAction, useEffect, useRef} from "react"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import {useLocation} from "@docusaurus/router"; +import {useAllPluginInstancesData} from "@docusaurus/useGlobalData"; +import { + useActiveDocContext, + useDocsPreferredVersion, + useVersions, +} from "@docusaurus/plugin-content-docs/client"; +import {useVersionDropdownConfig} from "@seqera/docusaurus-theme-seqera/lib/theme/hooks/useVersionDropdownConfig"; +import {getVersionTargetDoc, processVersions} from "@seqera/docusaurus-theme-seqera/lib/theme/utils/versionHelpers"; + +interface VersionSwitcherProps { + isOpen: boolean; + setIsOpen: Dispatch>; +} + +interface DocsPluginData { + path: string; +} + +interface CustomFields { + docsSiteMode?: string; + legacyEnterpriseArchiveUrl?: string; +} + +function useActiveDocsPlugin(): string | null { + const location = useLocation(); + const allDocsData = useAllPluginInstancesData( + "docusaurus-plugin-content-docs", + ) as Record; + + for (const [pluginId, pluginData] of Object.entries(allDocsData)) { + if (pluginData?.path && location.pathname.startsWith(pluginData.path)) { + return pluginId; + } + } + + return null; +} + +interface VersionSwitcherInnerProps extends VersionSwitcherProps { + pluginId: string; +} + +function VersionSwitcherInner({ + isOpen, + setIsOpen, + pluginId, +}: VersionSwitcherInnerProps) { + const dropdownRef = useRef(null); + const {siteConfig} = useDocusaurusContext(); + const customFields = (siteConfig.customFields ?? {}) as CustomFields; + const location = useLocation(); + const versions = useVersions(pluginId); + const {savePreferredVersionName} = useDocsPreferredVersion(pluginId); + const activeDocContext = useActiveDocContext(pluginId); + const currentVersion = activeDocContext.activeVersion; + const config = useVersionDropdownConfig(pluginId); + + const showLegacyArchiveLink = + customFields.docsSiteMode === "main" && + pluginId === "platform-enterprise" && + Boolean(customFields.legacyEnterpriseArchiveUrl); + + if (!config.enabled) return null; + + const processedVersions = processVersions(versions, config); + + if ( + !processedVersions || + (processedVersions.length < 2 && !showLegacyArchiveLink) + ) { + return null; + } + + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (dropdownRef.current?.contains(event.target as Node)) return; + setTimeout(() => setIsOpen(false), 100); + } + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [setIsOpen]); + + const toggleDropdown = () => setIsOpen((prev) => !prev); + + function handleSelectVersion(version: string) { + savePreferredVersionName(version); + } + + const displayCurrentVersion = + currentVersion?.name === "current" && config.showCurrent + ? {...currentVersion, label: config.currentLabel} + : currentVersion; + + const items = processedVersions.filter( + (version) => version.label !== displayCurrentVersion?.label, + ); + + return ( +
+
+ + + {isOpen && ( +
+ {items.map((version) => { + const targetDoc = getVersionTargetDoc(version, activeDocContext); + return ( + + ); + })} + {showLegacyArchiveLink && ( + setIsOpen(false)}> + Legacy versions + + External + + + )} +
+ )} +
+
+ ); +} + +export default function VersionSwitcher(props: VersionSwitcherProps) { + const activePluginId = useActiveDocsPlugin(); + + if (typeof window === "undefined") return null; + if (!activePluginId) return null; + + return ; +} diff --git a/src/theme/Navbar/Content/index.tsx b/src/theme/Navbar/Content/index.tsx new file mode 100644 index 000000000..9a4891850 --- /dev/null +++ b/src/theme/Navbar/Content/index.tsx @@ -0,0 +1,182 @@ +import React, {type ReactNode} from "react"; +import clsx from "clsx"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import { + ErrorCauseBoundary, + ThemeClassNames, + useThemeConfig, +} from "@docusaurus/theme-common"; +import { + splitNavbarItems, + useNavbarMobileSidebar, +} from "@docusaurus/theme-common/internal"; +import NavbarItem, {type Props as NavbarItemConfig} from "@theme/NavbarItem"; +import NavbarColorModeToggle from "@theme/Navbar/ColorModeToggle"; +import SearchBar from "@theme/SearchBar"; +import NavbarMobileSidebarToggle from "@theme/Navbar/MobileSidebar/Toggle"; +import NavbarLogo from "@theme/Navbar/Logo"; +import Submenu from "@seqera/docusaurus-theme-seqera/lib/theme/Navbar/Submenu"; +import styles from "@seqera/docusaurus-theme-seqera/lib/theme/Navbar/Content/styles.module.css"; + +interface CustomFields { + docsSiteMode?: string; + supportedEnterpriseDocsUrl?: string; +} + +function useNavbarItems() { + return useThemeConfig().navbar.items as NavbarItemConfig[]; +} + +function NavbarItems({items}: {items: NavbarItemConfig[]}): ReactNode { + return ( + <> + {items.map((item, i) => ( + + new Error( + `A theme navbar item failed to render. +Please double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config: +${JSON.stringify(item, null, 2)}`, + {cause: error}, + ) + }> + + + ))} + + ); +} + +function NavbarContentLayout({ + left, + right, +}: { + left: ReactNode; + right: ReactNode; +}) { + return ( +
+
+ {left} +
+
+ {right} +
+
+ ); +} + +function ArchiveNavbarContent({ + supportedEnterpriseDocsUrl, +}: { + supportedEnterpriseDocsUrl?: string; +}) { + const mobileSidebar = useNavbarMobileSidebar(); + + return ( +
+ } + right={ + <> + {supportedEnterpriseDocsUrl && ( + + Supported versions + + )} +
+ +
+ {!mobileSidebar.disabled && } + + } + /> +
+ ); +} + +export default function NavbarContent(): ReactNode { + const {siteConfig} = useDocusaurusContext(); + const customFields = (siteConfig.customFields ?? {}) as CustomFields; + + if (customFields.docsSiteMode === "enterprise-archive") { + return ( + + ); + } + + const mobileSidebar = useNavbarMobileSidebar(); + const items = useNavbarItems(); + const [leftItems, rightItems] = splitNavbarItems(items); + + return ( +
+
+ } + right={ + <> + + + +
+ +
+ + {!mobileSidebar.disabled && } + +
+ +
+ + } + /> +
+ +
+ ); +} diff --git a/src/theme/SearchBar.tsx b/src/theme/SearchBar.tsx new file mode 100644 index 000000000..54ff215e3 --- /dev/null +++ b/src/theme/SearchBar.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import SearchBar from "@theme-original/SearchBar"; + +interface CustomFields { + docsSiteMode?: string; +} + +export default function SearchBarWrapper() { + const {siteConfig} = useDocusaurusContext(); + const customFields = (siteConfig.customFields ?? {}) as CustomFields; + + if (customFields.docsSiteMode === "enterprise-archive") { + return null; + } + + return ; +}