Nutella Pancake Machine

    @jetshop/flight-voyado
    TypeScript icon, indicating that this package has built-in type declarations

    2.1.2 • Public • Published

    @jetshop/flight-voyado

    This module provides a set of hooks needed to control the UI on both activation and/or signup flow using Voyado. For the complete integration to work, the customer must exist in both Jetshop and Voyado.

    The module requires Flight to be on at least version 5.3.0.

    There are three possible states the end customer may end up in, each containing up to five sub-states, all covered by this module. To get to any of these states, the end customer has two options:

    1. Using a soft login link provided by Voyado inside their newsletter / promo emails.
    2. Using the lookup field provided from inside the store. Like so: https://voyado.jetshop.se/se/login

    From both of these, the API is returning a current customer status. There are five statuses to be returned:

    1. NON_EXISTING_CUSTOMER: The customer does not exist in either Jetshop nor Voyado.
    2. PREEXISTING_CUSTOMER: The customer exists in both Jetshop and Voyado. No further action needed.
    3. ACTIVATION_REQUIRED: The customer exists in Voyado, but not in Jetshop. The account needs activation in order for us to create the customer in Jetshop too.
    4. ADDITIONAL_USER_DATA_REQUIRED: The customer exists in Voyado, but there are fields in Jetshop that are required from admin in order to fully create the customer. The API is returning the field values that are present in Voyado.
    5. COUNTRY_NOT_VALID: The customer is trying to activate account connected to another country, the API is returning the customers countryCode.

    If the customer ends up in ACTIVATION_REQUIRED, we'll be sending an email to the customer on how to set their password. This email is edited by Jetshop Support.

    If the customer used a soft login link and the customer tries to edit their account details, the API will return with an error. To visually show this to the customer, you could use the useStatusLogin hook provided from the module described below.

    useVoyadoLookup

    Arguments

    This hook takes a single options object as an argument, with the following keys:

    Argument Type Default Description
    activateOnLookup Boolean False Controlls whether the end customer should manually activate the account after the lookup.
    signInOnActivation Boolean False Controlls whether the end customer should manually login after previously said activation.
    personLookupConfigured Boolean False Controlls whether the store is configured to use lookup provided by Voyado.
    manualPersonLookup Boolean False If personlookup should be a manual step. Set this to true if the key differs from external lookup

    Note: the hook may be initialized with none, all or some of the keys.

    The hook is returning a set of self explanatory booleans to controll the UI. Something like:

    import LogInFormProvider from '@jetshop/ui/Auth/LogInFormProvider';
    import { useVoyadoLookup } from '@jetshop/flight-voyado';
    
    const { ...voyado } = useVoyadoLookup({
      activateOnLookup: true,
      signInOnActivation: false,
    });
    
    if (voyado.isPreExistingCustomer) {
      return (
        <LogInFormProvider redirect="my-pages" initialEmail={voyado?.customer?.emailAddress?.masked}>
          {({ globalError }) => (
            <>
              <input id="email" type="email" name="email" />
              <input type="password" name="password" />
    
              {globalError && <span>{globalError}</span>}
    
              <button type="submit">Login</button>
            </>
          )
        </LogInFormProvider>
      );
    }

    If you'd like to show something to indicate that the account is being activated, you could do something like:

    import { useVoyadoLookup } from '@jetshop/flight-voyado';
    import useAuth from '@jetshop/core/components/AuthContext/useAuth';
    
    const { customer, ...voyado } = useVoyadoLookup({
      activateOnLookup: false,
      signInOnActivation: false,
    });
    
    if (voyado.isInActivation) {
      const { isActivationRequired, isActivationPending, isActivationSuccess } = voyado;
      return (
        <>
          <header>
            {isActivationRequired &&
              t(
                'You exist as an member, click to activate your account. We will send you an email with a link on how to set your password.'
              )}
            {isActivationSuccess && t('We have successfully activated your account.')}
            {isActivationPending && t('Activating account')}
          </header>
          <button
            disabled={isActivationPending}
            loading={isActivationPending}
            onClick={() => {
              if (isActivationRequired) {
                voyado.activate();
              } else {
                logIn(voyado.customer.token);
              }
            }}
          >
            {isActivationRequired && t('Activate')}
            {isActivationPending && t('Activating')}
            {isActivationSuccess && t('Login')}
          </button>
        </>
      );
    }

    The hook is also returning all functions needed to call the lookup, to retry the lookup and to call the activation mutation. Like so:

    import { useVoyadoLookup } from '@jetshop/flight-voyado';
    
    const { activate, ...voyado } = useVoyadoLookup({
      activateOnLookup: false,
      signInOnActivation: true,
    });
    
    if (voyado.isActivationRequired) {
      return <button onClick={activate}>Activate my account please dear</button>;
    }

    Note: To avoid impossible states, the functions can only be called inside a few different states. IE: The activate function has no effect when the API has returned AdditionUserDataRequired, or when the activation is pending etcetc.

    In addition to this, the hook is also returning the customer data for you to pre-fill (with masked data) the form with if requested.

    In the example store, we're saving the potential customer to location.state, like so:

    // SignInPage:
    import { Redirect } from 'react-router';
    import { useVoyadoLookup } from '@jetshop/flight-voyado';
    
    function ExternalLookupField() {
      const { ...voyado } = useVoyadoLookup({
        activateOnLookup: true,
        signInOnActivation: true,
      });
    
      if (voyado.IsAdditionalDataRequired) {
        return <Redirect to={{ pathname: '/signup', state: { ...voyado } }} />;
      }
    }
    
    // SignupPage:
    import SignupFormProvider from '@jetshop/core/components/Auth/SignupFormContainer';
    import { useLocation } from 'react-router-dom';
    
    function SignupPage() {
      const { state } = useLocation();
      return (
        <div>
        {state?.isAdditionDataRequired && (
          <h4>Hey, looks like we're missing some key information of you.</h4>
          <h5>Please fill in the rest of the form and you're good to go.</h5>
        )}
        <SignupFormProvider lookupData={state?.customer}>{/* rest of form */}</SignupFormProvider>
        </div>
      )
    }

    Then, on signup, you could grab it using the useLocation hook provided by react-router-dom. SignupFormProvider is handling all prefilling for you as long as you pass the data along to it. If you'd like to manipulate the data before prefilling, for example, the email should be left out of the prefilling, just delete it before passing it along to the SignupFormProvider. Using location.state is recommended if you'd like to reuse the component, since the SoftLogin also places the potential customer inside state and redirects to whatever route provided to it.

    useSoftLoginStatus

    This hook does not take any arguments. Instead it's merely returning the state from softlogin, all reminding of the statuses returned by useVoyadoLookup hook. If placed for example inside the header, it can be used to show something to visualize the background processes when activating a customer etcetc.

    VoyadoProvider

    In order to use either of the hooks, the VoyadoProvider must be added to the Shop. Preferably somewhat high up. The provider takes an options object with the following keys:

    Keys

    Key Type Default Description
    loginPath String '/login' Route to redirect to if the customer exist, and manually should login.
    signupPath String '/signup' Route to redirect to if the customer does not exist.
    loginOnActivation Boolean True This controlls whether the end customer should be logged in when clicking a soft login link.
    redirectOnLookupFailure Boolean false This controlls whether the end customer should be redirected to signupPath if lookup status is NON_EXISTING_CUSTOMER or ADDITIONAL_USER_DATA_REQUIRED
    activateOnSoftLogin Boolean true This controlls whether the end customer should be activated upon clicking a soft login link (if activation is required).

    Note: the provider may be initialized with none, all or some of the keys.

    useLoginStatus

    If the customer used the soft login functionality to login, the account details cannot be edited. You can use this hook to visually show that the form is disabled and do an actual login with the return values from this hook. Once this is submitted and success status returned, the form is no longer disabled.

    import customerProfileQuery from './customerProfileQuery.gql';
    import LoginMutation from '../Auth/LoginMutation.gql';
    import { useCustomerQuery } from '@jetshop/core/components/Auth/useCustomer';
    import { useLoginStatus } from '@jetshop/flight-voyado';
    
      const { customer } = useCustomerQuery({
        query: customerProfileQuery
      });
    
    const { isSoftLoggedIn, authorize } = useLoginStatus({
      loginMutation: LoginMutation
    })
    
    <div>
    {isSoftLoggedIn ? (
      <form
        onSubmit={event => {
          event.preventDefault();
          const { password } = event.target;
            authorize({
              email: customer.email,
              password
            });
      }}
      >
        <h5>You need to sign in to edit your details.</h5>
        <label htmlFor="password">Password:</label>
        <input type="email" name="email" id="email" defaultValue={customer.email} />
        <input type="password" name="password" id="password" />
        <button type="submit">Login</button>
      </form>
    ) : (
        <button onClick={() => formState.editDetails(true)}>
          Update information
        </button>
    )}
    </div>

    This hook takes a single options object as an argument, with the following key:

    Argument Type Required Description
    loginMutation DocumentNode True The login mutation. Can be different per marketid, which is why it isn't included in the module


    \

    Softlogin chart

    graph TD
    A[Customer clicks email link with hash] -- Calls loginExternalCustomer  --> B(Was customer logged in?)
    B -- No --> F(Calls activateExternalCustomerByToken)
    B -- Yes --> D(Customer exist in both Jetshop and Voyado.)
    F --> E(Customer found?)
    E -- No --> I
    E -- Yes --> H(Checks customer status)
    H -- NON_EXISTING_CUSTOMER --> I(Customer not found. <br> If configured, redirect to signup page)
    H -- PREEXISTING_CUSTOMER --> J(Logged in)
    H -- ACTIVATION_REQUIRED --> K(Activates customer. <br> )
    K -- Sends email with information <br> on how to set password --> M(Customer activated)
    H -- ADDITIONAL_USER_DATA_REQUIRED --> L(Populates signup form <br> with masked data from Voyado)
    

    Note: the UI between some of the states can be controlled with the return values from useSoftLoginStatus.

    Lookup chart

    graph TD
    A[Customer fills LookupField] -- Calls externalCustomerLookup --> B(Checks customer status)
    B -- NON_EXISTING_CUSTOMER --> C(Returns no customer data)
    C -- If configured, calls personLookup --> E(Customer data found)
    E -- No -->F(Customer manually <br> fills required data)
    E -- Yes -->G(Prefill signup form <br> with personLookup data)
    F --> H
    G --> H(Customer signed up in  <br> both Jetshop and Voyado. <br> Can login.)
    B -- PREEXISTING_CUSTOMER --> I(Returns status)
    I --> J(Can login)
    B -- ACTIVATION_REQUIRED --> K(Returns status <br> masked email <br> external id)
    K -- Calls activateExternalCustomerById --> L(Success)
    L -- No --> M(Present error message)
    L -- Yes, sends email with information <br> on how to set password --> N(Customer activated. <br> Soft logged in)
    B -- ADDITIONAL_USER_DATA_REQUIRED --> O(Returns masked customer data)
    B -- COUNTRY_NOT_VALID --> QX(Returns countryCode)
    QX --> QX2(Present error message)
    O --> P(Populates signup form <br> with masked data)
    P --> Q(Customer manually fills <br>missing required data)
    Q --> Q1(Customer created. <br> Can login.)
    

    Note: the UI between some of the states can be controlled with the return values from useVoyadoLookup.

    A complete Voyado setup is available at https://gitlab.jetshop.se/flight/voyado, with the following key Voyado components:

    Keywords

    none

    Install

    npm i @jetshop/flight-voyado

    DownloadsWeekly Downloads

    21

    Version

    2.1.2

    License

    none

    Unpacked Size

    291 kB

    Total Files

    23

    Last publish

    Collaborators

    • jansjogren
    • mrlundis
    • alfredringstad
    • woahitsraj
    • jakobankarhem
    • erikpehrers