From a2defb18b41f8a0b10b64f869a76fc31eb70b9cb Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Sun, 9 Jun 2024 15:48:20 -0500 Subject: [PATCH] add useFeature hook, only show workflows when enabled (#12) * add useFeature hook * only show workflows tab when workflow feature is enabled --- ui/src/components/Layout.tsx | 45 +++++++++++++++++++----------------- ui/src/hooks/use-feature.ts | 28 ++++++++++++++++++++++ ui/src/vite-env.d.ts | 1 + 3 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 ui/src/hooks/use-feature.ts diff --git a/ui/src/components/Layout.tsx b/ui/src/components/Layout.tsx index 73ec636..0d410f2 100644 --- a/ui/src/components/Layout.tsx +++ b/ui/src/components/Layout.tsx @@ -12,7 +12,7 @@ } ``` */ -import { Fragment, PropsWithChildren } from "react"; +import { Fragment, PropsWithChildren, useMemo } from "react"; import { Dialog, @@ -32,33 +32,37 @@ import { classNames } from "@utils/style"; import { Link } from "@tanstack/react-router"; import { JobState } from "@services/types"; import { useSidebarSetting } from "@contexts/SidebarSetting.hook"; +import useFeature from "@hooks/use-feature"; type LayoutProps = PropsWithChildren; -const navigation = [ - { - name: "Jobs", - href: "/jobs", - icon: QueueListIcon, - search: { state: JobState.Running }, - }, - { name: "Queues", href: "/queues", icon: InboxStackIcon }, - { name: "Workflows", href: "#", icon: RectangleGroupIcon }, -]; - const Layout = ({ children }: LayoutProps) => { const { open: sidebarOpen, setOpen: setSidebarOpen } = useSidebarSetting(); + const featureEnabledWorkflows = useFeature("ENABLE_WORKFLOWS", false); + + const navigation = useMemo( + () => + [ + { + name: "Jobs", + href: "/jobs", + icon: QueueListIcon, + search: { state: JobState.Running }, + }, + { name: "Queues", href: "/queues", icon: InboxStackIcon }, + { + name: "Workflows", + href: "#", + icon: RectangleGroupIcon, + hidden: !featureEnabledWorkflows, + }, + ].filter((item) => item.hidden === undefined || item.hidden === false), + [featureEnabledWorkflows] + ); + return ( <> - {/* - This example requires updating your template: - - ``` - - - ``` - */}
{
- {/* */} diff --git a/ui/src/hooks/use-feature.ts b/ui/src/hooks/use-feature.ts new file mode 100644 index 0000000..7a72e48 --- /dev/null +++ b/ui/src/hooks/use-feature.ts @@ -0,0 +1,28 @@ +import { useMemo } from "react"; + +const isNil = (val: string | null | undefined): boolean => + val === undefined || val === null; + +const getBoolVal = (val?: string | boolean | null): boolean => { + if (typeof val === "string") { + return val === "true"; + } + + return !!val; +}; + +const useFeature = (name: string, enabled = false): boolean => { + const featureEnabled = useMemo((): boolean => { + const localStorageValue = window?.localStorage?.getItem(`FEATURE_${name}`); + const envVar: string | undefined = import.meta.env[`VITE_FEATURE_${name}`]; + + if (!isNil(localStorageValue)) return getBoolVal(localStorageValue); + if (!isNil(envVar)) return getBoolVal(envVar); + + return !!enabled; + }, [name, enabled]); + + return featureEnabled; +}; + +export default useFeature; diff --git a/ui/src/vite-env.d.ts b/ui/src/vite-env.d.ts index 8ff4ccf..3e7e62a 100644 --- a/ui/src/vite-env.d.ts +++ b/ui/src/vite-env.d.ts @@ -2,6 +2,7 @@ /// interface ImportMetaEnv { + readonly VITE_FEATURE_ENABLE_WORKFLOWS: string; readonly VITE_RIVER_API_BASE_URL: string; }