diff --git a/packages/constants/onboarding-data.js b/packages/constants/onboarding-data.js new file mode 100644 index 000000000..5951bc33d --- /dev/null +++ b/packages/constants/onboarding-data.js @@ -0,0 +1,46 @@ +export const UNCONFIRMED_REQUEST = { + status: 'pending', + requestType: 'delete', + requestStatus: 'pending', + emails: [ + {id: 'dummy@datawallet.com', status: 'confirmed'}, + {id: 'dummy@datawallet.com', status: 'pending'}, + ], + phones: [{id: '+13224324234', status: 'confirmed'}], + addresses: [ + { + line1: 'Dummy address1', + line2: 'Dummy address2', + zip: '12345', + state: 'DU', + status: 'confirmed', + }, + ], + identity: { + firstName: 'dummy first name', + lastName: 'dummy last name', + middleNames: 'dummy middle name', + }, +}; + +export const CONFIRMED_REQUEST = { + status: 'pending', + requestType: 'delete', + requestStatus: 'pending', + emails: [{id: 'dummy@datawallet.com', status: 'confirmed'}], + phones: [{id: '+13224324234', status: 'confirmed'}], + addresses: [ + { + line1: 'Dummy address1', + line2: 'Dummy address2', + zip: '12345', + state: 'DU', + status: 'confirmed', + }, + ], + identity: { + firstName: 'dummy first name', + lastName: 'dummy last name', + middleNames: 'dummy middle name', + }, +}; diff --git a/packages/no-account-app/src/app.js b/packages/no-account-app/src/app.js index d98ef9aae..9058c4ddf 100644 --- a/packages/no-account-app/src/app.js +++ b/packages/no-account-app/src/app.js @@ -4,7 +4,10 @@ import {Route} from 'react-router-dom'; import {injectIntl} from 'react-intl'; import Container from '@datawallet/ui/components/data-lookup/container'; +import Onboarding from '@datawallet/ui/components/onboarding/index'; +import TourEnded from '@datawallet/ui/modals/dashboard/tour-ended'; +import OnboardingState from './state/onboarding'; import Theme from './state/theme'; import Company from './state/company'; @@ -23,8 +26,20 @@ const App = ({intl}) => { const {publicData} = Company.useContainer(); const {theme} = Theme.useContainer(); const {brandSettings} = theme.global; - const companyName = publicData && publicData.name; + const { + onBoardingStep, + tourOpen, + nextStep, + prevStep, + pauseTour, + endTour, + tourEnded, + startTour, + openTourEndedModal, + closeTourEndedModal, + ONBOARDING_STEPS, + } = OnboardingState.useContainer(); return ( @@ -37,6 +52,10 @@ const App = ({intl}) => { }} minBoxHeight="100vh" helpUrl={publicData && publicData.helpUrl} + showTour={!tourEnded && !openTourEndedModal} + tourEnded={tourEnded} + tourOpen={tourOpen} + startTour={() => startTour({delay: 1000})} > { {companyName}, )} /> - @@ -62,6 +80,30 @@ const App = ({intl}) => { + {openTourEndedModal && ( + <> + + + )} + {tourOpen && ( + nextStep({delay: 200})} + prevStep={() => prevStep({delay: 100})} + startAt={onBoardingStep} + goToStep={onBoardingStep} + endTour={endTour} + rounded={18} + scrollOffset={10} + disableInteraction + /> + )} ); diff --git a/packages/no-account-app/src/index.js b/packages/no-account-app/src/index.js index 21210e876..f1fdba3cf 100644 --- a/packages/no-account-app/src/index.js +++ b/packages/no-account-app/src/index.js @@ -9,6 +9,7 @@ import '@datawallet/ui/css/base.css'; import App from './app'; import Theme from './state/theme'; import Company from './state/company'; +import Onboarding from './state/onboarding'; function IntlApp({defaultMessages}) { const {messages} = Intl.useContainer(); @@ -31,11 +32,13 @@ export default function Root({messages}) { return ( - - - - - + + + + + + + ); diff --git a/packages/no-account-app/src/screens/confirm-identity.js b/packages/no-account-app/src/screens/confirm-identity.js index 24cce41b8..b3b623c89 100644 --- a/packages/no-account-app/src/screens/confirm-identity.js +++ b/packages/no-account-app/src/screens/confirm-identity.js @@ -4,7 +4,14 @@ import {Box} from 'grommet'; import ConfirmIdentity from '@datawallet/ui/screens/data-lookup/confirm-identity'; import {ACTIONS} from '@datawallet/constants/cosmos'; -import {CONFIRMED} from '@datawallet/common/shared/request-service-statuses'; +import { + CONFIRMED, + PENDING, +} from '@datawallet/common/shared/request-service-statuses'; +import { + UNCONFIRMED_REQUEST, + CONFIRMED_REQUEST, +} from '@datawallet/constants/onboarding-data'; import ConfirmModal from '@datawallet/ui/modals/confirm'; import FormattedText from '@datawallet/ui/components/formatted-text'; import { @@ -18,6 +25,7 @@ import { updateUserIdentity, startLookupProcess, } from '../services/data-lookup'; +import OnboardingState from '../state/onboarding'; const ConfirmIdentityContainer = ({history, match}) => { const {id} = match.params; @@ -26,7 +34,7 @@ const ConfirmIdentityContainer = ({history, match}) => { const [showConfirmModal, setShowConfirmModal] = useState(false); const [submittingRequest, setSubmittingRequest] = useState(false); const [request, setRequest] = useState({ - status: 'pending', + status: PENDING, requestType: undefined, requestStatus: undefined, emails: [], @@ -47,11 +55,20 @@ const ConfirmIdentityContainer = ({history, match}) => { status, requestStatus, } = request; + const {dummyUser} = OnboardingState.useContainer(); const fetchUser = useCallback(async () => { + if (dummyUser && id === 'unconfirmed') { + setRequest(UNCONFIRMED_REQUEST); + return; + } + if (dummyUser && (id === 'confirmed' || id === 'confirmed-accepted')) { + setRequest(CONFIRMED_REQUEST); + return; + } const user = await retrieveUserData(id); setRequest(user); - }, [id]); + }, [id, dummyUser]); useEffect(() => { if (id) { diff --git a/packages/no-account-app/src/screens/request-form.js b/packages/no-account-app/src/screens/request-form.js index e396c177c..53265c7af 100644 --- a/packages/no-account-app/src/screens/request-form.js +++ b/packages/no-account-app/src/screens/request-form.js @@ -8,6 +8,7 @@ import RequestForm from '@datawallet/ui/screens/data-lookup/request-form'; import Back from '@datawallet/ui/components/back'; import Company from '../state/company'; +import OnboardingState from '../state/onboarding'; import {sendRequestForm} from '../services/data-lookup'; const recaptchaSiteKey = process.env.RECAPTCHA_SITE_KEY; @@ -18,6 +19,7 @@ const RequestFormContainer = ({ match, }) => { const {publicData} = Company.useContainer(); + const {dummyUser} = OnboardingState.useContainer(); const {privacyUrl} = publicData; const {requestType} = match.params; const [submitting, setSubmitting] = useState(false); @@ -54,6 +56,7 @@ const RequestFormContainer = ({ modules={getAvailableModules(requestType)} requiredIdentityPieces={minRequirements(requestType)} recaptchaSiteKey={recaptchaSiteKey} + dummyUser={dummyUser} /> ); diff --git a/packages/no-account-app/src/state/onboarding.js b/packages/no-account-app/src/state/onboarding.js new file mode 100644 index 000000000..327d005a1 --- /dev/null +++ b/packages/no-account-app/src/state/onboarding.js @@ -0,0 +1,174 @@ +import {useState, useEffect} from 'react'; +import {useHistory, useLocation} from 'react-router'; +import {createContainer} from 'unstated-next'; +import { + ONBOARDING_LANDING, + ONBOARDING_REQUEST_TYPE, + ONBOARDING_CONTACT_SECTIION, + ONBOARDING_EMAIL, + ONBOARDING_IDENTITY, + ONBOARDING_PHONE, + ONBOARDING_CONFIRMATION, + ONBOARDING_CONFIRMED_EMAIL, + ONBOARDING_PENDING_EMAIL, + ONBOARDING_ADD_MORE, + ONBOARDING_CONFIRM_EMAIL, + ONBOARDING_DELETE_EMAIL, + ONBOARDING_CONFIRMED, + ONBOARDING_DECLARATION, + ONBOARDING_DECLARATION_ACCEPTED, +} from '@datawallet/ui/components/onboarding/no-account-steps'; +import { + ONBOARDING_STEP, + ONBOARDING_ENDED, + ONBOARDING_ENDED_MODAL, + ONBOARDING_TOUR_OPEN, +} from '@datawallet/constants/onboarding'; + +const ONBOARDING_DELAY = 1000; +export const ONBOARDING_NEXT = 'NEXT'; +export const ONBOARDING_PREV = 'PREV'; + +export function useOnboarding() { + const {push: redirect} = useHistory(); + const {pathname} = useLocation(); + const [tourOpen, setTourOpen] = useState( + localStorage.getItem(ONBOARDING_TOUR_OPEN) === 'true', + ); + const localStorageStep = localStorage.getItem(ONBOARDING_STEP); + const [onBoardingStep, setOnBoardingStep] = useState( + localStorageStep ? Number(localStorageStep) : 0, + ); + const [action, setAction] = useState(null); + const [dummyUser, setDummyUser] = useState(true); + const [tourEnded, setTourEnded] = useState( + localStorage.getItem(ONBOARDING_ENDED) === 'true', + ); + + const ONBOARDING_STEPS = [ + ONBOARDING_LANDING, + ONBOARDING_CONTACT_SECTIION, + ONBOARDING_REQUEST_TYPE, + ONBOARDING_EMAIL, + ONBOARDING_PHONE, + ONBOARDING_IDENTITY, + ONBOARDING_CONFIRMATION, + ONBOARDING_CONFIRMED_EMAIL, + ONBOARDING_PENDING_EMAIL, + ONBOARDING_CONFIRM_EMAIL, + ONBOARDING_DELETE_EMAIL, + ONBOARDING_ADD_MORE, + ONBOARDING_DECLARATION, + ONBOARDING_DECLARATION_ACCEPTED, + ONBOARDING_CONFIRMED, + ]; + + const [openTourEndedModal, setTourEndedModal] = useState( + localStorage.getItem(ONBOARDING_ENDED_MODAL) === 'true', + ); + + useEffect(() => { + localStorage.setItem(ONBOARDING_STEP, onBoardingStep); + }, [onBoardingStep]); + useEffect(() => { + localStorage.setItem(ONBOARDING_TOUR_OPEN, tourOpen); + }, [tourOpen]); + useEffect(() => { + localStorage.setItem(ONBOARDING_ENDED, tourEnded); + }, [tourEnded]); + useEffect(() => { + localStorage.setItem(ONBOARDING_ENDED_MODAL, openTourEndedModal); + }, [openTourEndedModal]); + + const prevStep = ({delay}) => { + const prev = onBoardingStep - 1; + setOnBoardingStep(prev); + if (ONBOARDING_STEPS[prev].redirect) { + setTourOpen(false); + redirect(ONBOARDING_STEPS[prev].redirect); + if (delay) { + setTimeout(() => setTourOpen(true), delay); + } else { + setTourOpen(true); + } + } + setAction(ONBOARDING_PREV); + }; + + const pauseTour = () => { + setTourOpen(false); + setDummyUser(false); + }; + const startTour = ({delay}) => { + if ( + ONBOARDING_STEPS[onBoardingStep] && + pathname !== ONBOARDING_STEPS[onBoardingStep].redirect + ) { + redirect(ONBOARDING_STEPS[onBoardingStep].redirect); + } + if (!onBoardingStep) { + setOnBoardingStep(0); + setTourEnded(false); + } + + setDummyUser(true); + if (delay) { + setTimeout(() => setTourOpen(true), ONBOARDING_DELAY); + } else { + setTourOpen(true); + } + }; + + const endTour = () => { + setTourEnded(true); + setTourEndedModal(true); + setTourOpen(false); + setOnBoardingStep(null); + setDummyUser(false); + redirect('/'); + }; + + const nextStep = ({delay}) => { + const next = onBoardingStep + 1; + if (next >= ONBOARDING_STEPS.length) { + return endTour(); + } + setOnBoardingStep(next); + if (ONBOARDING_STEPS[next].redirect) { + setTourOpen(false); + redirect(ONBOARDING_STEPS[next].redirect); + if (delay) { + setTimeout(() => setTourOpen(true), delay); + } else { + setTourOpen(true); + } + } + return setAction(ONBOARDING_NEXT); + }; + + const closeTourEndedModal = () => { + setTourEndedModal(false); + }; + return { + tourEnded, + setTourEnded, + onBoardingStep, + setOnBoardingStep, + tourOpen, + setTourOpen, + openTourEndedModal, + setTourEndedModal, + nextStep, + prevStep, + pauseTour, + startTour, + endTour, + action, + closeTourEndedModal, + dummyUser, + ONBOARDING_STEPS, + }; +} + +const Onboarding = createContainer(useOnboarding); +export default Onboarding; diff --git a/packages/storybook/__snapshots__/storyshots.test.js.snap b/packages/storybook/__snapshots__/storyshots.test.js.snap index 8e57362c5..d8912ace8 100644 --- a/packages/storybook/__snapshots__/storyshots.test.js.snap +++ b/packages/storybook/__snapshots__/storyshots.test.js.snap @@ -20150,7 +20150,7 @@ exports[`Storyshots Screens/Data Lookup Confirm Identity - Already submitted + f } >