mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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 { createLocalStorage } from 'utils/createLocalStorage'; | ||||
| import { TOPICS } from './demo-topics'; | ||||
| import { DemoDialogWelcome } from './DemoDialog/DemoDialogWelcome/DemoDialogWelcome'; | ||||
| 
 | ||||
| const defaultProgress = { | ||||
|     welcomeOpen: true, | ||||
|     expanded: true, | ||||
|     run: false, | ||||
|     active: false, | ||||
|     topic: 0, | ||||
|     steps: [0], | ||||
| }; | ||||
| @ -18,34 +20,47 @@ const { value: storedProgress, setValue: setStoredProgress } = | ||||
| 
 | ||||
| export const Demo = () => { | ||||
|     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 [run, setRun] = useState(false); | ||||
|     const [topic, setTopic] = useState(storedProgress.topic ?? 0); | ||||
|     const [steps, setSteps] = useState(storedProgress.steps ?? [0]); | ||||
| 
 | ||||
|     useEffect(() => { | ||||
|         setTimeout(() => { | ||||
|             setLoaded(true); | ||||
|             if (storedProgress.run) { | ||||
|                 setRun(true); | ||||
|             } | ||||
|         }, 1000); | ||||
|         if (storedProgress.active) { | ||||
|             setTimeout(() => { | ||||
|                 setActive(true); | ||||
|             }, 1000); | ||||
|         } | ||||
|     }, []); | ||||
| 
 | ||||
|     useEffect(() => { | ||||
|         setStoredProgress({ | ||||
|             welcomeOpen, | ||||
|             expanded, | ||||
|             run, | ||||
|             active, | ||||
|             topic, | ||||
|             steps, | ||||
|         }); | ||||
|     }, [expanded, run, topic, steps]); | ||||
|     }, [welcomeOpen, expanded, active, topic, steps]); | ||||
| 
 | ||||
|     if (!uiConfig.flags.demo) return null; | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <DemoDialogWelcome | ||||
|                 open={welcomeOpen} | ||||
|                 onClose={() => { | ||||
|                     setWelcomeOpen(false); | ||||
|                     setExpanded(false); | ||||
|                 }} | ||||
|                 onStart={() => { | ||||
|                     setWelcomeOpen(false); | ||||
|                     setActive(true); | ||||
|                 }} | ||||
|             /> | ||||
|             <DemoTopics | ||||
|                 expanded={expanded} | ||||
|                 setExpanded={setExpanded} | ||||
| @ -60,13 +75,12 @@ export const Demo = () => { | ||||
|                     }); | ||||
|                 }} | ||||
|                 topics={TOPICS} | ||||
|                 onShowWelcome={() => setWelcomeOpen(true)} | ||||
|             /> | ||||
|             <ConditionallyRender | ||||
|                 condition={loaded} | ||||
|                 condition={active} | ||||
|                 show={ | ||||
|                     <DemoSteps | ||||
|                         run={run} | ||||
|                         setRun={setRun} | ||||
|                         setExpanded={setExpanded} | ||||
|                         steps={steps} | ||||
|                         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 { | ||||
|     run: boolean; | ||||
|     setRun: React.Dispatch<React.SetStateAction<boolean>>; | ||||
|     setExpanded: React.Dispatch<React.SetStateAction<boolean>>; | ||||
|     steps: number[]; | ||||
|     setSteps: React.Dispatch<React.SetStateAction<number[]>>; | ||||
| @ -55,8 +53,6 @@ interface IDemoStepsProps { | ||||
| } | ||||
| 
 | ||||
| export const DemoSteps = ({ | ||||
|     run, | ||||
|     setRun, | ||||
|     setExpanded, | ||||
|     steps, | ||||
|     setSteps, | ||||
| @ -67,6 +63,7 @@ export const DemoSteps = ({ | ||||
|     const theme = useTheme(); | ||||
|     const navigate = useNavigate(); | ||||
|     const location = useLocation(); | ||||
|     const [run, setRun] = useState(false); | ||||
|     const [flow, setFlow] = useState<'next' | 'back'>('next'); | ||||
| 
 | ||||
|     const abortController = new AbortController(); | ||||
|  | ||||
| @ -138,6 +138,7 @@ interface IDemoTopicsProps { | ||||
|     currentTopic: number; | ||||
|     setCurrentTopic: (topic: number) => void; | ||||
|     topics: ITutorialTopic[]; | ||||
|     onShowWelcome: () => void; | ||||
| } | ||||
| 
 | ||||
| export const DemoTopics = ({ | ||||
| @ -147,6 +148,7 @@ export const DemoTopics = ({ | ||||
|     currentTopic, | ||||
|     setCurrentTopic, | ||||
|     topics, | ||||
|     onShowWelcome, | ||||
| }: IDemoTopicsProps) => { | ||||
|     const completedSteps = steps.reduce((acc, step) => acc + (step || 0), 0); | ||||
|     const totalSteps = topics.flatMap(({ steps }) => steps).length; | ||||
| @ -201,7 +203,7 @@ export const DemoTopics = ({ | ||||
|                         </StyledStep> | ||||
|                     ); | ||||
|                 })} | ||||
|                 <StyledButton variant="outlined"> | ||||
|                 <StyledButton variant="outlined" onClick={onShowWelcome}> | ||||
|                     View demo link again | ||||
|                 </StyledButton> | ||||
|             </AccordionDetails> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user