diff --git a/src/Hub.tsx b/src/Hub.tsx new file mode 100644 index 0000000..17d12ac --- /dev/null +++ b/src/Hub.tsx @@ -0,0 +1,50 @@ +import { useQuery } from '@tanstack/react-query'; +import { IntegrationPicker } from './modules/integration-picker/IntegrationPicker'; +import { FeatureFlagProvider } from './shared/contexts/featureFlagContext'; +import { getSettings } from './shared/queries'; +import { HubModes } from './types/types'; + +interface HubProps { + mode: HubModes; + token: string; + apiUrl: string; + dashboardUrl: string; + height: string; + onSuccess?: () => void; + onClose?: () => void; + onCancel?: () => void; + accountId?: string; +} +export const Hub = ({ + mode, + token, + apiUrl, + dashboardUrl, + height, + onSuccess, + onClose, + onCancel, + accountId, +}: HubProps) => { + const { data: settings } = useQuery({ + queryKey: ['settings'], + queryFn: () => getSettings(apiUrl, token), + }); + + return ( + + {mode === 'integration-picker' && ( + + )} + + ); +}; diff --git a/src/StackOneHub.tsx b/src/StackOneHub.tsx index 054e770..0dc6e2d 100644 --- a/src/StackOneHub.tsx +++ b/src/StackOneHub.tsx @@ -13,6 +13,7 @@ import { } from '@stackone/malachite'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useEffect } from 'react'; +import { Hub } from './Hub'; import { CsvImporter } from './modules/csv-importer.tsx/CsvImporter'; import { IntegrationPicker } from './modules/integration-picker/IntegrationPicker'; import ErrorContainer from './shared/components/error'; @@ -119,18 +120,17 @@ export const StackOneHub: React.FC = ({ } > - {mode === 'integration-picker' && ( - - )} + diff --git a/src/modules/integration-picker/IntegrationPicker.tsx b/src/modules/integration-picker/IntegrationPicker.tsx index f1fbf12..7ccc590 100644 --- a/src/modules/integration-picker/IntegrationPicker.tsx +++ b/src/modules/integration-picker/IntegrationPicker.tsx @@ -1,4 +1,5 @@ import { Card } from '@stackone/malachite'; +import useFeatureFlags from '../../shared/hooks/useFeatureFlags'; import { IntegrationPickerContent } from './components/IntegrationPickerContent'; import CardFooter from './components/cardFooter'; import CardTitle from './components/cardTitle'; @@ -23,6 +24,8 @@ export const IntegrationPicker: React.FC = ({ onSuccess, dashboardUrl, }) => { + const isHubLinkAccountReleaseEnabled = useFeatureFlags('hub_link_account_release'); + const { // Data hubData, @@ -73,20 +76,22 @@ export const IntegrationPicker: React.FC = ({ } height={height} > - + {isHubLinkAccountReleaseEnabled && ( + + )} ); }; diff --git a/src/shared/contexts/featureFlagContext.tsx b/src/shared/contexts/featureFlagContext.tsx new file mode 100644 index 0000000..95fb896 --- /dev/null +++ b/src/shared/contexts/featureFlagContext.tsx @@ -0,0 +1,26 @@ +import { createContext, useMemo } from 'react'; +import { FeatureFlag } from '../types/featureFlags'; + +export const FeatureFlagContext = createContext<{ featureFlags: FeatureFlag[] }>({ + featureFlags: [], +}); + +export const FeatureFlagProvider = ({ + featureFlags, + children, +}: { + featureFlags: FeatureFlag[]; + children: React.ReactNode; +}) => { + const memoizedContextValue = useMemo( + () => ({ + featureFlags, + }), + [featureFlags], + ); + return ( + + {children} + + ); +}; diff --git a/src/shared/hooks/useFeatureFlags.ts b/src/shared/hooks/useFeatureFlags.ts new file mode 100644 index 0000000..8c49fbd --- /dev/null +++ b/src/shared/hooks/useFeatureFlags.ts @@ -0,0 +1,24 @@ +import { useContext } from 'react'; +import { FeatureFlagContext } from '../contexts/featureFlagContext'; +import { FeatureFlag } from '../types/featureFlags'; + +const isFeatureEnabled = ({ + featureFlags, + featureFlag, +}: { featureFlags: FeatureFlag[]; featureFlag: FeatureFlag }): boolean => { + return featureFlags.includes(featureFlag); +}; + +export const useFeatureFlags = (featureFlag: FeatureFlag): boolean => { + const { featureFlags } = useContext(FeatureFlagContext); + if (!featureFlags) { + throw new Error('useFeatureFlags must be used within a FeatureFlagProvider'); + } + + return isFeatureEnabled({ + featureFlags, + featureFlag, + }); +}; + +export default useFeatureFlags; diff --git a/src/shared/queries.ts b/src/shared/queries.ts new file mode 100644 index 0000000..ac13cc1 --- /dev/null +++ b/src/shared/queries.ts @@ -0,0 +1,12 @@ +import { getRequest } from './httpClient'; +import { FeatureFlag } from './types/featureFlags'; + +export const getSettings = async (baseUrl: string, token: string) => { + return await getRequest<{ enabled_features: FeatureFlag[] }>({ + url: `${baseUrl}/hub/settings`, + headers: { + 'Content-Type': 'application/json', + 'x-hub-session-token': token, + }, + }); +}; diff --git a/src/shared/types/featureFlags.ts b/src/shared/types/featureFlags.ts new file mode 100644 index 0000000..2b1df98 --- /dev/null +++ b/src/shared/types/featureFlags.ts @@ -0,0 +1 @@ +export type FeatureFlag = 'hub_link_account_release';