diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx index 397c4ed4e62..f753391ed28 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx @@ -13,7 +13,6 @@ import { SearchBarTrigger } from "./components/SearchBarTrigger"; import { SidebarToggle } from "./components/SidebarToggle"; import { V2WorkspaceOpenInButton } from "./components/V2WorkspaceOpenInButton"; import { V2WorkspaceSearchBarTrigger } from "./components/V2WorkspaceSearchBarTrigger"; -import { VersionToggle } from "./components/VersionToggle"; import { WindowControls } from "./components/WindowControls"; export function TopBar() { @@ -31,7 +30,7 @@ export function TopBar() { { enabled: !!workspaceId && !isV2WorkspaceRoute }, ); const isOnline = useOnlineStatus(); - const { isV2CloudEnabled, isRemoteV2Enabled } = useIsV2CloudEnabled(); + const { isV2CloudEnabled } = useIsV2CloudEnabled(); // Default to Mac layout while loading to avoid overlap with traffic lights const isMac = platform === undefined || platform === "darwin"; @@ -46,7 +45,6 @@ export function TopBar() { - {isRemoteV2Enabled && } {isV2WorkspaceRoute ? ( diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/VersionToggle.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/VersionToggle.tsx deleted file mode 100644 index 33462108871..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/VersionToggle.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; -import { cn } from "@superset/ui/utils"; -import { useV2LocalOverrideStore } from "renderer/stores/v2-local-override"; - -export function VersionToggle() { - const { forceV1, toggle } = useV2LocalOverrideStore(); - const activeVersion = forceV1 ? "v1" : "v2"; - - return ( - - - - - - {forceV1 - ? "Early Access: Switch to Superset V2" - : "Switch to Superset V1"} - - - ); -} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/index.ts deleted file mode 100644 index 3e06c734ddd..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { VersionToggle } from "./VersionToggle"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx index 0905de1ef99..74378ce1a31 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx @@ -1,6 +1,7 @@ import { cn } from "@superset/ui/utils"; import { Link, useMatchRoute } from "@tanstack/react-router"; import { + HiOutlineBeaker, HiOutlineBell, HiOutlineBuildingOffice2, HiOutlineCommandLine, @@ -35,6 +36,7 @@ type SettingsRoute = | "/settings/terminal" | "/settings/links" | "/settings/models" + | "/settings/experimental" | "/settings/integrations" | "/settings/billing" | "/settings/api-keys" @@ -170,6 +172,12 @@ const SECTION_GROUPS: SectionGroup[] = [ icon: , macOnly: true, }, + { + id: "/settings/experimental", + section: "experimental", + label: "Experimental", + icon: , + }, ], }, ]; diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx new file mode 100644 index 00000000000..90c4da1646d --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx @@ -0,0 +1,61 @@ +import { Label } from "@superset/ui/label"; +import { Switch } from "@superset/ui/switch"; +import { useIsV2CloudEnabled } from "renderer/hooks/useIsV2CloudEnabled"; +import { useV2LocalOverrideStore } from "renderer/stores/v2-local-override"; +import { + isItemVisible, + SETTING_ITEM_ID, + type SettingItemId, +} from "../../../utils/settings-search"; + +interface ExperimentalSettingsProps { + visibleItems?: SettingItemId[] | null; +} + +export function ExperimentalSettings({ + visibleItems, +}: ExperimentalSettingsProps) { + const showSupersetV2 = isItemVisible( + SETTING_ITEM_ID.EXPERIMENTAL_SUPERSET_V2, + visibleItems, + ); + const { isV2CloudEnabled, isRemoteV2Enabled } = useIsV2CloudEnabled(); + const setForceV1 = useV2LocalOverrideStore((state) => state.setForceV1); + + return ( +
+
+

Experimental

+

+ Try early access features and previews +

+
+ +
+ {showSupersetV2 && ( +
+
+ +

+ Use the new workspace experience when early access is available +

+ {!isRemoteV2Enabled && ( +

+ Early access is not enabled for this account. +

+ )} +
+ setForceV1(!enabled)} + disabled={!isRemoteV2Enabled} + /> +
+ )} +
+
+ ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/index.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/index.ts new file mode 100644 index 00000000000..7f3499d9e21 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/index.ts @@ -0,0 +1 @@ +export { ExperimentalSettings } from "./ExperimentalSettings"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/page.tsx new file mode 100644 index 00000000000..f03700a391b --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/page.tsx @@ -0,0 +1,22 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { useMemo } from "react"; +import { useSettingsSearchQuery } from "renderer/stores/settings-state"; +import { getMatchingItemsForSection } from "../utils/settings-search"; +import { ExperimentalSettings } from "./components/ExperimentalSettings"; + +export const Route = createFileRoute("/_authenticated/settings/experimental/")({ + component: ExperimentalSettingsPage, +}); + +function ExperimentalSettingsPage() { + const searchQuery = useSettingsSearchQuery(); + + const visibleItems = useMemo(() => { + if (!searchQuery) return null; + return getMatchingItemsForSection(searchQuery, "experimental").map( + (item) => item.id, + ); + }, [searchQuery]); + + return ; +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx index 5b1597aad31..2bff0a9e57c 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx @@ -39,6 +39,7 @@ const SECTION_ORDER: SettingsSection[] = [ "billing", "apikeys", "permissions", + "experimental", ]; function getSectionFromPath(pathname: string): SettingsSection | null { @@ -52,6 +53,7 @@ function getSectionFromPath(pathname: string): SettingsSection | null { if (pathname.includes("/settings/terminal")) return "terminal"; if (pathname.includes("/settings/links")) return "links"; if (pathname.includes("/settings/models")) return "models"; + if (pathname.includes("/settings/experimental")) return "experimental"; if (pathname.includes("/settings/integrations")) return "integrations"; if (pathname.includes("/settings/permissions")) return "permissions"; if (pathname.includes("/settings/project")) return "project"; @@ -80,6 +82,8 @@ function getPathFromSection(section: SettingsSection): string { return "/settings/links"; case "models": return "/settings/models"; + case "experimental": + return "/settings/experimental"; case "integrations": return "/settings/integrations"; case "permissions": diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts index eaf25734214..a332c1ad67c 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts @@ -46,6 +46,8 @@ export const SETTING_ITEM_ID = { MODELS_ANTHROPIC: "models-anthropic", MODELS_OPENAI: "models-openai", + EXPERIMENTAL_SUPERSET_V2: "experimental-superset-v2", + INTEGRATIONS_LINEAR: "integrations-linear", INTEGRATIONS_GITHUB: "integrations-github", INTEGRATIONS_SLACK: "integrations-slack", @@ -704,6 +706,26 @@ export const SETTINGS_ITEMS: SettingsItem[] = [ "auto name", ], }, + { + id: SETTING_ITEM_ID.EXPERIMENTAL_SUPERSET_V2, + section: "experimental", + title: "Try Superset Version 2 (Early Access)", + description: "Switch between Superset V1 and the new V2 experience", + keywords: [ + "experimental", + "experiments", + "v2", + "v1", + "version", + "early access", + "beta", + "preview", + "workspace", + "workspaces", + "toggle", + "switch", + ], + }, { id: SETTING_ITEM_ID.INTEGRATIONS_LINEAR, section: "integrations", diff --git a/apps/desktop/src/renderer/stores/settings-state.ts b/apps/desktop/src/renderer/stores/settings-state.ts index 98d18fe2599..e3a744e3151 100644 --- a/apps/desktop/src/renderer/stores/settings-state.ts +++ b/apps/desktop/src/renderer/stores/settings-state.ts @@ -13,6 +13,7 @@ export type SettingsSection = | "terminal" | "links" | "models" + | "experimental" | "integrations" | "billing" | "apikeys" diff --git a/apps/desktop/src/renderer/stores/v2-local-override.ts b/apps/desktop/src/renderer/stores/v2-local-override.ts index 01b66fb9885..c815a605aa2 100644 --- a/apps/desktop/src/renderer/stores/v2-local-override.ts +++ b/apps/desktop/src/renderer/stores/v2-local-override.ts @@ -4,15 +4,15 @@ import { devtools, persist } from "zustand/middleware"; interface V2LocalOverrideState { /** When true, forces v1 mode locally even though v2 is enabled remotely. */ forceV1: boolean; - toggle: () => void; + setForceV1: (forceV1: boolean) => void; } export const useV2LocalOverrideStore = create()( devtools( persist( - (set, get) => ({ + (set) => ({ forceV1: false, - toggle: () => set({ forceV1: !get().forceV1 }), + setForceV1: (forceV1) => set({ forceV1 }), }), { name: "v2-local-override" }, ),