mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: demo ui-ux improvements (#3634)
https://linear.app/unleash/issue/2-914/improve-demo-guide-uiux Includes a big batch of UI/UX improvements, including but not limited to: - Updating steps text; - Improve behavior of intro step dialogs (use normal dialogs); - Improve overall design; - Improve escape key and backdrop click behaviors; - Add plans dialog; - Add sticky demo banner; - Assume `demo-app` project and `dev` environment to better fit our demo instance; - Misc fixes and refactors; Relates to [roadmap](https://github.com/orgs/Unleash/projects/10) item: #3537 ![image](https://user-images.githubusercontent.com/14320932/234637210-1936fd48-ce40-4980-81ae-f1fe64e65545.png)
This commit is contained in:
parent
21640481ab
commit
3599e7478c
9
frontend/src/assets/img/stars.svg
Normal file
9
frontend/src/assets/img/stars.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg width="45" height="61" viewBox="0 0 45 61" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path opacity="0.789" d="M29.701 38.852C25.591 37.941 24.679 37.007 23.764 32.915C23.664 32.469 23.764 32 23.306 32C22.848 32 22.937 32.469 22.848 32.915C21.937 37.044 21.022 37.94 16.911 38.852C16.465 38.952 16 38.852 16 39.31C16 39.768 16.465 39.679 16.911 39.768C21.021 40.679 21.936 41.613 22.848 45.705C22.948 46.151 22.848 46.62 23.306 46.62C23.764 46.62 23.675 46.151 23.764 45.705C24.679 41.595 25.609 40.68 29.701 39.768C30.147 39.668 30.616 39.768 30.616 39.31C30.616 38.852 30.148 38.952 29.701 38.852Z" fill="white"/>
|
||||||
|
<path opacity="0.4" d="M10.686 52.1838C7.46503 51.4728 6.75403 50.7558 6.06003 47.5578C5.97903 47.2048 6.06003 46.8408 5.70103 46.8408C5.34203 46.8408 5.41803 47.2048 5.34203 47.5578C4.62503 50.7788 3.90803 51.4958 0.716026 52.1838C0.369026 52.2648 -0.000976562 52.1838 -0.000976562 52.5428C-0.000976562 52.9018 0.369026 52.8258 0.716026 52.9018C3.93703 53.6188 4.65403 54.3358 5.34203 57.5568C5.41703 57.9038 5.34203 58.2738 5.70103 58.2738C6.06003 58.2738 5.97903 57.9038 6.06003 57.5568C6.77703 54.3358 7.48803 53.6188 10.686 52.9018C11.039 52.8268 11.403 52.9018 11.403 52.5428C11.403 52.1838 11.039 52.2648 10.686 52.1838Z" fill="white"/>
|
||||||
|
<path opacity="0.739" d="M34.19 12.595C31.434 11.981 30.825 11.37 30.209 8.612C30.142 8.312 30.209 8 29.902 8C29.595 8 29.662 8.313 29.595 8.612C28.983 11.37 28.37 11.977 25.614 12.595C25.314 12.66 25 12.595 25 12.902C25 13.209 25.315 13.142 25.614 13.202C28.37 13.816 28.979 14.427 29.595 17.185C29.662 17.485 29.595 17.797 29.902 17.797C30.209 17.797 30.142 17.484 30.209 17.185C30.821 14.427 31.434 13.82 34.19 13.202C34.49 13.137 34.804 13.202 34.804 12.902C34.804 12.602 34.49 12.66 34.19 12.595Z" fill="white"/>
|
||||||
|
<path opacity="0.7" d="M38.666 55.832C36.366 55.32 35.859 54.81 35.345 52.51C35.289 52.261 35.345 52 35.089 52C34.833 52 34.889 52.261 34.833 52.51C34.323 54.81 33.811 55.317 31.512 55.832C31.263 55.886 31 55.832 31 56.088C31 56.344 31.263 56.288 31.512 56.342C33.812 56.854 34.319 57.364 34.833 59.664C34.889 59.913 34.833 60.174 35.089 60.174C35.345 60.174 35.289 59.913 35.345 59.664C35.855 57.364 36.367 56.857 38.666 56.342C38.917 56.287 39.178 56.342 39.178 56.088C39.178 55.834 38.917 55.886 38.666 55.832Z" fill="white"/>
|
||||||
|
<path opacity="0.855" d="M19.336 2.668C17.736 2.312 17.382 1.95598 17.024 0.35498C16.985 0.18098 17.024 0 16.846 0C16.668 0 16.707 0.18198 16.668 0.35498C16.313 1.95498 15.956 2.309 14.356 2.668C14.182 2.706 14 2.66798 14 2.84598C14 3.02398 14.183 2.98498 14.356 3.02298C15.956 3.37898 16.31 3.735 16.668 5.336C16.707 5.51 16.668 5.69098 16.846 5.69098C17.024 5.69098 16.985 5.509 17.024 5.336C17.379 3.736 17.736 3.38198 19.336 3.02298C19.511 2.98498 19.692 3.02298 19.692 2.84598C19.692 2.66898 19.511 2.706 19.336 2.668Z" fill="white"/>
|
||||||
|
<path opacity="0.855" d="M11.336 25.668C9.736 25.312 9.382 24.956 9.024 23.355C8.985 23.181 9.024 23 8.846 23C8.668 23 8.707 23.182 8.668 23.355C8.313 24.955 7.956 25.309 6.356 25.668C6.182 25.706 6 25.668 6 25.846C6 26.024 6.183 25.985 6.356 26.023C7.956 26.379 8.31 26.735 8.668 28.336C8.707 28.51 8.668 28.691 8.846 28.691C9.024 28.691 8.985 28.509 9.024 28.336C9.379 26.736 9.736 26.382 11.336 26.023C11.511 25.985 11.692 26.023 11.692 25.846C11.692 25.669 11.511 25.706 11.336 25.668Z" fill="white"/>
|
||||||
|
<path opacity="0.5" d="M44.336 28.668C42.736 28.312 42.382 27.956 42.024 26.355C41.985 26.181 42.024 26 41.846 26C41.668 26 41.707 26.182 41.668 26.355C41.313 27.955 40.956 28.309 39.356 28.668C39.182 28.706 39 28.668 39 28.846C39 29.024 39.183 28.985 39.356 29.023C40.956 29.379 41.31 29.735 41.668 31.336C41.707 31.51 41.668 31.691 41.846 31.691C42.024 31.691 41.985 31.509 42.024 31.336C42.379 29.736 42.736 29.382 44.336 29.023C44.511 28.985 44.692 29.023 44.692 28.846C44.692 28.669 44.511 28.706 44.336 28.668Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
@ -20,7 +20,6 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|||||||
import MaintenanceBanner from './maintenance/MaintenanceBanner';
|
import MaintenanceBanner from './maintenance/MaintenanceBanner';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import { InitialRedirect } from './InitialRedirect';
|
import { InitialRedirect } from './InitialRedirect';
|
||||||
import { Demo } from './demo/Demo';
|
|
||||||
|
|
||||||
const StyledContainer = styled('div')(() => ({
|
const StyledContainer = styled('div')(() => ({
|
||||||
'& ul': {
|
'& ul': {
|
||||||
@ -101,13 +100,6 @@ export const App = () => {
|
|||||||
|
|
||||||
<FeedbackNPS openUrl="http://feedback.unleash.run" />
|
<FeedbackNPS openUrl="http://feedback.unleash.run" />
|
||||||
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(
|
|
||||||
uiConfig.flags.demo
|
|
||||||
)}
|
|
||||||
show={<Demo />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SplashPageRedirect />
|
<SplashPageRedirect />
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</>
|
</>
|
||||||
|
@ -5,6 +5,9 @@ import { createLocalStorage } from 'utils/createLocalStorage';
|
|||||||
import { TOPICS } from './demo-topics';
|
import { TOPICS } from './demo-topics';
|
||||||
import { DemoDialogWelcome } from './DemoDialog/DemoDialogWelcome/DemoDialogWelcome';
|
import { DemoDialogWelcome } from './DemoDialog/DemoDialogWelcome/DemoDialogWelcome';
|
||||||
import { DemoDialogFinish } from './DemoDialog/DemoDialogFinish/DemoDialogFinish';
|
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 = {
|
const defaultProgress = {
|
||||||
welcomeOpen: true,
|
welcomeOpen: true,
|
||||||
@ -13,14 +16,21 @@ const defaultProgress = {
|
|||||||
steps: [0],
|
steps: [0],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { value: storedProgress, setValue: setStoredProgress } =
|
interface IDemoProps {
|
||||||
createLocalStorage('Tutorial:v1', defaultProgress);
|
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(
|
const [welcomeOpen, setWelcomeOpen] = useState(
|
||||||
storedProgress.welcomeOpen ?? defaultProgress.welcomeOpen
|
storedProgress.welcomeOpen ?? defaultProgress.welcomeOpen
|
||||||
);
|
);
|
||||||
const [finishOpen, setFinishOpen] = useState(false);
|
const [finishOpen, setFinishOpen] = useState(false);
|
||||||
|
const [plansOpen, setPlansOpen] = useState(false);
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(
|
const [expanded, setExpanded] = useState(
|
||||||
storedProgress.expanded ?? defaultProgress.expanded
|
storedProgress.expanded ?? defaultProgress.expanded
|
||||||
@ -59,8 +69,16 @@ export const Demo = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!uiConfig.flags.demo) return children;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<DemoBanner
|
||||||
|
onPlans={() => {
|
||||||
|
setPlansOpen(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
<DemoDialogWelcome
|
<DemoDialogWelcome
|
||||||
open={welcomeOpen}
|
open={welcomeOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@ -76,12 +94,17 @@ export const Demo = () => {
|
|||||||
open={finishOpen}
|
open={finishOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setFinishOpen(false);
|
setFinishOpen(false);
|
||||||
|
setPlansOpen(true);
|
||||||
}}
|
}}
|
||||||
onRestart={() => {
|
onRestart={() => {
|
||||||
setFinishOpen(false);
|
setFinishOpen(false);
|
||||||
onStart();
|
onStart();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<DemoDialogPlans
|
||||||
|
open={plansOpen}
|
||||||
|
onClose={() => setPlansOpen(false)}
|
||||||
|
/>
|
||||||
<DemoTopics
|
<DemoTopics
|
||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
setExpanded={setExpanded}
|
setExpanded={setExpanded}
|
||||||
|
51
frontend/src/component/demo/DemoBanner/DemoBanner.tsx
Normal file
51
frontend/src/component/demo/DemoBanner/DemoBanner.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Button, styled } from '@mui/material';
|
||||||
|
|
||||||
|
const StyledBanner = styled('div')(({ theme }) => ({
|
||||||
|
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) => (
|
||||||
|
<StyledBanner>
|
||||||
|
<span>
|
||||||
|
This is a <strong>demo of Unleash</strong>. Play around as much as
|
||||||
|
you want. Reach out when you're ready.
|
||||||
|
</span>
|
||||||
|
<StyledQuestionsButton
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ ml: 1 }}
|
||||||
|
href="https://slack.unleash.run/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Ask questions
|
||||||
|
</StyledQuestionsButton>
|
||||||
|
<StyledButton variant="contained" color="primary" onClick={onPlans}>
|
||||||
|
Get Unleash
|
||||||
|
</StyledButton>
|
||||||
|
</StyledBanner>
|
||||||
|
);
|
@ -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';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
|
||||||
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
||||||
@ -22,14 +28,19 @@ const StyledHeader = styled(Typography)(({ theme }) => ({
|
|||||||
fontWeight: theme.fontWeight.bold,
|
fontWeight: theme.fontWeight.bold,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IDemoDialogProps {
|
interface IDemoDialogProps extends DialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DemoDialog = ({ open, onClose, children }: IDemoDialogProps) => (
|
export const DemoDialog = ({
|
||||||
<StyledDialog open={open} onClose={onClose}>
|
open,
|
||||||
|
onClose,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: IDemoDialogProps) => (
|
||||||
|
<StyledDialog open={open} onClose={onClose} {...props}>
|
||||||
<StyledCloseButton aria-label="close" onClick={onClose}>
|
<StyledCloseButton aria-label="close" onClick={onClose}>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</StyledCloseButton>
|
</StyledCloseButton>
|
||||||
|
@ -39,11 +39,11 @@ export const DemoDialogFinish = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<DemoDialog open={open} onClose={onClose}>
|
<DemoDialog open={open} onClose={onClose}>
|
||||||
<DemoDialog.Header>You finished the tutorial</DemoDialog.Header>
|
<DemoDialog.Header>You finished the demo</DemoDialog.Header>
|
||||||
<Typography color="textSecondary" sx={{ mt: 4 }}>
|
<Typography color="textSecondary" sx={{ mt: 4 }}>
|
||||||
Great job! Keep exploring Unleash, as this was just a small
|
Great job! Keep exploring Unleash, as this was just a small
|
||||||
example of its full potential. You can do the tutorial again at
|
example of its full potential. You can do the demo again at any
|
||||||
any moment.
|
moment.
|
||||||
</Typography>
|
</Typography>
|
||||||
<StyledActions>
|
<StyledActions>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
@ -51,14 +51,14 @@ export const DemoDialogFinish = ({
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={onRestart}
|
onClick={onRestart}
|
||||||
>
|
>
|
||||||
Restart tutorial
|
Restart demo
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
Close
|
Continue
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</StyledActions>
|
</StyledActions>
|
||||||
</DemoDialog>
|
</DemoDialog>
|
||||||
|
@ -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) => (
|
||||||
|
<StyledDemoDialog open={open} onClose={onClose}>
|
||||||
|
<DemoDialog.Header>Want to keep going with Unleash?</DemoDialog.Header>
|
||||||
|
<StyledPlans>
|
||||||
|
<StyledPlan>
|
||||||
|
<Typography variant="h5" fontWeight="bold">
|
||||||
|
Open Source
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
Self-hosted basic feature management solution
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h6" fontWeight="normal">
|
||||||
|
Free
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<GitHub />}
|
||||||
|
href="https://github.com/unleash/unleash"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
View project on GitHub
|
||||||
|
</Button>
|
||||||
|
</StyledPlan>
|
||||||
|
<StyledPlan>
|
||||||
|
<Typography variant="h5" fontWeight="bold">
|
||||||
|
Pro
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
Free your team to collaborate. We'll do the heavy lifting.
|
||||||
|
</Typography>
|
||||||
|
<div>
|
||||||
|
<Typography variant="h6" fontWeight="normal">
|
||||||
|
$80/month
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">includes 5 seats</Typography>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
href="https://www.getunleash.io/plans/pro"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Start 14-day free trial
|
||||||
|
</Button>
|
||||||
|
</StyledPlan>
|
||||||
|
<StyledPlan>
|
||||||
|
<Typography variant="h5" fontWeight="bold">
|
||||||
|
Enterprise
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
Security, compliance, and development controls for scale.
|
||||||
|
</Typography>
|
||||||
|
<div>
|
||||||
|
<Typography variant="h6" fontWeight="normal">
|
||||||
|
Custom
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">unlimited seats</Typography>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="web"
|
||||||
|
href="https://www.getunleash.io/plans/enterprise"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Contact sales
|
||||||
|
</Button>
|
||||||
|
</StyledPlan>
|
||||||
|
</StyledPlans>
|
||||||
|
<StyledCompareLink
|
||||||
|
href="https://www.getunleash.io/plans"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Compare plans <Launch />
|
||||||
|
</StyledCompareLink>
|
||||||
|
</StyledDemoDialog>
|
||||||
|
);
|
@ -60,8 +60,8 @@ export const DemoDialogWelcome = ({
|
|||||||
<DemoDialog.Header>Explore Unleash</DemoDialog.Header>
|
<DemoDialog.Header>Explore Unleash</DemoDialog.Header>
|
||||||
<Typography color="textSecondary" sx={{ mt: 2 }}>
|
<Typography color="textSecondary" sx={{ mt: 2 }}>
|
||||||
You can explore Unleash on your own, however for the best experience
|
You can explore Unleash on your own, however for the best experience
|
||||||
it's recommended you follow our interactive tutorial. To get
|
it's recommended you follow our interactive demo. To get started,
|
||||||
started, you will need to open the demo website below.
|
you will need to open the demo website below.
|
||||||
</Typography>
|
</Typography>
|
||||||
<StyledDemoPane>
|
<StyledDemoPane>
|
||||||
<StyledScanMessage>
|
<StyledScanMessage>
|
||||||
@ -87,7 +87,7 @@ export const DemoDialogWelcome = ({
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={onStart}
|
onClick={onStart}
|
||||||
>
|
>
|
||||||
Start Unleash tutorial
|
Try Unleash demo
|
||||||
</StyledStartButton>
|
</StyledStartButton>
|
||||||
</DemoDialog>
|
</DemoDialog>
|
||||||
);
|
);
|
||||||
|
@ -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 (
|
||||||
|
<div {...tooltipProps}>
|
||||||
|
<StyledDialog
|
||||||
|
open
|
||||||
|
onClose={(_, r) => {
|
||||||
|
if (r !== 'backdropClick') onClose();
|
||||||
|
}}
|
||||||
|
transitionDuration={0}
|
||||||
|
>
|
||||||
|
<StyledCloseButton aria-label="close" onClick={onClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</StyledCloseButton>
|
||||||
|
<StyledTooltipTitle>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(step.title)}
|
||||||
|
show={step.title}
|
||||||
|
elseShow={
|
||||||
|
<Typography fontWeight="bold">
|
||||||
|
{topics[topic].title}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</StyledTooltipTitle>
|
||||||
|
{step.content}
|
||||||
|
<StyledTooltipActions>
|
||||||
|
<div>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={topic > 0 || steps[topic] > 0}
|
||||||
|
show={
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => onBack(step)}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(step.nextButton)}
|
||||||
|
show={
|
||||||
|
<Button
|
||||||
|
onClick={() => onNext(steps[topic])}
|
||||||
|
variant="contained"
|
||||||
|
sx={{ alignSelf: 'flex-end' }}
|
||||||
|
>
|
||||||
|
{topic === topics.length - 1 &&
|
||||||
|
steps[topic] ===
|
||||||
|
topics[topic].steps.length - 1
|
||||||
|
? 'Finish'
|
||||||
|
: 'Next'}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</StyledTooltipActions>
|
||||||
|
</StyledDialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledTooltip {...tooltipProps}>
|
||||||
|
<StyledCloseButton aria-label="close" onClick={onClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</StyledCloseButton>
|
||||||
|
<StyledTooltipTitle>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(step.title)}
|
||||||
|
show={step.title}
|
||||||
|
elseShow={
|
||||||
|
<Typography fontWeight="bold">
|
||||||
|
{topics[topic].title}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</StyledTooltipTitle>
|
||||||
|
{step.content}
|
||||||
|
<StyledTooltipActions>
|
||||||
|
<div>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={topic > 0 || steps[topic] > 0}
|
||||||
|
show={
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => onBack(step)}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(step.nextButton)}
|
||||||
|
show={
|
||||||
|
<Button
|
||||||
|
onClick={() => onNext(steps[topic])}
|
||||||
|
variant="contained"
|
||||||
|
sx={{ alignSelf: 'flex-end' }}
|
||||||
|
>
|
||||||
|
{topic === topics.length - 1 &&
|
||||||
|
steps[topic] === topics[topic].steps.length - 1
|
||||||
|
? 'Finish'
|
||||||
|
: 'Next'}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</StyledTooltipActions>
|
||||||
|
</StyledTooltip>
|
||||||
|
);
|
||||||
|
};
|
@ -3,45 +3,11 @@ import Joyride, {
|
|||||||
CallBackProps,
|
CallBackProps,
|
||||||
TooltipRenderProps,
|
TooltipRenderProps,
|
||||||
} from 'react-joyride';
|
} from 'react-joyride';
|
||||||
import { Button, Typography, styled, useTheme } from '@mui/material';
|
import { useTheme } from '@mui/material';
|
||||||
import { ITutorialTopic, ITutorialTopicStep } from '../demo-topics';
|
import { ITutorialTopic, ITutorialTopicStep } from '../demo-topics';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
import { DemoStepTooltip } from './DemoStepTooltip/DemoStepTooltip';
|
||||||
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),
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface IDemoStepsProps {
|
interface IDemoStepsProps {
|
||||||
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
|
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
@ -82,10 +48,9 @@ export const DemoSteps = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const skip = () => {
|
const close = () => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
setTopicStep(-1);
|
setTopicStep(-1);
|
||||||
setExpanded(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const back = () => {
|
const back = () => {
|
||||||
@ -124,6 +89,10 @@ export const DemoSteps = ({
|
|||||||
) => {
|
) => {
|
||||||
const { action, index, step } = data;
|
const { action, index, step } = data;
|
||||||
|
|
||||||
|
if (action === ACTIONS.CLOSE) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
if (action === ACTIONS.UPDATE) {
|
if (action === ACTIONS.UPDATE) {
|
||||||
const el = document.querySelector(step.target as string);
|
const el = document.querySelector(step.target as string);
|
||||||
if (el) {
|
if (el) {
|
||||||
@ -237,78 +206,27 @@ export const DemoSteps = ({
|
|||||||
border: `2px solid ${theme.palette.primary.main}`,
|
border: `2px solid ${theme.palette.primary.main}`,
|
||||||
outline: `2px solid ${theme.palette.secondary.border}`,
|
outline: `2px solid ${theme.palette.secondary.border}`,
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
|
animation: 'pulse 2s infinite',
|
||||||
},
|
},
|
||||||
overlay: {
|
overlay: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
mixBlendMode: 'unset',
|
mixBlendMode: 'unset',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
tooltipComponent={({
|
tooltipComponent={(
|
||||||
step,
|
props: TooltipRenderProps & {
|
||||||
tooltipProps,
|
step: ITutorialTopicStep;
|
||||||
}: TooltipRenderProps & {
|
}
|
||||||
step: ITutorialTopicStep;
|
) => (
|
||||||
}) => (
|
<DemoStepTooltip
|
||||||
<StyledTooltip {...tooltipProps}>
|
{...props}
|
||||||
<StyledTooltipTitle>
|
topic={topic}
|
||||||
<ConditionallyRender
|
topics={topics}
|
||||||
condition={Boolean(step.title)}
|
steps={steps}
|
||||||
show={step.title}
|
onClose={close}
|
||||||
elseShow={
|
onBack={onBack}
|
||||||
<Typography fontWeight="bold">
|
onNext={next}
|
||||||
{topics[topic].title}
|
/>
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={topics[topic].steps.length > 1}
|
|
||||||
show={
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
color="text.secondary"
|
|
||||||
flexShrink={0}
|
|
||||||
>
|
|
||||||
(step {steps[topic] + 1} of{' '}
|
|
||||||
{topics[topic].steps.length})
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StyledTooltipTitle>
|
|
||||||
{step.content}
|
|
||||||
<StyledTooltipActions>
|
|
||||||
<Button variant="text" onClick={skip}>
|
|
||||||
Skip
|
|
||||||
</Button>
|
|
||||||
<StyledTooltipPrimaryActions>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={topic > 0 || steps[topic] > 0}
|
|
||||||
show={
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => onBack(step)}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(step.nextButton)}
|
|
||||||
show={
|
|
||||||
<Button
|
|
||||||
onClick={() => next(steps[topic])}
|
|
||||||
variant="contained"
|
|
||||||
>
|
|
||||||
{topic === topics.length - 1 &&
|
|
||||||
steps[topic] ===
|
|
||||||
topics[topic].steps.length - 1
|
|
||||||
? 'Finish'
|
|
||||||
: 'Next'}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StyledTooltipPrimaryActions>
|
|
||||||
</StyledTooltipActions>
|
|
||||||
</StyledTooltip>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
import { CheckCircle, CircleOutlined, ExpandMore } from '@mui/icons-material';
|
import { CheckCircle, CircleOutlined, ExpandMore } from '@mui/icons-material';
|
||||||
import { ITutorialTopic } from '../demo-topics';
|
import { ITutorialTopic } from '../demo-topics';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { ReactComponent as StarsIcon } from 'assets/img/stars.svg';
|
||||||
|
|
||||||
const StyledAccordion = styled(Accordion)(({ theme }) => ({
|
const StyledAccordion = styled(Accordion)(({ theme }) => ({
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
@ -47,8 +48,15 @@ const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
|
|||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
borderTopLeftRadius: theme.shape.borderRadiusLarge,
|
borderTopLeftRadius: theme.shape.borderRadiusLarge,
|
||||||
borderTopRightRadius: theme.shape.borderRadiusLarge,
|
borderTopRightRadius: theme.shape.borderRadiusLarge,
|
||||||
|
height: 91,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledStars = styled(StarsIcon)({
|
||||||
|
position: 'absolute',
|
||||||
|
left: 6,
|
||||||
|
top: -24,
|
||||||
|
});
|
||||||
|
|
||||||
const StyledExpandMoreIcon = styled(ExpandMore)(({ theme }) => ({
|
const StyledExpandMoreIcon = styled(ExpandMore)(({ theme }) => ({
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
}));
|
}));
|
||||||
@ -99,7 +107,7 @@ const StyledStep = styled('li', {
|
|||||||
...(selected && {
|
...(selected && {
|
||||||
backgroundColor: theme.palette.secondary.light,
|
backgroundColor: theme.palette.secondary.light,
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
border: `1px solid ${theme.palette.primary.main}`,
|
outline: `1px solid ${theme.palette.primary.main}`,
|
||||||
}),
|
}),
|
||||||
...(completed && {
|
...(completed && {
|
||||||
backgroundColor: theme.palette.background.elevation1,
|
backgroundColor: theme.palette.background.elevation1,
|
||||||
@ -160,12 +168,13 @@ export const DemoTopics = ({
|
|||||||
onChange={() => setExpanded(expanded => !expanded)}
|
onChange={() => setExpanded(expanded => !expanded)}
|
||||||
>
|
>
|
||||||
<StyledAccordionSummary>
|
<StyledAccordionSummary>
|
||||||
|
<StyledStars />
|
||||||
<StyledTitle>
|
<StyledTitle>
|
||||||
<Typography fontWeight="bold">Unleash tutorial</Typography>
|
<Typography fontWeight="bold">Unleash demo</Typography>
|
||||||
<StyledExpandMoreIcon className="expand-icon" />
|
<StyledExpandMoreIcon className="expand-icon" />
|
||||||
</StyledTitle>
|
</StyledTitle>
|
||||||
<StyledSubtitle>
|
<StyledSubtitle>
|
||||||
Complete all steps to finish tutorial
|
Complete all steps to finish demo
|
||||||
</StyledSubtitle>
|
</StyledSubtitle>
|
||||||
<StyledProgress>
|
<StyledProgress>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
@ -204,7 +213,7 @@ export const DemoTopics = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<StyledButton variant="outlined" onClick={onWelcome}>
|
<StyledButton variant="outlined" onClick={onWelcome}>
|
||||||
View demo link again
|
View demo page
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</StyledAccordion>
|
</StyledAccordion>
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { IFeatureToggle } from 'interfaces/featureToggle';
|
import { IFeatureToggle } from 'interfaces/featureToggle';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
|
|
||||||
|
const PROJECT = 'demo-app';
|
||||||
|
const ENVIRONMENT = 'dev';
|
||||||
|
|
||||||
export const gradualRollout = async () => {
|
export const gradualRollout = async () => {
|
||||||
const projectId = 'default';
|
|
||||||
const featureId = 'demoApp.step3';
|
const featureId = 'demoApp.step3';
|
||||||
const environmentId = 'default';
|
|
||||||
|
|
||||||
const { environments }: IFeatureToggle = await fetch(
|
const { environments }: IFeatureToggle = await fetch(
|
||||||
formatApiPath(
|
formatApiPath(
|
||||||
`api/admin/projects/${projectId}/features/${featureId}?variantEnvironments=true`
|
`api/admin/projects/${PROJECT}/features/${featureId}?variantEnvironments=true`
|
||||||
)
|
)
|
||||||
).then(res => res.json());
|
).then(res => res.json());
|
||||||
|
|
||||||
const strategies =
|
const strategies =
|
||||||
environments.find(({ name }) => name === environmentId)?.strategies ||
|
environments.find(({ name }) => name === ENVIRONMENT)?.strategies || [];
|
||||||
[];
|
|
||||||
|
|
||||||
if (!strategies.find(({ name }) => name === 'flexibleRollout')) {
|
if (!strategies.find(({ name }) => name === 'flexibleRollout')) {
|
||||||
await fetch(
|
await fetch(
|
||||||
formatApiPath(
|
formatApiPath(
|
||||||
`api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies`
|
`api/admin/projects/${PROJECT}/features/${featureId}/environments/${ENVIRONMENT}/strategies`
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -41,20 +41,18 @@ export const gradualRollout = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const variants = async () => {
|
export const variants = async () => {
|
||||||
const projectId = 'default';
|
|
||||||
const featureId = 'demoApp.step4';
|
const featureId = 'demoApp.step4';
|
||||||
const environmentId = 'default';
|
|
||||||
|
|
||||||
const { variants }: IFeatureToggle = await fetch(
|
const { variants }: IFeatureToggle = await fetch(
|
||||||
formatApiPath(
|
formatApiPath(
|
||||||
`api/admin/projects/${projectId}/features/${featureId}?variantEnvironments=true`
|
`api/admin/projects/${PROJECT}/features/${featureId}?variantEnvironments=true`
|
||||||
)
|
)
|
||||||
).then(res => res.json());
|
).then(res => res.json());
|
||||||
|
|
||||||
if (!variants.length) {
|
if (!variants.length) {
|
||||||
await fetch(
|
await fetch(
|
||||||
formatApiPath(
|
formatApiPath(
|
||||||
`api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/variants`
|
`api/admin/projects/${PROJECT}/features/${featureId}/environments/${ENVIRONMENT}/variants`
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
@ -3,6 +3,7 @@ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
|||||||
import { Badge } from 'component/common/Badge/Badge';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
import { Step } from 'react-joyride';
|
import { Step } from 'react-joyride';
|
||||||
import { gradualRollout, variants } from './demo-setup';
|
import { gradualRollout, variants } from './demo-setup';
|
||||||
|
import { basePath } from 'utils/formatPath';
|
||||||
|
|
||||||
export interface ITutorialTopicStep extends Step {
|
export interface ITutorialTopicStep extends Step {
|
||||||
href?: string;
|
href?: string;
|
||||||
@ -24,12 +25,15 @@ const Description = (props: TypographyProps) => (
|
|||||||
<Typography variant="body2" color="text.secondary" {...props} />
|
<Typography variant="body2" color="text.secondary" {...props} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const PROJECT = 'demo-app';
|
||||||
|
const ENVIRONMENT = 'dev';
|
||||||
|
|
||||||
export const TOPICS: ITutorialTopic[] = [
|
export const TOPICS: ITutorialTopic[] = [
|
||||||
{
|
{
|
||||||
title: 'Enable/disable a feature toggle',
|
title: 'Enable/disable a feature toggle',
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'body',
|
target: 'body',
|
||||||
placement: 'center',
|
placement: 'center',
|
||||||
content: (
|
content: (
|
||||||
@ -58,8 +62,8 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
nextButton: true,
|
nextButton: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'div[data-testid="TOGGLE-demoApp.step1-default"]',
|
target: `div[data-testid="TOGGLE-demoApp.step1-${ENVIRONMENT}"]`,
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<Description>
|
<Description>
|
||||||
@ -82,7 +86,7 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
title: 'Enable for a specific user',
|
title: 'Enable for a specific user',
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'body',
|
target: 'body',
|
||||||
placement: 'center',
|
placement: 'center',
|
||||||
content: (
|
content: (
|
||||||
@ -106,19 +110,19 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
nextButton: true,
|
nextButton: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'a[href="/projects/default/features/demoApp.step2"]',
|
target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step2"]`,
|
||||||
content: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
First, let's open the feature toggle configuration for{' '}
|
First, let's open the feature toggle configuration for{' '}
|
||||||
<Badge as="span">demoApp.step2</Badge>.
|
<Badge as="span">demoApp.step2</Badge>
|
||||||
</Description>
|
</Description>
|
||||||
),
|
),
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default/features/demoApp.step2',
|
href: `/projects/${PROJECT}/features/demoApp.step2`,
|
||||||
target: 'div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_default"] button',
|
target: `div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_${ENVIRONMENT}"] button`,
|
||||||
content: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
Add a new strategy to this environment by clicking this
|
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: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
Select the <Badge as="span">Standard</Badge> strategy
|
Select the <Badge as="span">Standard</Badge> strategy
|
||||||
@ -197,7 +201,7 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<Description>
|
<Description>
|
||||||
Enter your <Badge as="span">userId</Badge>.
|
Enter your <Badge as="span">userId</Badge>
|
||||||
</Description>
|
</Description>
|
||||||
<Badge
|
<Badge
|
||||||
sx={{ marginTop: 2 }}
|
sx={{ marginTop: 2 }}
|
||||||
@ -207,7 +211,6 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
</Badge>
|
</Badge>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
backCloseModal: true,
|
|
||||||
nextButton: true,
|
nextButton: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -217,7 +220,6 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
{
|
{
|
||||||
target: 'button[data-testid="CONSTRAINT_SAVE_BUTTON"]',
|
target: 'button[data-testid="CONSTRAINT_SAVE_BUTTON"]',
|
||||||
content: <Description>Save the constraint.</Description>,
|
content: <Description>Save the constraint.</Description>,
|
||||||
backCloseModal: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: 'button[data-testid="STRATEGY_FORM_SUBMIT_ID"]',
|
target: 'button[data-testid="STRATEGY_FORM_SUBMIT_ID"]',
|
||||||
@ -259,7 +261,7 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
setup: gradualRollout,
|
setup: gradualRollout,
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'body',
|
target: 'body',
|
||||||
placement: 'center',
|
placement: 'center',
|
||||||
content: (
|
content: (
|
||||||
@ -289,19 +291,19 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
nextButton: true,
|
nextButton: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'a[href="/projects/default/features/demoApp.step3"]',
|
target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step3"]`,
|
||||||
content: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
First, let's open the feature toggle configuration for{' '}
|
First, let's open the feature toggle configuration for{' '}
|
||||||
<Badge as="span">demoApp.step3</Badge>.
|
<Badge as="span">demoApp.step3</Badge>
|
||||||
</Description>
|
</Description>
|
||||||
),
|
),
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default/features/demoApp.step3',
|
href: `/projects/${PROJECT}/features/demoApp.step3`,
|
||||||
target: 'div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_default"] .MuiAccordionSummary-expandIconWrapper',
|
target: `div[data-testid="FEATURE_ENVIRONMENT_ACCORDION_${ENVIRONMENT}"] .MuiAccordionSummary-expandIconWrapper`,
|
||||||
content: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
Expand the environment card to see all the defined
|
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: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
Edit the existing gradual rollout strategy.
|
Edit the existing gradual rollout strategy.
|
||||||
@ -365,7 +367,7 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
setup: variants,
|
setup: variants,
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'body',
|
target: 'body',
|
||||||
placement: 'center',
|
placement: 'center',
|
||||||
content: (
|
content: (
|
||||||
@ -390,18 +392,18 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
nextButton: true,
|
nextButton: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default',
|
href: `/projects/${PROJECT}`,
|
||||||
target: 'a[href="/projects/default/features/demoApp.step4"]',
|
target: `a[href="${basePath}/projects/${PROJECT}/features/demoApp.step4"]`,
|
||||||
content: (
|
content: (
|
||||||
<Description>
|
<Description>
|
||||||
First, let's open the feature toggle configuration for{' '}
|
First, let's open the feature toggle configuration for{' '}
|
||||||
<Badge as="span">demoApp.step4</Badge>.
|
<Badge as="span">demoApp.step4</Badge>
|
||||||
</Description>
|
</Description>
|
||||||
),
|
),
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/projects/default/features/demoApp.step4',
|
href: `/projects/${PROJECT}/features/demoApp.step4`,
|
||||||
target: 'button[data-testid="TAB-Variants"]',
|
target: 'button[data-testid="TAB-Variants"]',
|
||||||
content: <Description>Select the variants tab.</Description>,
|
content: <Description>Select the variants tab.</Description>,
|
||||||
},
|
},
|
||||||
@ -432,7 +434,7 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
.
|
.
|
||||||
</Description>
|
</Description>
|
||||||
<Description>
|
<Description>
|
||||||
Example: <Badge as="span">aqua</Badge>.
|
Example: <Badge as="span">aqua</Badge>
|
||||||
</Description>
|
</Description>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
@ -485,7 +487,7 @@ export const TOPICS: ITutorialTopic[] = [
|
|||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<Description>
|
<Description>
|
||||||
Enter your <Badge as="span">userId</Badge>.
|
Enter your <Badge as="span">userId</Badge>
|
||||||
</Description>
|
</Description>
|
||||||
<Badge
|
<Badge
|
||||||
sx={{ marginTop: 2 }}
|
sx={{ marginTop: 2 }}
|
||||||
|
@ -14,6 +14,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||||
import { DraftBanner } from './DraftBanner/DraftBanner';
|
import { DraftBanner } from './DraftBanner/DraftBanner';
|
||||||
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||||
|
import { Demo } from 'component/demo/Demo';
|
||||||
|
|
||||||
interface IMainLayoutProps {
|
interface IMainLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -84,41 +85,50 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SkipNavLink />
|
<SkipNavLink />
|
||||||
<Header />
|
<Demo>
|
||||||
<SkipNavTarget />
|
<>
|
||||||
<MainLayoutContainer>
|
<Header />
|
||||||
<MainLayoutContentWrapper>
|
<SkipNavTarget />
|
||||||
<ConditionallyRender
|
<MainLayoutContainer>
|
||||||
condition={Boolean(
|
<MainLayoutContentWrapper>
|
||||||
projectId && isChangeRequestConfiguredInAnyEnv()
|
<ConditionallyRender
|
||||||
)}
|
condition={Boolean(
|
||||||
show={<DraftBanner project={projectId || ''} />}
|
projectId &&
|
||||||
/>
|
isChangeRequestConfiguredInAnyEnv()
|
||||||
<MainLayoutContent item xs={12} sm={12} my={2}>
|
)}
|
||||||
<MainLayoutContentContainer ref={ref}>
|
show={
|
||||||
<BreadcrumbNav />
|
<DraftBanner
|
||||||
<Proclamation toast={uiConfig.toast} />
|
project={projectId || ''}
|
||||||
{children}
|
/>
|
||||||
</MainLayoutContentContainer>
|
}
|
||||||
</MainLayoutContent>
|
|
||||||
<ThemeMode
|
|
||||||
darkmode={
|
|
||||||
<StyledImg
|
|
||||||
style={{ opacity: 0.06 }}
|
|
||||||
src={formatAssetPath(textureImage)}
|
|
||||||
alt=""
|
|
||||||
/>
|
/>
|
||||||
}
|
<MainLayoutContent item xs={12} sm={12} my={2}>
|
||||||
lightmode={
|
<MainLayoutContentContainer ref={ref}>
|
||||||
<StyledImg
|
<BreadcrumbNav />
|
||||||
src={formatAssetPath(textureImage)}
|
<Proclamation toast={uiConfig.toast} />
|
||||||
alt=""
|
{children}
|
||||||
|
</MainLayoutContentContainer>
|
||||||
|
</MainLayoutContent>
|
||||||
|
<ThemeMode
|
||||||
|
darkmode={
|
||||||
|
<StyledImg
|
||||||
|
style={{ opacity: 0.06 }}
|
||||||
|
src={formatAssetPath(textureImage)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
lightmode={
|
||||||
|
<StyledImg
|
||||||
|
src={formatAssetPath(textureImage)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
}
|
</MainLayoutContentWrapper>
|
||||||
/>
|
<Footer />
|
||||||
</MainLayoutContentWrapper>
|
</MainLayoutContainer>
|
||||||
<Footer />
|
</>
|
||||||
</MainLayoutContainer>
|
</Demo>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,10 @@ const theme = {
|
|||||||
border: '#8A3E45',
|
border: '#8A3E45',
|
||||||
contrastText: '#EEEEFC', // Color used for content when error.main is used as a background
|
contrastText: '#EEEEFC', // Color used for content when error.main is used as a background
|
||||||
},
|
},
|
||||||
|
web: {
|
||||||
|
main: '#1A4049', // used on sales-related elements
|
||||||
|
contrastText: '#EEEEFC', // Color used for inner text
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for grey badges, hover elements, and grey light elements
|
* Used for grey badges, hover elements, and grey light elements
|
||||||
|
@ -118,6 +118,10 @@ const theme = {
|
|||||||
border: colors.red[300],
|
border: colors.red[300],
|
||||||
contrastText: colors.red[800], // Color used for text inside alert
|
contrastText: colors.red[800], // Color used for text inside alert
|
||||||
},
|
},
|
||||||
|
web: {
|
||||||
|
main: '#1A4049', // used on sales-related elements
|
||||||
|
contrastText: colors.grey[50], // Color used for inner text
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for grey badges, hover elements, and grey light elements
|
* Used for grey badges, hover elements, and grey light elements
|
||||||
|
@ -38,6 +38,11 @@ declare module '@mui/material/styles' {
|
|||||||
*/
|
*/
|
||||||
neutral: PaletteColorOptions;
|
neutral: PaletteColorOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sales-related palette color
|
||||||
|
*/
|
||||||
|
web: PaletteColorOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table colors
|
* Table colors
|
||||||
*/
|
*/
|
||||||
@ -148,5 +153,10 @@ declare module '@mui/material/styles/zIndex' {
|
|||||||
sticky: number;
|
sticky: number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
declare module '@mui/material' {
|
||||||
|
interface ButtonPropsColorOverrides {
|
||||||
|
web: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
Loading…
Reference in New Issue
Block a user