diff --git a/frontend/src/assets/img/stars.svg b/frontend/src/assets/img/stars.svg new file mode 100644 index 0000000000..dbc00d2cbb --- /dev/null +++ b/frontend/src/assets/img/stars.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/component/App.tsx b/frontend/src/component/App.tsx index ad70203d09..65a50202c2 100644 --- a/frontend/src/component/App.tsx +++ b/frontend/src/component/App.tsx @@ -20,7 +20,6 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import MaintenanceBanner from './maintenance/MaintenanceBanner'; import { styled } from '@mui/material'; import { InitialRedirect } from './InitialRedirect'; -import { Demo } from './demo/Demo'; const StyledContainer = styled('div')(() => ({ '& ul': { @@ -101,13 +100,6 @@ export const App = () => { - } - /> - diff --git a/frontend/src/component/demo/Demo.tsx b/frontend/src/component/demo/Demo.tsx index 9fcf68a4ca..3dafe512e8 100644 --- a/frontend/src/component/demo/Demo.tsx +++ b/frontend/src/component/demo/Demo.tsx @@ -5,6 +5,9 @@ import { createLocalStorage } from 'utils/createLocalStorage'; import { TOPICS } from './demo-topics'; import { DemoDialogWelcome } from './DemoDialog/DemoDialogWelcome/DemoDialogWelcome'; import { DemoDialogFinish } from './DemoDialog/DemoDialogFinish/DemoDialogFinish'; +import { DemoDialogPlans } from './DemoDialog/DemoDialogPlans/DemoDialogPlans'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { DemoBanner } from './DemoBanner/DemoBanner'; const defaultProgress = { welcomeOpen: true, @@ -13,14 +16,21 @@ const defaultProgress = { steps: [0], }; -const { value: storedProgress, setValue: setStoredProgress } = - createLocalStorage('Tutorial:v1', defaultProgress); +interface IDemoProps { + children: JSX.Element; +} + +export const Demo = ({ children }: IDemoProps): JSX.Element => { + const { uiConfig } = useUiConfig(); + + const { value: storedProgress, setValue: setStoredProgress } = + createLocalStorage('Tutorial:v1', defaultProgress); -export const Demo = () => { const [welcomeOpen, setWelcomeOpen] = useState( storedProgress.welcomeOpen ?? defaultProgress.welcomeOpen ); const [finishOpen, setFinishOpen] = useState(false); + const [plansOpen, setPlansOpen] = useState(false); const [expanded, setExpanded] = useState( storedProgress.expanded ?? defaultProgress.expanded @@ -59,8 +69,16 @@ export const Demo = () => { } }; + if (!uiConfig.flags.demo) return children; + return ( <> + { + setPlansOpen(true); + }} + /> + {children} { @@ -76,12 +94,17 @@ export const Demo = () => { open={finishOpen} onClose={() => { setFinishOpen(false); + setPlansOpen(true); }} onRestart={() => { setFinishOpen(false); onStart(); }} /> + setPlansOpen(false)} + /> ({ + position: 'sticky', + top: 0, + zIndex: theme.zIndex.appBar, + display: 'flex', + gap: theme.spacing(1), + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.palette.web.main, + color: theme.palette.web.contrastText, + padding: theme.spacing(1), +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + whiteSpace: 'nowrap', + flexShrink: 0, + '&&&': { + fontSize: theme.fontSizes.smallBody, + }, +})); + +const StyledQuestionsButton = styled(StyledButton)(({ theme }) => ({ + color: theme.palette.web.contrastText, + border: `1px solid rgba(255, 255, 255, 0.5)`, +})) as typeof Button; + +interface IDemoBannerProps { + onPlans: () => void; +} + +export const DemoBanner = ({ onPlans }: IDemoBannerProps) => ( + + + This is a demo of Unleash. Play around as much as + you want. Reach out when you're ready. + + + Ask questions + + + Get Unleash + + +); diff --git a/frontend/src/component/demo/DemoDialog/DemoDialog.tsx b/frontend/src/component/demo/DemoDialog/DemoDialog.tsx index c9e90171c8..3a73f1102c 100644 --- a/frontend/src/component/demo/DemoDialog/DemoDialog.tsx +++ b/frontend/src/component/demo/DemoDialog/DemoDialog.tsx @@ -1,4 +1,10 @@ -import { Dialog, IconButton, Typography, styled } from '@mui/material'; +import { + Dialog, + DialogProps, + IconButton, + Typography, + styled, +} from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; const StyledDialog = styled(Dialog)(({ theme }) => ({ @@ -22,14 +28,19 @@ const StyledHeader = styled(Typography)(({ theme }) => ({ fontWeight: theme.fontWeight.bold, })); -interface IDemoDialogProps { +interface IDemoDialogProps extends DialogProps { open: boolean; onClose: () => void; children: React.ReactNode; } -export const DemoDialog = ({ open, onClose, children }: IDemoDialogProps) => ( - +export const DemoDialog = ({ + open, + onClose, + children, + ...props +}: IDemoDialogProps) => ( + diff --git a/frontend/src/component/demo/DemoDialog/DemoDialogFinish/DemoDialogFinish.tsx b/frontend/src/component/demo/DemoDialog/DemoDialogFinish/DemoDialogFinish.tsx index 98e17f1af7..b278ad3209 100644 --- a/frontend/src/component/demo/DemoDialog/DemoDialogFinish/DemoDialogFinish.tsx +++ b/frontend/src/component/demo/DemoDialog/DemoDialogFinish/DemoDialogFinish.tsx @@ -39,11 +39,11 @@ export const DemoDialogFinish = ({ } /> - You finished the tutorial + You finished the demo Great job! Keep exploring Unleash, as this was just a small - example of its full potential. You can do the tutorial again at - any moment. + example of its full potential. You can do the demo again at any + moment. - Restart tutorial + Restart demo - Close + Continue diff --git a/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx b/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx new file mode 100644 index 0000000000..193adf1749 --- /dev/null +++ b/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx @@ -0,0 +1,130 @@ +import { Button, Typography, styled } from '@mui/material'; +import { DemoDialog } from '../DemoDialog'; +import { GitHub } from '@mui/icons-material'; +import { Launch } from '@mui/icons-material'; + +const StyledDemoDialog = styled(DemoDialog)(({ theme }) => ({ + '& .MuiDialog-paper': { + maxWidth: theme.spacing(120), + }, +})); + +const StyledPlans = styled('div')(({ theme }) => ({ + display: 'grid', + gridTemplateColumns: 'auto auto auto', + gap: theme.spacing(1), + marginTop: theme.spacing(6), +})); + +const StyledPlan = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + backgroundColor: theme.palette.background.elevation1, + borderRadius: theme.shape.borderRadiusLarge, + padding: theme.spacing(4, 3), + '& > a': { + whiteSpace: 'nowrap', + }, + height: theme.spacing(34), + width: theme.spacing(34), +})); + +const StyledCompareLink = styled('a')(({ theme }) => ({ + fontSize: theme.fontSizes.mainHeader, + textDecoration: 'none', + '&:hover': { + textDecoration: 'underline', + }, + margin: 'auto', + marginTop: theme.spacing(4), + display: 'inline-flex', + alignItems: 'center', + gap: theme.spacing(1), + '& > svg': { + fontSize: theme.fontSizes.mainHeader, + }, +})); + +interface IDemoDialogPlansProps { + open: boolean; + onClose: () => void; +} + +export const DemoDialogPlans = ({ open, onClose }: IDemoDialogPlansProps) => ( + + Want to keep going with Unleash? + + + + Open Source + + + Self-hosted basic feature management solution + + + Free + + + + + + Pro + + + Free your team to collaborate. We'll do the heavy lifting. + +
+ + $80/month + + includes 5 seats +
+ +
+ + + Enterprise + + + Security, compliance, and development controls for scale. + +
+ + Custom + + unlimited seats +
+ +
+
+ + Compare plans + +
+); diff --git a/frontend/src/component/demo/DemoDialog/DemoDialogWelcome/DemoDialogWelcome.tsx b/frontend/src/component/demo/DemoDialog/DemoDialogWelcome/DemoDialogWelcome.tsx index 16763ec99c..204ecc2b1e 100644 --- a/frontend/src/component/demo/DemoDialog/DemoDialogWelcome/DemoDialogWelcome.tsx +++ b/frontend/src/component/demo/DemoDialog/DemoDialogWelcome/DemoDialogWelcome.tsx @@ -60,8 +60,8 @@ export const DemoDialogWelcome = ({ Explore Unleash You can explore Unleash on your own, however for the best experience - it's recommended you follow our interactive tutorial. To get - started, you will need to open the demo website below. + it's recommended you follow our interactive demo. To get started, + you will need to open the demo website below. @@ -87,7 +87,7 @@ export const DemoDialogWelcome = ({ color="primary" onClick={onStart} > - Start Unleash tutorial + Try Unleash demo ); diff --git a/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx b/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx new file mode 100644 index 0000000000..08401cc503 --- /dev/null +++ b/frontend/src/component/demo/DemoSteps/DemoStepTooltip/DemoStepTooltip.tsx @@ -0,0 +1,203 @@ +import { + Button, + Dialog, + IconButton, + Typography, + alpha, + styled, +} from '@mui/material'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { ITutorialTopic, ITutorialTopicStep } from 'component/demo/demo-topics'; +import { TooltipRenderProps } from 'react-joyride'; +import CloseIcon from '@mui/icons-material/Close'; + +const StyledDialog = styled(Dialog)(({ theme }) => ({ + '& .MuiDialog-paper': { + borderRadius: theme.shape.borderRadiusMedium, + width: '100%', + maxWidth: theme.spacing(45), + padding: theme.spacing(3), + }, +})); + +const StyledTooltip = styled('div')(({ theme }) => ({ + '@keyframes pulse': { + '0%': { + boxShadow: `0 0 0 0 ${alpha(theme.palette.primary.main, 0.7)}`, + }, + '70%': { + boxShadow: `0 0 0 10px ${alpha(theme.palette.primary.main, 0)}`, + }, + '100%': { + boxShadow: `0 0 0 0 ${alpha(theme.palette.primary.main, 0)}`, + }, + }, + position: 'relative', + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + borderRadius: theme.shape.borderRadiusMedium, + width: '100%', + maxWidth: theme.spacing(45), + padding: theme.spacing(3), +})); + +const StyledCloseButton = styled(IconButton)(({ theme }) => ({ + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.neutral.main, +})); + +const StyledTooltipTitle = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + marginBottom: theme.spacing(1), + flexWrap: 'wrap', + paddingRight: theme.spacing(4), +})); + +const StyledTooltipActions = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + marginTop: theme.spacing(3), + '&&& button': { + fontSize: theme.fontSizes.smallBody, + }, +})); + +export interface IDemoStepTooltipProps extends TooltipRenderProps { + step: ITutorialTopicStep; + topic: number; + topics: ITutorialTopic[]; + steps: number[]; + onClose: () => void; + onBack: (step: ITutorialTopicStep) => void; + onNext: (step: number) => void; +} + +export const DemoStepTooltip = ({ + tooltipProps, + step, + topic, + topics, + steps, + onClose, + onBack, + onNext, +}: IDemoStepTooltipProps) => { + if (step.target === 'body') { + return ( +
+ { + if (r !== 'backdropClick') onClose(); + }} + transitionDuration={0} + > + + + + + + {topics[topic].title} + + } + /> + + {step.content} + +
+ 0 || steps[topic] > 0} + show={ + + } + /> +
+
+ onNext(steps[topic])} + variant="contained" + sx={{ alignSelf: 'flex-end' }} + > + {topic === topics.length - 1 && + steps[topic] === + topics[topic].steps.length - 1 + ? 'Finish' + : 'Next'} + + } + /> +
+
+
+
+ ); + } + + return ( + + + + + + + {topics[topic].title} + + } + /> + + {step.content} + +
+ 0 || steps[topic] > 0} + show={ + + } + /> +
+
+ onNext(steps[topic])} + variant="contained" + sx={{ alignSelf: 'flex-end' }} + > + {topic === topics.length - 1 && + steps[topic] === topics[topic].steps.length - 1 + ? 'Finish' + : 'Next'} + + } + /> +
+
+
+ ); +}; diff --git a/frontend/src/component/demo/DemoSteps/DemoSteps.tsx b/frontend/src/component/demo/DemoSteps/DemoSteps.tsx index 3093d0a1a5..1d146a5288 100644 --- a/frontend/src/component/demo/DemoSteps/DemoSteps.tsx +++ b/frontend/src/component/demo/DemoSteps/DemoSteps.tsx @@ -3,45 +3,11 @@ import Joyride, { CallBackProps, TooltipRenderProps, } from 'react-joyride'; -import { Button, Typography, styled, useTheme } from '@mui/material'; +import { useTheme } from '@mui/material'; import { ITutorialTopic, ITutorialTopicStep } from '../demo-topics'; import { useEffect, useState } from 'react'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useLocation, useNavigate } from 'react-router-dom'; - -const StyledTooltip = styled('div')(({ theme }) => ({ - backgroundColor: theme.palette.background.paper, - color: theme.palette.text.primary, - borderRadius: theme.shape.borderRadiusMedium, - width: '100%', - maxWidth: theme.spacing(45), - padding: theme.spacing(3), -})); - -const StyledTooltipTitle = styled('div')(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1), - marginBottom: theme.spacing(1), - flexWrap: 'wrap', -})); - -const StyledTooltipActions = styled('div')(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', - marginTop: theme.spacing(3), - '&&& button': { - '&:first-of-type': { - marginLeft: theme.spacing(-2), - }, - fontSize: theme.fontSizes.smallBody, - }, -})); - -const StyledTooltipPrimaryActions = styled('div')(({ theme }) => ({ - display: 'flex', - gap: theme.spacing(1), -})); +import { DemoStepTooltip } from './DemoStepTooltip/DemoStepTooltip'; interface IDemoStepsProps { setExpanded: React.Dispatch>; @@ -82,10 +48,9 @@ export const DemoSteps = ({ } }; - const skip = () => { + const close = () => { abortController.abort(); setTopicStep(-1); - setExpanded(false); }; const back = () => { @@ -124,6 +89,10 @@ export const DemoSteps = ({ ) => { const { action, index, step } = data; + if (action === ACTIONS.CLOSE) { + close(); + } + if (action === ACTIONS.UPDATE) { const el = document.querySelector(step.target as string); if (el) { @@ -237,78 +206,27 @@ export const DemoSteps = ({ border: `2px solid ${theme.palette.primary.main}`, outline: `2px solid ${theme.palette.secondary.border}`, backgroundColor: 'transparent', + animation: 'pulse 2s infinite', }, overlay: { backgroundColor: 'transparent', mixBlendMode: 'unset', }, }} - tooltipComponent={({ - step, - tooltipProps, - }: TooltipRenderProps & { - step: ITutorialTopicStep; - }) => ( - - - - {topics[topic].title} - - } - /> - 1} - show={ - - (step {steps[topic] + 1} of{' '} - {topics[topic].steps.length}) - - } - /> - - {step.content} - - - - 0 || steps[topic] > 0} - show={ - - } - /> - next(steps[topic])} - variant="contained" - > - {topic === topics.length - 1 && - steps[topic] === - topics[topic].steps.length - 1 - ? 'Finish' - : 'Next'} - - } - /> - - - + tooltipComponent={( + props: TooltipRenderProps & { + step: ITutorialTopicStep; + } + ) => ( + )} /> ); diff --git a/frontend/src/component/demo/DemoTopics/DemoTopics.tsx b/frontend/src/component/demo/DemoTopics/DemoTopics.tsx index e96bb22ceb..e464a6fdca 100644 --- a/frontend/src/component/demo/DemoTopics/DemoTopics.tsx +++ b/frontend/src/component/demo/DemoTopics/DemoTopics.tsx @@ -11,6 +11,7 @@ import { import { CheckCircle, CircleOutlined, ExpandMore } from '@mui/icons-material'; import { ITutorialTopic } from '../demo-topics'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { ReactComponent as StarsIcon } from 'assets/img/stars.svg'; const StyledAccordion = styled(Accordion)(({ theme }) => ({ position: 'fixed', @@ -47,8 +48,15 @@ const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({ color: theme.palette.primary.contrastText, borderTopLeftRadius: theme.shape.borderRadiusLarge, borderTopRightRadius: theme.shape.borderRadiusLarge, + height: 91, })); +const StyledStars = styled(StarsIcon)({ + position: 'absolute', + left: 6, + top: -24, +}); + const StyledExpandMoreIcon = styled(ExpandMore)(({ theme }) => ({ color: theme.palette.primary.contrastText, })); @@ -99,7 +107,7 @@ const StyledStep = styled('li', { ...(selected && { backgroundColor: theme.palette.secondary.light, fontWeight: theme.typography.fontWeightBold, - border: `1px solid ${theme.palette.primary.main}`, + outline: `1px solid ${theme.palette.primary.main}`, }), ...(completed && { backgroundColor: theme.palette.background.elevation1, @@ -160,12 +168,13 @@ export const DemoTopics = ({ onChange={() => setExpanded(expanded => !expanded)} > + - Unleash tutorial + Unleash demo - Complete all steps to finish tutorial + Complete all steps to finish demo @@ -204,7 +213,7 @@ export const DemoTopics = ({ ); })} - View demo link again + View demo page diff --git a/frontend/src/component/demo/demo-setup.ts b/frontend/src/component/demo/demo-setup.ts index da9183b7d2..596b39af87 100644 --- a/frontend/src/component/demo/demo-setup.ts +++ b/frontend/src/component/demo/demo-setup.ts @@ -1,25 +1,25 @@ import { IFeatureToggle } from 'interfaces/featureToggle'; import { formatApiPath } from 'utils/formatPath'; +const PROJECT = 'demo-app'; +const ENVIRONMENT = 'dev'; + export const gradualRollout = async () => { - const projectId = 'default'; const featureId = 'demoApp.step3'; - const environmentId = 'default'; const { environments }: IFeatureToggle = await fetch( formatApiPath( - `api/admin/projects/${projectId}/features/${featureId}?variantEnvironments=true` + `api/admin/projects/${PROJECT}/features/${featureId}?variantEnvironments=true` ) ).then(res => res.json()); const strategies = - environments.find(({ name }) => name === environmentId)?.strategies || - []; + environments.find(({ name }) => name === ENVIRONMENT)?.strategies || []; if (!strategies.find(({ name }) => name === 'flexibleRollout')) { await fetch( formatApiPath( - `api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies` + `api/admin/projects/${PROJECT}/features/${featureId}/environments/${ENVIRONMENT}/strategies` ), { method: 'POST', @@ -41,20 +41,18 @@ export const gradualRollout = async () => { }; export const variants = async () => { - const projectId = 'default'; const featureId = 'demoApp.step4'; - const environmentId = 'default'; const { variants }: IFeatureToggle = await fetch( formatApiPath( - `api/admin/projects/${projectId}/features/${featureId}?variantEnvironments=true` + `api/admin/projects/${PROJECT}/features/${featureId}?variantEnvironments=true` ) ).then(res => res.json()); if (!variants.length) { await fetch( formatApiPath( - `api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/variants` + `api/admin/projects/${PROJECT}/features/${featureId}/environments/${ENVIRONMENT}/variants` ), { method: 'PATCH', diff --git a/frontend/src/component/demo/demo-topics.tsx b/frontend/src/component/demo/demo-topics.tsx index a4d2aeae37..856114a691 100644 --- a/frontend/src/component/demo/demo-topics.tsx +++ b/frontend/src/component/demo/demo-topics.tsx @@ -3,6 +3,7 @@ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import { Badge } from 'component/common/Badge/Badge'; import { Step } from 'react-joyride'; import { gradualRollout, variants } from './demo-setup'; +import { basePath } from 'utils/formatPath'; export interface ITutorialTopicStep extends Step { href?: string; @@ -24,12 +25,15 @@ const Description = (props: TypographyProps) => ( ); +const PROJECT = 'demo-app'; +const ENVIRONMENT = 'dev'; + export const TOPICS: ITutorialTopic[] = [ { title: 'Enable/disable a feature toggle', steps: [ { - href: '/projects/default', + href: `/projects/${PROJECT}`, target: 'body', placement: 'center', content: ( @@ -58,8 +62,8 @@ export const TOPICS: ITutorialTopic[] = [ nextButton: true, }, { - href: '/projects/default', - target: 'div[data-testid="TOGGLE-demoApp.step1-default"]', + href: `/projects/${PROJECT}`, + target: `div[data-testid="TOGGLE-demoApp.step1-${ENVIRONMENT}"]`, content: ( <> @@ -82,7 +86,7 @@ export const TOPICS: ITutorialTopic[] = [ title: 'Enable for a specific user', steps: [ { - href: '/projects/default', + href: `/projects/${PROJECT}`, target: 'body', placement: 'center', content: ( @@ -106,19 +110,19 @@ export const TOPICS: ITutorialTopic[] = [ nextButton: true, }, { - href: '/projects/default', - target: 'a[href="/projects/default/features/demoApp.step2"]', + href: `/projects/${PROJECT}`, + target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step2"]`, content: ( First, let's open the feature toggle configuration for{' '} - demoApp.step2. + demoApp.step2 ), preventDefault: true, }, { - href: '/projects/default/features/demoApp.step2', - target: 'div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_default"] button', + href: `/projects/${PROJECT}/features/demoApp.step2`, + target: `div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_${ENVIRONMENT}"] button`, content: ( Add a new strategy to this environment by clicking this @@ -127,7 +131,7 @@ export const TOPICS: ITutorialTopic[] = [ ), }, { - target: 'a[href="/projects/default/features/demoApp.step2/strategies/create?environmentId=default&strategyName=default"]', + target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step2/strategies/create?environmentId=${ENVIRONMENT}&strategyName=default"]`, content: ( Select the Standard strategy @@ -197,7 +201,7 @@ export const TOPICS: ITutorialTopic[] = [ content: ( <> - Enter your userId. + Enter your userId ), - backCloseModal: true, nextButton: true, }, { @@ -217,7 +220,6 @@ export const TOPICS: ITutorialTopic[] = [ { target: 'button[data-testid="CONSTRAINT_SAVE_BUTTON"]', content: Save the constraint., - backCloseModal: true, }, { target: 'button[data-testid="STRATEGY_FORM_SUBMIT_ID"]', @@ -259,7 +261,7 @@ export const TOPICS: ITutorialTopic[] = [ setup: gradualRollout, steps: [ { - href: '/projects/default', + href: `/projects/${PROJECT}`, target: 'body', placement: 'center', content: ( @@ -289,19 +291,19 @@ export const TOPICS: ITutorialTopic[] = [ nextButton: true, }, { - href: '/projects/default', - target: 'a[href="/projects/default/features/demoApp.step3"]', + href: `/projects/${PROJECT}`, + target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step3"]`, content: ( First, let's open the feature toggle configuration for{' '} - demoApp.step3. + demoApp.step3 ), preventDefault: true, }, { - href: '/projects/default/features/demoApp.step3', - target: 'div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_default"] .MuiAccordionSummary-expandIconWrapper', + href: `/projects/${PROJECT}/features/demoApp.step3`, + target: `div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_${ENVIRONMENT}"] .MuiAccordionSummary-expandIconWrapper`, content: ( Expand the environment card to see all the defined @@ -310,7 +312,7 @@ export const TOPICS: ITutorialTopic[] = [ ), }, { - target: 'div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_default"].Mui-expanded a[data-testid="STRATEGY_EDIT-flexibleRollout"]', + target: `div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_${ENVIRONMENT}"].Mui-expanded a[data-testid="STRATEGY_EDIT-flexibleRollout"]`, content: ( Edit the existing gradual rollout strategy. @@ -365,7 +367,7 @@ export const TOPICS: ITutorialTopic[] = [ setup: variants, steps: [ { - href: '/projects/default', + href: `/projects/${PROJECT}`, target: 'body', placement: 'center', content: ( @@ -390,18 +392,18 @@ export const TOPICS: ITutorialTopic[] = [ nextButton: true, }, { - href: '/projects/default', - target: 'a[href="/projects/default/features/demoApp.step4"]', + href: `/projects/${PROJECT}`, + target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step4"]`, content: ( First, let's open the feature toggle configuration for{' '} - demoApp.step4. + demoApp.step4 ), preventDefault: true, }, { - href: '/projects/default/features/demoApp.step4', + href: `/projects/${PROJECT}/features/demoApp.step4`, target: 'button[data-testid="TAB-Variants"]', content: Select the variants tab., }, @@ -432,7 +434,7 @@ export const TOPICS: ITutorialTopic[] = [ . - Example: aqua. + Example: aqua ), @@ -485,7 +487,7 @@ export const TOPICS: ITutorialTopic[] = [ content: ( <> - Enter your userId. + Enter your userId ( return ( <> -
- - - - } - /> - - - - - {children} - - - + <> +
+ + + + + } /> - } - lightmode={ - + + + + {children} + + + + } + lightmode={ + + } /> - } - /> - -