mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
feat: add demo guide welcome dialog (#3574)
https://linear.app/unleash/issue/2-923/add-initial-getting-started-dialog Adds the welcome dialog with the static QR code pointing to our demo website. Also changes how the run logic works a bit, so that we have better control over it (only start demo steps once we click the start button, etc). Relates to [roadmap](https://github.com/orgs/Unleash/projects/10) item: #3537 
This commit is contained in:
parent
23487f143d
commit
2cf6e689ee
BIN
frontend/src/assets/img/demo_qr.png
Normal file
BIN
frontend/src/assets/img/demo_qr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
@ -5,10 +5,12 @@ import { DemoSteps } from './DemoSteps/DemoSteps';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { createLocalStorage } from 'utils/createLocalStorage';
|
import { createLocalStorage } from 'utils/createLocalStorage';
|
||||||
import { TOPICS } from './demo-topics';
|
import { TOPICS } from './demo-topics';
|
||||||
|
import { DemoDialogWelcome } from './DemoDialog/DemoDialogWelcome/DemoDialogWelcome';
|
||||||
|
|
||||||
const defaultProgress = {
|
const defaultProgress = {
|
||||||
|
welcomeOpen: true,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
run: false,
|
active: false,
|
||||||
topic: 0,
|
topic: 0,
|
||||||
steps: [0],
|
steps: [0],
|
||||||
};
|
};
|
||||||
@ -18,34 +20,47 @@ const { value: storedProgress, setValue: setStoredProgress } =
|
|||||||
|
|
||||||
export const Demo = () => {
|
export const Demo = () => {
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [welcomeOpen, setWelcomeOpen] = useState(
|
||||||
|
storedProgress.welcomeOpen ?? true
|
||||||
|
);
|
||||||
|
const [active, setActive] = useState(false);
|
||||||
const [expanded, setExpanded] = useState(storedProgress.expanded ?? true);
|
const [expanded, setExpanded] = useState(storedProgress.expanded ?? true);
|
||||||
const [run, setRun] = useState(false);
|
|
||||||
const [topic, setTopic] = useState(storedProgress.topic ?? 0);
|
const [topic, setTopic] = useState(storedProgress.topic ?? 0);
|
||||||
const [steps, setSteps] = useState(storedProgress.steps ?? [0]);
|
const [steps, setSteps] = useState(storedProgress.steps ?? [0]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
if (storedProgress.active) {
|
||||||
setLoaded(true);
|
setTimeout(() => {
|
||||||
if (storedProgress.run) {
|
setActive(true);
|
||||||
setRun(true);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}, 1000);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setStoredProgress({
|
setStoredProgress({
|
||||||
|
welcomeOpen,
|
||||||
expanded,
|
expanded,
|
||||||
run,
|
active,
|
||||||
topic,
|
topic,
|
||||||
steps,
|
steps,
|
||||||
});
|
});
|
||||||
}, [expanded, run, topic, steps]);
|
}, [welcomeOpen, expanded, active, topic, steps]);
|
||||||
|
|
||||||
if (!uiConfig.flags.demo) return null;
|
if (!uiConfig.flags.demo) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<DemoDialogWelcome
|
||||||
|
open={welcomeOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setWelcomeOpen(false);
|
||||||
|
setExpanded(false);
|
||||||
|
}}
|
||||||
|
onStart={() => {
|
||||||
|
setWelcomeOpen(false);
|
||||||
|
setActive(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<DemoTopics
|
<DemoTopics
|
||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
setExpanded={setExpanded}
|
setExpanded={setExpanded}
|
||||||
@ -60,13 +75,12 @@ export const Demo = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
topics={TOPICS}
|
topics={TOPICS}
|
||||||
|
onShowWelcome={() => setWelcomeOpen(true)}
|
||||||
/>
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={loaded}
|
condition={active}
|
||||||
show={
|
show={
|
||||||
<DemoSteps
|
<DemoSteps
|
||||||
run={run}
|
|
||||||
setRun={setRun}
|
|
||||||
setExpanded={setExpanded}
|
setExpanded={setExpanded}
|
||||||
steps={steps}
|
steps={steps}
|
||||||
setSteps={setSteps}
|
setSteps={setSteps}
|
||||||
|
40
frontend/src/component/demo/DemoDialog/DemoDialog.tsx
Normal file
40
frontend/src/component/demo/DemoDialog/DemoDialog.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Dialog, IconButton, Typography, styled } from '@mui/material';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
|
||||||
|
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
borderRadius: theme.shape.borderRadiusExtraLarge,
|
||||||
|
maxWidth: theme.spacing(90),
|
||||||
|
padding: theme.spacing(7.5),
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledCloseButton = styled(IconButton)(({ theme }) => ({
|
||||||
|
position: 'absolute',
|
||||||
|
right: theme.spacing(2),
|
||||||
|
top: theme.spacing(2),
|
||||||
|
color: theme.palette.neutral.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledHeader = styled(Typography)(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.largeHeader,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IDemoDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DemoDialog = ({ open, onClose, children }: IDemoDialogProps) => (
|
||||||
|
<StyledDialog open={open} onClose={onClose}>
|
||||||
|
<StyledCloseButton aria-label="close" onClick={onClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</StyledCloseButton>
|
||||||
|
{children}
|
||||||
|
</StyledDialog>
|
||||||
|
);
|
||||||
|
|
||||||
|
DemoDialog.Header = StyledHeader;
|
@ -0,0 +1,93 @@
|
|||||||
|
import { Button, Divider, Typography, styled } from '@mui/material';
|
||||||
|
import qrImage from 'assets/img/demo_qr.png';
|
||||||
|
import { formatAssetPath } from 'utils/formatPath';
|
||||||
|
import { Launch } from '@mui/icons-material';
|
||||||
|
import { DemoDialog } from '../DemoDialog';
|
||||||
|
|
||||||
|
const StyledDemoPane = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: theme.palette.neutral.light,
|
||||||
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
|
padding: theme.spacing(4),
|
||||||
|
margin: theme.spacing(4, 0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledScanMessage = styled(Typography)(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.mainHeader,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledQRCode = styled('img')(({ theme }) => ({
|
||||||
|
width: theme.spacing(20),
|
||||||
|
height: theme.spacing(20),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDivider = styled(Divider)(({ theme }) => ({
|
||||||
|
margin: theme.spacing(4, 0),
|
||||||
|
padding: theme.spacing(0, 4),
|
||||||
|
width: '100%',
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledLink = styled('a')(({ theme }) => ({
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(0.5),
|
||||||
|
'& > svg': {
|
||||||
|
fontSize: theme.fontSizes.bodySize,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledStartButton = styled(Button)(({ theme }) => ({
|
||||||
|
height: theme.spacing(7),
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IDemoDialogWelcomeProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onStart: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DemoDialogWelcome = ({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
onStart,
|
||||||
|
}: IDemoDialogWelcomeProps) => (
|
||||||
|
<DemoDialog open={open} onClose={onClose}>
|
||||||
|
<DemoDialog.Header>Explore Unleash</DemoDialog.Header>
|
||||||
|
<Typography color="textSecondary" sx={{ mt: 2 }}>
|
||||||
|
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.
|
||||||
|
</Typography>
|
||||||
|
<StyledDemoPane>
|
||||||
|
<StyledScanMessage>
|
||||||
|
Scan the QR code with your phone
|
||||||
|
</StyledScanMessage>
|
||||||
|
<StyledQRCode src={formatAssetPath(qrImage)} alt="Demo QR Code" />
|
||||||
|
<StyledDivider>OR</StyledDivider>
|
||||||
|
<Typography>
|
||||||
|
Open demo website in another tab:{' '}
|
||||||
|
<StyledLink
|
||||||
|
href="https://demo.unleash-hosted.com/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
demo.unleash-hosted.com <Launch />
|
||||||
|
</StyledLink>
|
||||||
|
</Typography>
|
||||||
|
<Typography color="textSecondary">
|
||||||
|
(we recommend you keep the pages open side by side)
|
||||||
|
</Typography>
|
||||||
|
</StyledDemoPane>
|
||||||
|
<StyledStartButton
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={onStart}
|
||||||
|
>
|
||||||
|
Start Unleash tutorial
|
||||||
|
</StyledStartButton>
|
||||||
|
</DemoDialog>
|
||||||
|
);
|
@ -44,8 +44,6 @@ const StyledTooltipPrimaryActions = styled('div')(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
interface IDemoStepsProps {
|
interface IDemoStepsProps {
|
||||||
run: boolean;
|
|
||||||
setRun: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
|
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
steps: number[];
|
steps: number[];
|
||||||
setSteps: React.Dispatch<React.SetStateAction<number[]>>;
|
setSteps: React.Dispatch<React.SetStateAction<number[]>>;
|
||||||
@ -55,8 +53,6 @@ interface IDemoStepsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DemoSteps = ({
|
export const DemoSteps = ({
|
||||||
run,
|
|
||||||
setRun,
|
|
||||||
setExpanded,
|
setExpanded,
|
||||||
steps,
|
steps,
|
||||||
setSteps,
|
setSteps,
|
||||||
@ -67,6 +63,7 @@ export const DemoSteps = ({
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const [run, setRun] = useState(false);
|
||||||
const [flow, setFlow] = useState<'next' | 'back'>('next');
|
const [flow, setFlow] = useState<'next' | 'back'>('next');
|
||||||
|
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
@ -138,6 +138,7 @@ interface IDemoTopicsProps {
|
|||||||
currentTopic: number;
|
currentTopic: number;
|
||||||
setCurrentTopic: (topic: number) => void;
|
setCurrentTopic: (topic: number) => void;
|
||||||
topics: ITutorialTopic[];
|
topics: ITutorialTopic[];
|
||||||
|
onShowWelcome: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DemoTopics = ({
|
export const DemoTopics = ({
|
||||||
@ -147,6 +148,7 @@ export const DemoTopics = ({
|
|||||||
currentTopic,
|
currentTopic,
|
||||||
setCurrentTopic,
|
setCurrentTopic,
|
||||||
topics,
|
topics,
|
||||||
|
onShowWelcome,
|
||||||
}: IDemoTopicsProps) => {
|
}: IDemoTopicsProps) => {
|
||||||
const completedSteps = steps.reduce((acc, step) => acc + (step || 0), 0);
|
const completedSteps = steps.reduce((acc, step) => acc + (step || 0), 0);
|
||||||
const totalSteps = topics.flatMap(({ steps }) => steps).length;
|
const totalSteps = topics.flatMap(({ steps }) => steps).length;
|
||||||
@ -201,7 +203,7 @@ export const DemoTopics = ({
|
|||||||
</StyledStep>
|
</StyledStep>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<StyledButton variant="outlined">
|
<StyledButton variant="outlined" onClick={onShowWelcome}>
|
||||||
View demo link again
|
View demo link again
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
|
Loading…
Reference in New Issue
Block a user