Skip to content

FEAT: [UI] implement step-by-step user onboarding. #3530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions xinference/web/ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions xinference/web/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-cookie": "^6.1.1",
"react-dom": "^18.2.0",
"react-i18next": "^15.4.0",
"react-joyride": "^2.9.3",
"react-pro-sidebar": "^1.1.0-alpha.1",
"react-router-dom": "^6.14.1",
"react-scripts": "5.0.1",
Expand Down
114 changes: 113 additions & 1 deletion xinference/web/ui/src/components/MenuSide.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from '@mui/material'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Joyride, { STATUS } from 'react-joyride'
import { useLocation, useNavigate } from 'react-router-dom'

import icon from '../media/icon.webp'
Expand All @@ -36,6 +37,10 @@ const MenuSide = () => {
const [drawerWidth, setDrawerWidth] = useState(
`${Math.min(Math.max(window.innerWidth * 0.2, 287), 320)}px`
)
const [run, setRun] = useState(false)
const [stepIndex, setStepIndex] = useState(0)
const [spotlightClicks, setSpotlightClicks] = useState(false)

const { i18n, t } = useTranslation()

const navItems = [
Expand Down Expand Up @@ -71,11 +76,51 @@ const MenuSide = () => {
},
]

const clickableSteps = [4, 5]

const steps = [
{
target: '.step-0',
content: t('components.step-0'),
disableBeacon: true,
},
{
target: '.step-1',
content: t('components.step-1'),
disableBeacon: true,
},
{
target: '.step-2',
content: t('components.step-2'),
disableBeacon: true,
},
{
target: '.step-3',
content: t('components.step-3'),
disableBeacon: true,
},
{
target: '.step-4',
content: t('components.step-4'),
disableBeacon: true,
},
{
target: '.step-5',
content: t('components.step-5'),
disableBeacon: true,
},
]

useEffect(() => {
setActive(pathname.substring(1))
}, [pathname])

useEffect(() => {
if (!localStorage.getItem('hasSeenGuide')) {
setRun(true)
localStorage.setItem('hasSeenGuide', 'true')
}

const screenWidth = window.innerWidth
const maxDrawerWidth = Math.min(Math.max(screenWidth * 0.2, 287), 320)
setDrawerWidth(`${maxDrawerWidth}px`)
Expand All @@ -96,6 +141,44 @@ const MenuSide = () => {
}
}, [])

const handleJoyrideCallback = (data) => {
const { index, type, status, action } = data

if (type === 'step:before') {
setStepIndex(index)
setSpotlightClicks(clickableSteps.includes(index))
}

if (status === STATUS.FINISHED || status === STATUS.SKIPPED) {
setRun(false)
setStepIndex(0)
}

if (type === 'step:after') {
if (action !== 'prev') {
setStepIndex(index + 1)
if (index === 3) {
const target = document.querySelector('.step-3')
if (target) {
target.click()

setTimeout(() => {
window.dispatchEvent(new Event('resize'))
}, 500)
}
}
} else {
setStepIndex(index - 1)
if (index === 4) {
const target = document.querySelector('.step-goBack')
if (target) {
target.click()
}
}
}
}
}

return (
<Drawer
variant="permanent"
Expand All @@ -110,6 +193,35 @@ const MenuSide = () => {
}}
style={{ zIndex: 1 }}
>
<Joyride
steps={steps}
run={run}
stepIndex={stepIndex}
spotlightClicks={spotlightClicks}
callback={handleJoyrideCallback}
continuous
showStepsProgress
showSkipButton
showProgress
scrollToFirstStep
disableScrolling
hideCloseButton
disableScrollParentFix
locale={{
back: t('components.back'),
close: t('components.close'),
next: t('components.next'),
last: t('components.last'),
skip: t('components.skip'),
nextLabelWithProgress: t('components.next'),
}}
styles={{
options: {
zIndex: 1300,
primaryColor: '#1976D2',
},
}}
/>
{/* Title */}
<Box
display="flex"
Expand Down Expand Up @@ -145,7 +257,7 @@ const MenuSide = () => {
<Box sx={{ flexGrow: 1 }}>
<Box width="100%">
<Box m="1.5rem 2rem 2rem 3rem"></Box>
<List>
<List className="step-0">
{navItems.map(({ text, label, icon }) => {
if (!icon) {
return (
Expand Down
13 changes: 12 additions & 1 deletion xinference/web/ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@

"components": {
"copySuccess": "Copied to clipboard!",
"suggestsCommonParameters": "Suggests common parameters; others allowed."
"suggestsCommonParameters": "Suggests common parameters; others allowed.",
"back": "Back",
"close": "Close",
"next": "Next",
"last": "Finish",
"skip": "Skip Tour",
"step-0": "Menu bar, containing all main function entries.",
"step-1": "Classify models by category for quick access.",
"step-2": "Use the filter function to quickly locate the desired model.",
"step-3": "Click to open the model parameter configuration window.",
"step-4": "Configure relevant model parameters in this window.",
"step-5": "After filling in the required parameters, click the button to start the model."
}
}
Loading
Loading