npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@norce/flight-voyado

v1.0.0

Published

Voyado Integration for Flight

Readme

@norce/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 '@norce/ui/Auth/LogInFormProvider';
import { useVoyadoLookup } from '@norce/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 '@norce/flight-voyado';
import useAuth from '@norce/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 '@norce/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 '@norce/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 '@norce/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 '@norce/core/components/Auth/useCustomer';
import { useLoginStatus } from '@norce/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:
  • https://gitlab.jetshop.se/flight/voyado/-/blob/master/src/components/Auth/ExternalLookupField.js
  • https://gitlab.jetshop.se/flight/voyado/-/blob/master/src/components/Auth/LogInPage.js
  • https://gitlab.jetshop.se/flight/voyado/-/blob/master/src/components/Auth/Signup/SignUpPage.js
  • https://gitlab.jetshop.se/flight/voyado/-/blob/master/src/components/Shop.js