From 4e7145382ffddc7fa65b4a4bb367fb94e6e918bc Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 23 Dec 2025 14:41:42 +0530 Subject: [PATCH 1/7] upcoming: [UIE-9813] - Implement routing for Cloud Manager Marketplace --- .../components/PrimaryNav/PrimaryNav.test.tsx | 19 +++++++++++ .../src/components/PrimaryNav/PrimaryNav.tsx | 20 ++++++++++-- .../manager/src/dev-tools/FeatureFlagTool.tsx | 1 + packages/manager/src/featureFlags.ts | 8 ++++- .../Marketplace/MarketplaceLanding.tsx | 8 +++++ .../Marketplace/marketplaceLazyRoute.tsx | 9 ++++++ .../manager/src/features/Marketplace/utils.ts | 29 +++++++++++++++++ packages/manager/src/routes/index.tsx | 2 ++ .../routes/marketplace/MarketplaceRoute.tsx | 23 +++++++++++++ .../manager/src/routes/marketplace/index.ts | 32 +++++++++++++++++++ 10 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 packages/manager/src/features/Marketplace/MarketplaceLanding.tsx create mode 100644 packages/manager/src/features/Marketplace/marketplaceLazyRoute.tsx create mode 100644 packages/manager/src/features/Marketplace/utils.ts create mode 100644 packages/manager/src/routes/marketplace/MarketplaceRoute.tsx create mode 100644 packages/manager/src/routes/marketplace/index.ts diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx index 774bcdc49c1..ff82b26e826 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx @@ -590,4 +590,23 @@ describe('PrimaryNav', () => { expect(databaseNavItem).toBeVisible(); }); + + it('should show Partner Referral menu item if the user has the account capability and the flag is enabled', async () => { + const flags: Partial = { + marketplace: { + enabled: true, + beta: false, + la: false, + ga: false, + }, + }; + + const { findByTestId } = renderWithTheme(, { + flags, + }); + + const marketplaceNavItem = await findByTestId('menu-item-Partner Referral'); + + expect(marketplaceNavItem).toBeVisible(); + }); }); diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index d42cfe40962..8b3cf38990a 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -22,6 +22,7 @@ import { useIsACLPEnabled } from 'src/features/CloudPulse/Utils/utils'; import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; import { useIsACLPLogsEnabled } from 'src/features/Delivery/deliveryUtils'; import { useIsIAMEnabled } from 'src/features/IAM/hooks/useIsIAMEnabled'; +import { useIsMarketplaceEnabled } from 'src/features/Marketplace/utils'; import { useIsNetworkLoadBalancerEnabled } from 'src/features/NetworkLoadBalancers/utils'; import { useIsPlacementGroupsEnabled } from 'src/features/PlacementGroups/utils'; import { useFlags } from 'src/hooks/useFlags'; @@ -54,13 +55,14 @@ export type NavEntity = | 'Longview' | 'Maintenance' | 'Managed' - | 'Marketplace' | 'Metrics' | 'Monitor' | 'Network Load Balancer' | 'NodeBalancers' | 'Object Storage' + | 'Partner Referrals' | 'Placement Groups' + | 'Quick Deploy Apps' | 'Quotas' | 'Service Transfers' | 'StackScripts' @@ -121,6 +123,9 @@ export const PrimaryNav = (props: PrimaryNavProps) => { const { isNetworkLoadBalancerEnabled } = useIsNetworkLoadBalancerEnabled(); + const { isMarketplaceFeatureEnabled, isMarketplaceBetaEnabled } = + useIsMarketplaceEnabled(); + const { data: preferences, error: preferencesError, @@ -176,9 +181,18 @@ export const PrimaryNav = (props: PrimaryNavProps) => { }, { attr: { 'data-qa-one-click-nav-btn': true }, - display: 'Marketplace', + display: !isMarketplaceFeatureEnabled + ? 'Marketplace' + : 'Quick Deploy Apps', to: '/linodes/create/marketplace', }, + { + attr: { 'data-qa-one-click-nav-btn': true }, + display: 'Partner Referrals', + hide: !isMarketplaceFeatureEnabled, + isBeta: isMarketplaceFeatureEnabled && isMarketplaceBetaEnabled, + to: '/cloud-marketplace/catalog', + }, ], name: 'Compute', }, @@ -352,6 +366,8 @@ export const PrimaryNav = (props: PrimaryNavProps) => { isIAMBeta, isIAMEnabled, iamRbacPrimaryNavChanges, + isMarketplaceFeatureEnabled, + isMarketplaceBetaEnabled, isNetworkLoadBalancerEnabled, limitsEvolution, ] diff --git a/packages/manager/src/dev-tools/FeatureFlagTool.tsx b/packages/manager/src/dev-tools/FeatureFlagTool.tsx index e017a81f19c..d294aa98822 100644 --- a/packages/manager/src/dev-tools/FeatureFlagTool.tsx +++ b/packages/manager/src/dev-tools/FeatureFlagTool.tsx @@ -40,6 +40,7 @@ const options: { flag: keyof Flags; label: string }[] = [ { flag: 'linodeDiskEncryption', label: 'Linode Disk Encryption (LDE)' }, { flag: 'linodeInterfaces', label: 'Linode Interfaces' }, { flag: 'lkeEnterprise2', label: 'LKE-Enterprise' }, + { flag: 'marketplace', label: 'Marketplace' }, { flag: 'networkLoadBalancer', label: 'Network Load Balancer' }, { flag: 'nodebalancerIpv6', label: 'NodeBalancer Dual Stack (IPv6)' }, { flag: 'nodebalancerVpc', label: 'NodeBalancer-VPC Integration' }, diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 3576eb95e94..6e723f9c639 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -177,6 +177,11 @@ interface FirewallRulesetsAndPrefixLists extends BetaFeatureFlag { la: boolean; } +interface Marketplace extends BetaFeatureFlag { + ga: boolean; + la: boolean; +} + export interface Flags { acceleratedPlans: AcceleratedPlansFlag; aclp: AclpFlag; @@ -223,6 +228,7 @@ export interface Flags { linodeInterfaces: LinodeInterfacesFlag; lkeEnterprise2: LkeEnterpriseFlag; mainContentBanner: MainContentBanner; + marketplace: Marketplace; marketplaceAppOverrides: MarketplaceAppOverride[]; metadata: boolean; mtc: MTC; @@ -344,12 +350,12 @@ export type ProductInformationBannerLocation = | 'Identity and Access' | 'Images' | 'Kubernetes' - | 'LinodeCreate' // Use for Marketplace banners | 'Linodes' | 'LoadBalancers' | 'Logs' | 'Longview' | 'Managed' + | 'Marketplace' | 'Network LoadBalancers' | 'NodeBalancers' | 'Object Storage' diff --git a/packages/manager/src/features/Marketplace/MarketplaceLanding.tsx b/packages/manager/src/features/Marketplace/MarketplaceLanding.tsx new file mode 100644 index 00000000000..10c9c37b848 --- /dev/null +++ b/packages/manager/src/features/Marketplace/MarketplaceLanding.tsx @@ -0,0 +1,8 @@ +import { Notice } from '@linode/ui'; +import * as React from 'react'; + +export const MarketplaceLanding = () => { + return ( + Partner Referral Catalog is coming soon... + ); +}; diff --git a/packages/manager/src/features/Marketplace/marketplaceLazyRoute.tsx b/packages/manager/src/features/Marketplace/marketplaceLazyRoute.tsx new file mode 100644 index 00000000000..e6784df0a68 --- /dev/null +++ b/packages/manager/src/features/Marketplace/marketplaceLazyRoute.tsx @@ -0,0 +1,9 @@ +import { createLazyRoute } from '@tanstack/react-router'; + +import { MarketplaceLanding } from './MarketplaceLanding'; + +export const marketplaceLazyRoute = createLazyRoute( + '/cloud-marketplace/catalog' +)({ + component: MarketplaceLanding, +}); diff --git a/packages/manager/src/features/Marketplace/utils.ts b/packages/manager/src/features/Marketplace/utils.ts new file mode 100644 index 00000000000..c3f54b10092 --- /dev/null +++ b/packages/manager/src/features/Marketplace/utils.ts @@ -0,0 +1,29 @@ +import { useFlags } from 'src/hooks/useFlags'; + +/** + * Returns whether or not features related to the Marketplace project + * should be enabled, and whether they are in beta, LA, or GA. + * + * Note: Currently, this just uses the `Marketplace` feature flag as a source of truth, + * but will eventually also look at account capabilities if available. + */ +export const useIsMarketplaceEnabled = () => { + const flags = useFlags(); + + if (!flags) { + return { + isMarketplaceFeatureEnabled: false, + isMarketplaceBetaEnabled: false, + isMarketplaceLAEnabled: false, + isMarketplaceGAEnabled: false, + }; + } + + // @TODO: Cloud Manager Marketplace - check for customer tag/account capability when it exists + return { + isMarketplaceFeatureEnabled: flags.marketplace?.enabled, + isMarketplaceBetaEnabled: flags.marketplace?.beta, + isMarketplaceLAEnabled: flags.marketplace?.la, + isMarketplaceGAEnabled: flags.marketplace?.ga, + }; +}; diff --git a/packages/manager/src/routes/index.tsx b/packages/manager/src/routes/index.tsx index 3c2193f36fa..e24aed3b464 100644 --- a/packages/manager/src/routes/index.tsx +++ b/packages/manager/src/routes/index.tsx @@ -29,6 +29,7 @@ import { loginHistoryRouteTree } from './loginHistory/'; import { longviewRouteTree } from './longview'; import { maintenanceRouteTree } from './maintenance'; import { managedRouteTree } from './managed'; +import { marketplaceRouteTree } from './marketplace'; import { cloudPulseMetricsRouteTree } from './metrics'; import { networkLoadBalancersRouteTree } from './networkLoadBalancer'; import { nodeBalancersRouteTree } from './nodeBalancers'; @@ -80,6 +81,7 @@ export const routeTree = rootRoute.addChildren([ longviewRouteTree, maintenanceRouteTree, managedRouteTree, + marketplaceRouteTree, networkLoadBalancersRouteTree, nodeBalancersRouteTree, objectStorageRouteTree, diff --git a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx new file mode 100644 index 00000000000..32adea1fbbd --- /dev/null +++ b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx @@ -0,0 +1,23 @@ +import { NotFound } from '@linode/ui'; +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; +import { useIsMarketplaceEnabled } from 'src/features/Marketplace/utils'; + +export const MarketplaceRoute = () => { + const { isMarketplaceFeatureEnabled } = useIsMarketplaceEnabled(); + + if (!isMarketplaceFeatureEnabled) { + return ; + } + return ( + }> + + + + + ); +}; diff --git a/packages/manager/src/routes/marketplace/index.ts b/packages/manager/src/routes/marketplace/index.ts new file mode 100644 index 00000000000..9ef9c903270 --- /dev/null +++ b/packages/manager/src/routes/marketplace/index.ts @@ -0,0 +1,32 @@ +import { createRoute, redirect } from '@tanstack/react-router'; + +import { rootRoute } from '../root'; +import { MarketplaceRoute } from './MarketplaceRoute'; + +export const marketplaceRoute = createRoute({ + component: MarketplaceRoute, + getParentRoute: () => rootRoute, + path: 'cloud-marketplace', +}); + +export const marketplaceLandingRoute = createRoute({ + beforeLoad: async () => { + throw redirect({ to: '/cloud-marketplace/catalog' }); + }, + getParentRoute: () => marketplaceRoute, + path: '/', +}); + +export const marketplaceCatlogRoute = createRoute({ + getParentRoute: () => marketplaceRoute, + path: '/catalog', +}).lazy(() => + import('src/features/Marketplace/marketplaceLazyRoute').then( + (m) => m.marketplaceLazyRoute + ) +); + +export const marketplaceRouteTree = marketplaceRoute.addChildren([ + marketplaceLandingRoute, + marketplaceCatlogRoute, +]); From 72bb820798b45a47ffa471eaad42490c89e018c6 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 23 Dec 2025 18:31:03 +0530 Subject: [PATCH 2/7] fix failing test cases and other marketplace references --- packages/manager/src/GoTo.tsx | 9 +++++++-- .../src/components/PrimaryNav/PrimaryNav.test.tsx | 6 ++++-- .../manager/src/components/PrimaryNav/PrimaryNav.tsx | 1 + .../src/features/TopMenu/CreateMenu/CreateMenu.tsx | 9 +++++++-- .../manager/src/routes/marketplace/MarketplaceRoute.tsx | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/manager/src/GoTo.tsx b/packages/manager/src/GoTo.tsx index e64ef845be7..f0ceef02e1a 100644 --- a/packages/manager/src/GoTo.tsx +++ b/packages/manager/src/GoTo.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { useIsDatabasesEnabled } from './features/Databases/utilities'; import { usePermissions } from './features/IAM/hooks/usePermissions'; +import { useIsMarketplaceEnabled } from './features/Marketplace/utils'; import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils'; import { useFlags } from './hooks/useFlags'; import { useGlobalKeyboardListener } from './hooks/useGlobalKeyboardListener'; @@ -24,6 +25,8 @@ export const GoTo = React.memo(() => { const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); const { isDatabasesEnabled } = useIsDatabasesEnabled(); + const { isMarketplaceFeatureEnabled } = useIsMarketplaceEnabled(); + const { goToOpen, setGoToOpen } = useGlobalKeyboardListener(); const onClose = () => { @@ -99,9 +102,10 @@ export const GoTo = React.memo(() => { display: 'Longview', href: '/longview', }, - { - display: 'Marketplace', + display: !isMarketplaceFeatureEnabled + ? 'Marketplace' + : 'Quick Deploy Apps', href: '/linodes/create/marketplace', }, ...(iamRbacPrimaryNavChanges @@ -133,6 +137,7 @@ export const GoTo = React.memo(() => { permissions.is_account_admin, isDatabasesEnabled, isManagedAccount, + isMarketplaceFeatureEnabled, isPlacementGroupsEnabled, iamRbacPrimaryNavChanges, ] diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx index ff82b26e826..142d5ef5c48 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx @@ -605,8 +605,10 @@ describe('PrimaryNav', () => { flags, }); - const marketplaceNavItem = await findByTestId('menu-item-Partner Referral'); + const partnerReferralNavItem = await findByTestId( + 'menu-item-Partner Referrals' + ); - expect(marketplaceNavItem).toBeVisible(); + expect(partnerReferralNavItem).toBeVisible(); }); }); diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index 8b3cf38990a..a197406bac5 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -55,6 +55,7 @@ export type NavEntity = | 'Longview' | 'Maintenance' | 'Managed' + | 'Marketplace' // TODO: Cloud Manager Marketplace - Remove marketplace references once 'Quick Deploy Apps' is fully rolled out | 'Metrics' | 'Monitor' | 'Network Load Balancer' diff --git a/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx b/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx index 25935ebaee0..bf3784183be 100644 --- a/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx +++ b/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx @@ -7,6 +7,7 @@ import DatabaseIcon from 'src/assets/icons/entityIcons/database.svg'; import NetworkIcon from 'src/assets/icons/entityIcons/networking.svg'; import StorageIcon from 'src/assets/icons/entityIcons/storage.svg'; import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; +import { useIsMarketplaceEnabled } from 'src/features/Marketplace/utils'; import { useIsPlacementGroupsEnabled } from 'src/features/PlacementGroups/utils'; import { @@ -30,10 +31,11 @@ export type CreateEntity = | 'Image' | 'Kubernetes' | 'Linode' - | 'Marketplace' + | 'Marketplace' // TODO: Cloud Manager Marketplace - Remove marketplace references once 'Quick Deploy Apps' is fully rolled out | 'NodeBalancer' | 'Object Storage' | 'Placement Group' + | 'Quick Deploy Apps' | 'Volume' | 'VPC'; @@ -52,6 +54,7 @@ export const CreateMenu = () => { const { isDatabasesEnabled } = useIsDatabasesEnabled(); const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); + const { isMarketplaceFeatureEnabled } = useIsMarketplaceEnabled(); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -90,7 +93,9 @@ export const CreateMenu = () => { { attr: { 'data-qa-one-click-add-new': true }, description: 'Deploy applications with ease', - display: 'Marketplace', + display: !isMarketplaceFeatureEnabled + ? 'Marketplace' + : 'Quick Deploy Apps', to: '/linodes/create/marketplace', }, ], diff --git a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx index 32adea1fbbd..435fcfdfd2d 100644 --- a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx +++ b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx @@ -15,7 +15,7 @@ export const MarketplaceRoute = () => { } return ( }> - + From fc0b67c478a8e4516494ea0d61bfd3f3600fa435 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 23 Dec 2025 18:33:58 +0530 Subject: [PATCH 3/7] Added changeset: Implement routing for Cloud Manager Marketplace --- .../.changeset/pr-13222-upcoming-features-1766495037845.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-13222-upcoming-features-1766495037845.md diff --git a/packages/manager/.changeset/pr-13222-upcoming-features-1766495037845.md b/packages/manager/.changeset/pr-13222-upcoming-features-1766495037845.md new file mode 100644 index 00000000000..ac1b9b2170c --- /dev/null +++ b/packages/manager/.changeset/pr-13222-upcoming-features-1766495037845.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Implement routing for Cloud Manager Marketplace ([#13222](https://github.com/linode/manager/pull/13222)) From fc71b8a0b97956e880a8759e843768f9d78a07f6 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 23 Dec 2025 18:51:02 +0530 Subject: [PATCH 4/7] fix variable name --- .../manager/src/components/PrimaryNav/PrimaryNav.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx index 142d5ef5c48..ea97cbf901f 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx @@ -584,11 +584,11 @@ describe('PrimaryNav', () => { flags, }); - const databaseNavItem = await findByTestId( + const networkLoadbalancerNavItem = await findByTestId( 'menu-item-Network Load Balancer' ); - expect(databaseNavItem).toBeVisible(); + expect(networkLoadbalancerNavItem).toBeVisible(); }); it('should show Partner Referral menu item if the user has the account capability and the flag is enabled', async () => { From a3700f13c996fbf615983776ec656a561072cd3c Mon Sep 17 00:00:00 2001 From: hrao Date: Mon, 5 Jan 2026 18:01:59 +0530 Subject: [PATCH 5/7] renamed marketplace feature flag for semantic correctness --- packages/manager/src/GoTo.tsx | 4 ++-- .../src/components/PrimaryNav/PrimaryNav.test.tsx | 7 +------ .../src/components/PrimaryNav/PrimaryNav.tsx | 8 +++----- packages/manager/src/dev-tools/FeatureFlagTool.tsx | 2 +- packages/manager/src/featureFlags.ts | 7 +------ packages/manager/src/features/Marketplace/utils.ts | 14 ++++---------- .../src/features/TopMenu/CreateMenu/CreateMenu.tsx | 4 ++-- .../src/routes/marketplace/MarketplaceRoute.tsx | 4 ++-- 8 files changed, 16 insertions(+), 34 deletions(-) diff --git a/packages/manager/src/GoTo.tsx b/packages/manager/src/GoTo.tsx index f0ceef02e1a..1a3108caf35 100644 --- a/packages/manager/src/GoTo.tsx +++ b/packages/manager/src/GoTo.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { useIsDatabasesEnabled } from './features/Databases/utilities'; import { usePermissions } from './features/IAM/hooks/usePermissions'; -import { useIsMarketplaceEnabled } from './features/Marketplace/utils'; +import { useIsMarketplaceV2Enabled } from './features/Marketplace/utils'; import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils'; import { useFlags } from './hooks/useFlags'; import { useGlobalKeyboardListener } from './hooks/useGlobalKeyboardListener'; @@ -25,7 +25,7 @@ export const GoTo = React.memo(() => { const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); const { isDatabasesEnabled } = useIsDatabasesEnabled(); - const { isMarketplaceFeatureEnabled } = useIsMarketplaceEnabled(); + const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); const { goToOpen, setGoToOpen } = useGlobalKeyboardListener(); diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx index ea97cbf901f..1dc29e4d77a 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx @@ -593,12 +593,7 @@ describe('PrimaryNav', () => { it('should show Partner Referral menu item if the user has the account capability and the flag is enabled', async () => { const flags: Partial = { - marketplace: { - enabled: true, - beta: false, - la: false, - ga: false, - }, + marketplaceV2: true, }; const { findByTestId } = renderWithTheme(, { diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index a197406bac5..6a632336873 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -22,7 +22,7 @@ import { useIsACLPEnabled } from 'src/features/CloudPulse/Utils/utils'; import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; import { useIsACLPLogsEnabled } from 'src/features/Delivery/deliveryUtils'; import { useIsIAMEnabled } from 'src/features/IAM/hooks/useIsIAMEnabled'; -import { useIsMarketplaceEnabled } from 'src/features/Marketplace/utils'; +import { useIsMarketplaceV2Enabled } from 'src/features/Marketplace/utils'; import { useIsNetworkLoadBalancerEnabled } from 'src/features/NetworkLoadBalancers/utils'; import { useIsPlacementGroupsEnabled } from 'src/features/PlacementGroups/utils'; import { useFlags } from 'src/hooks/useFlags'; @@ -124,8 +124,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => { const { isNetworkLoadBalancerEnabled } = useIsNetworkLoadBalancerEnabled(); - const { isMarketplaceFeatureEnabled, isMarketplaceBetaEnabled } = - useIsMarketplaceEnabled(); + const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); const { data: preferences, @@ -191,7 +190,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => { attr: { 'data-qa-one-click-nav-btn': true }, display: 'Partner Referrals', hide: !isMarketplaceFeatureEnabled, - isBeta: isMarketplaceFeatureEnabled && isMarketplaceBetaEnabled, + isBeta: isMarketplaceFeatureEnabled, to: '/cloud-marketplace/catalog', }, ], @@ -368,7 +367,6 @@ export const PrimaryNav = (props: PrimaryNavProps) => { isIAMEnabled, iamRbacPrimaryNavChanges, isMarketplaceFeatureEnabled, - isMarketplaceBetaEnabled, isNetworkLoadBalancerEnabled, limitsEvolution, ] diff --git a/packages/manager/src/dev-tools/FeatureFlagTool.tsx b/packages/manager/src/dev-tools/FeatureFlagTool.tsx index d294aa98822..a3b79b4945d 100644 --- a/packages/manager/src/dev-tools/FeatureFlagTool.tsx +++ b/packages/manager/src/dev-tools/FeatureFlagTool.tsx @@ -40,7 +40,7 @@ const options: { flag: keyof Flags; label: string }[] = [ { flag: 'linodeDiskEncryption', label: 'Linode Disk Encryption (LDE)' }, { flag: 'linodeInterfaces', label: 'Linode Interfaces' }, { flag: 'lkeEnterprise2', label: 'LKE-Enterprise' }, - { flag: 'marketplace', label: 'Marketplace' }, + { flag: 'marketplaceV2', label: 'MarketplaceV2' }, { flag: 'networkLoadBalancer', label: 'Network Load Balancer' }, { flag: 'nodebalancerIpv6', label: 'NodeBalancer Dual Stack (IPv6)' }, { flag: 'nodebalancerVpc', label: 'NodeBalancer-VPC Integration' }, diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 6e723f9c639..b8e2e2df5c0 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -177,11 +177,6 @@ interface FirewallRulesetsAndPrefixLists extends BetaFeatureFlag { la: boolean; } -interface Marketplace extends BetaFeatureFlag { - ga: boolean; - la: boolean; -} - export interface Flags { acceleratedPlans: AcceleratedPlansFlag; aclp: AclpFlag; @@ -228,8 +223,8 @@ export interface Flags { linodeInterfaces: LinodeInterfacesFlag; lkeEnterprise2: LkeEnterpriseFlag; mainContentBanner: MainContentBanner; - marketplace: Marketplace; marketplaceAppOverrides: MarketplaceAppOverride[]; + marketplaceV2: boolean; metadata: boolean; mtc: MTC; networkLoadBalancer: boolean; diff --git a/packages/manager/src/features/Marketplace/utils.ts b/packages/manager/src/features/Marketplace/utils.ts index c3f54b10092..c3ea47c6470 100644 --- a/packages/manager/src/features/Marketplace/utils.ts +++ b/packages/manager/src/features/Marketplace/utils.ts @@ -2,28 +2,22 @@ import { useFlags } from 'src/hooks/useFlags'; /** * Returns whether or not features related to the Marketplace project - * should be enabled, and whether they are in beta, LA, or GA. + * should be enabled. * - * Note: Currently, this just uses the `Marketplace` feature flag as a source of truth, + * Note: Currently, this just uses the `marketplaceV2` feature flag as a source of truth, * but will eventually also look at account capabilities if available. */ -export const useIsMarketplaceEnabled = () => { +export const useIsMarketplaceV2Enabled = () => { const flags = useFlags(); if (!flags) { return { isMarketplaceFeatureEnabled: false, - isMarketplaceBetaEnabled: false, - isMarketplaceLAEnabled: false, - isMarketplaceGAEnabled: false, }; } // @TODO: Cloud Manager Marketplace - check for customer tag/account capability when it exists return { - isMarketplaceFeatureEnabled: flags.marketplace?.enabled, - isMarketplaceBetaEnabled: flags.marketplace?.beta, - isMarketplaceLAEnabled: flags.marketplace?.la, - isMarketplaceGAEnabled: flags.marketplace?.ga, + isMarketplaceFeatureEnabled: flags.marketplaceV2, }; }; diff --git a/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx b/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx index bf3784183be..c9844cd2d61 100644 --- a/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx +++ b/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx @@ -7,7 +7,7 @@ import DatabaseIcon from 'src/assets/icons/entityIcons/database.svg'; import NetworkIcon from 'src/assets/icons/entityIcons/networking.svg'; import StorageIcon from 'src/assets/icons/entityIcons/storage.svg'; import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; -import { useIsMarketplaceEnabled } from 'src/features/Marketplace/utils'; +import { useIsMarketplaceV2Enabled } from 'src/features/Marketplace/utils'; import { useIsPlacementGroupsEnabled } from 'src/features/PlacementGroups/utils'; import { @@ -54,7 +54,7 @@ export const CreateMenu = () => { const { isDatabasesEnabled } = useIsDatabasesEnabled(); const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); - const { isMarketplaceFeatureEnabled } = useIsMarketplaceEnabled(); + const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); diff --git a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx index 435fcfdfd2d..10fe4f81eb0 100644 --- a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx +++ b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx @@ -5,10 +5,10 @@ import React from 'react'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; import { SuspenseLoader } from 'src/components/SuspenseLoader'; -import { useIsMarketplaceEnabled } from 'src/features/Marketplace/utils'; +import { useIsMarketplaceV2Enabled } from 'src/features/Marketplace/utils'; export const MarketplaceRoute = () => { - const { isMarketplaceFeatureEnabled } = useIsMarketplaceEnabled(); + const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); if (!isMarketplaceFeatureEnabled) { return ; From 148766adba4b70882145a71643356a7741223e66 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 6 Jan 2026 12:45:04 +0530 Subject: [PATCH 6/7] PR feedback and pendo addition --- packages/manager/src/GoTo.tsx | 6 +++--- .../src/components/PrimaryNav/PrimaryNav.tsx | 15 +++++++++------ .../manager/src/features/Marketplace/utils.ts | 4 ++-- .../features/TopMenu/CreateMenu/CreateMenu.tsx | 4 ++-- .../src/routes/marketplace/MarketplaceRoute.tsx | 4 ++-- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/manager/src/GoTo.tsx b/packages/manager/src/GoTo.tsx index 1a3108caf35..93a0bddf0b7 100644 --- a/packages/manager/src/GoTo.tsx +++ b/packages/manager/src/GoTo.tsx @@ -25,7 +25,7 @@ export const GoTo = React.memo(() => { const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); const { isDatabasesEnabled } = useIsDatabasesEnabled(); - const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); + const { isMarketplaceV2FeatureEnabled } = useIsMarketplaceV2Enabled(); const { goToOpen, setGoToOpen } = useGlobalKeyboardListener(); @@ -103,7 +103,7 @@ export const GoTo = React.memo(() => { href: '/longview', }, { - display: !isMarketplaceFeatureEnabled + display: !isMarketplaceV2FeatureEnabled ? 'Marketplace' : 'Quick Deploy Apps', href: '/linodes/create/marketplace', @@ -137,7 +137,7 @@ export const GoTo = React.memo(() => { permissions.is_account_admin, isDatabasesEnabled, isManagedAccount, - isMarketplaceFeatureEnabled, + isMarketplaceV2FeatureEnabled, isPlacementGroupsEnabled, iamRbacPrimaryNavChanges, ] diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index 77d389c6775..1a4d0499baa 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -124,7 +124,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => { const { isNetworkLoadBalancerEnabled } = useIsNetworkLoadBalancerEnabled(); - const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); + const { isMarketplaceV2FeatureEnabled } = useIsMarketplaceV2Enabled(); const { data: preferences, @@ -181,16 +181,19 @@ export const PrimaryNav = (props: PrimaryNavProps) => { }, { attr: { 'data-qa-one-click-nav-btn': true }, - display: !isMarketplaceFeatureEnabled + display: !isMarketplaceV2FeatureEnabled ? 'Marketplace' : 'Quick Deploy Apps', to: '/linodes/create/marketplace', }, { - attr: { 'data-qa-one-click-nav-btn': true }, + attr: { + 'data-qa-one-click-nav-btn': true, + 'data-pendo-id': 'menu-item-Cloud Marketplace', + }, display: 'Partner Referrals', - hide: !isMarketplaceFeatureEnabled, - isBeta: isMarketplaceFeatureEnabled, + hide: !isMarketplaceV2FeatureEnabled, + isBeta: isMarketplaceV2FeatureEnabled, to: '/cloud-marketplace/catalog', }, ], @@ -367,7 +370,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => { isIAMBeta, isIAMEnabled, iamRbacPrimaryNavChanges, - isMarketplaceFeatureEnabled, + isMarketplaceV2FeatureEnabled, isNetworkLoadBalancerEnabled, limitsEvolution, ] diff --git a/packages/manager/src/features/Marketplace/utils.ts b/packages/manager/src/features/Marketplace/utils.ts index c3ea47c6470..e942cb496c2 100644 --- a/packages/manager/src/features/Marketplace/utils.ts +++ b/packages/manager/src/features/Marketplace/utils.ts @@ -12,12 +12,12 @@ export const useIsMarketplaceV2Enabled = () => { if (!flags) { return { - isMarketplaceFeatureEnabled: false, + isMarketplaceV2FeatureEnabled: false, }; } // @TODO: Cloud Manager Marketplace - check for customer tag/account capability when it exists return { - isMarketplaceFeatureEnabled: flags.marketplaceV2, + isMarketplaceV2FeatureEnabled: flags.marketplaceV2, }; }; diff --git a/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx b/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx index c9844cd2d61..cba276d3914 100644 --- a/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx +++ b/packages/manager/src/features/TopMenu/CreateMenu/CreateMenu.tsx @@ -54,7 +54,7 @@ export const CreateMenu = () => { const { isDatabasesEnabled } = useIsDatabasesEnabled(); const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); - const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); + const { isMarketplaceV2FeatureEnabled } = useIsMarketplaceV2Enabled(); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -93,7 +93,7 @@ export const CreateMenu = () => { { attr: { 'data-qa-one-click-add-new': true }, description: 'Deploy applications with ease', - display: !isMarketplaceFeatureEnabled + display: !isMarketplaceV2FeatureEnabled ? 'Marketplace' : 'Quick Deploy Apps', to: '/linodes/create/marketplace', diff --git a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx index 10fe4f81eb0..f394a0e2be6 100644 --- a/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx +++ b/packages/manager/src/routes/marketplace/MarketplaceRoute.tsx @@ -8,9 +8,9 @@ import { SuspenseLoader } from 'src/components/SuspenseLoader'; import { useIsMarketplaceV2Enabled } from 'src/features/Marketplace/utils'; export const MarketplaceRoute = () => { - const { isMarketplaceFeatureEnabled } = useIsMarketplaceV2Enabled(); + const { isMarketplaceV2FeatureEnabled } = useIsMarketplaceV2Enabled(); - if (!isMarketplaceFeatureEnabled) { + if (!isMarketplaceV2FeatureEnabled) { return ; } return ( From 84892eee72aaec069f35053b75908651bae820fc Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 6 Jan 2026 17:25:10 +0530 Subject: [PATCH 7/7] missed changes --- packages/api-v4/src/marketplace/index.ts | 1 + packages/api-v4/src/marketplace/marketplace.ts | 15 +++++++++++++-- packages/manager/src/mocks/serverHandlers.ts | 15 +++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/api-v4/src/marketplace/index.ts b/packages/api-v4/src/marketplace/index.ts index fcb073fefcd..a16c03c32d8 100644 --- a/packages/api-v4/src/marketplace/index.ts +++ b/packages/api-v4/src/marketplace/index.ts @@ -1 +1,2 @@ +export * from './marketplace'; export * from './types'; diff --git a/packages/api-v4/src/marketplace/marketplace.ts b/packages/api-v4/src/marketplace/marketplace.ts index 72aaeed2c26..e253226a592 100644 --- a/packages/api-v4/src/marketplace/marketplace.ts +++ b/packages/api-v4/src/marketplace/marketplace.ts @@ -10,8 +10,11 @@ import Request, { } from 'src/request'; import type { + MarketplaceCategory, + MarketplacePartner, MarketplacePartnerReferralPayload, MarketplaceProduct, + MarketplaceType, } from './types'; import type { Filter, ResourcePage as Page, Params } from 'src/types'; @@ -32,7 +35,7 @@ export const getMarketplaceProduct = (productId: number) => ); export const getMarketplaceCategories = (params?: Params, filters?: Filter) => - Request>( + Request>( setURL(`${BETA_API_ROOT}/marketplace/categories`), setMethod('GET'), setParams(params), @@ -40,13 +43,21 @@ export const getMarketplaceCategories = (params?: Params, filters?: Filter) => ); export const getMarketplaceTypes = (params?: Params, filters?: Filter) => - Request>( + Request>( setURL(`${BETA_API_ROOT}/marketplace/types`), setMethod('GET'), setParams(params), setXFilter(filters), ); +export const getMarketplacePartners = (params?: Params, filters?: Filter) => + Request>( + setURL(`${BETA_API_ROOT}/marketplace/partners`), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); + export const createPartnerReferral = ( data: MarketplacePartnerReferralPayload, ) => diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index 809905f438f..d5bc8f82c9a 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -21,7 +21,10 @@ import { linodeStatsFactory, linodeTransferFactory, linodeTypeFactory, + marketplaceCategoryFactory, + marketplacePartnersFactory, marketplaceProductFactory, + marketplaceTypeFactory, nodeBalancerConfigFactory, nodeBalancerConfigNodeFactory, nodeBalancerFactory, @@ -625,7 +628,7 @@ const marketplace = [ return HttpResponse.json(makeResourcePage([...marketplaceProduct])); }), http.get('*/v4beta/marketplace/products/:productId', () => { - const marketplaceProductDetail = marketplaceProductFactory.buildList(10, { + const marketplaceProductDetail = marketplaceProductFactory.build({ details: { overview: { description: @@ -636,14 +639,18 @@ const marketplace = [ support: 'Support information goes here.', }, }); - return HttpResponse.json(...marketplaceProductDetail); + return HttpResponse.json(marketplaceProductDetail); }), http.get('*/v4beta/marketplace/categories', () => { - const marketplaceCategory = marketplaceProductFactory.buildList(5); + const marketplaceCategory = marketplaceCategoryFactory.buildList(5); return HttpResponse.json(makeResourcePage([...marketplaceCategory])); }), http.get('*/v4beta/marketplace/types', () => { - const marketplaceType = marketplaceProductFactory.buildList(5); + const marketplaceType = marketplaceTypeFactory.buildList(5); + return HttpResponse.json(makeResourcePage([...marketplaceType])); + }), + http.get('*/v4beta/marketplace/partners', () => { + const marketplaceType = marketplacePartnersFactory.buildList(5); return HttpResponse.json(makeResourcePage([...marketplaceType])); }), http.post('*/v4beta/marketplace/referral', async () => {