Advanced Tutorial: KYC Plugin web views

We will go over how to develop home and verification web views for the KYC plugin.

Step 1: Initialize the KYC plugin web view.

Run the following commands to initialize the plugin web view:

cd web/
npm run add-plugin --plugin=kyc --type=kyc

Step 2: Install the initial plugin.

Run the following commands to build the initial plugin with a default view and install the plugin.

cd ..
npm run build --plugin=kyc

Step 3: disable CORS error on the browser.

The web view JSON file and bundles are served on the localhost port 8080. In order to allow the kit to access them in plugin development mode, we need to disable the CORS issue on the browser. This can be achieved by using a browser extension.

Step 4: Plugin interactive development mode

To start using interactive plugin development mode, we need to run the commands of steps 5 and 6 simultaneously.

Step 5: Create, watch and serve web view bundles and the JSON file.

Run the following command on the plugin starter kit web directory:

npm start --plugin=kyc

Step 6: Start Hollaex kit plugin development mode.

Run the following command on the Hollaex kit web directory:

npm run dev:plugin --plugin=kyc

In order to see the plugin's changes, we need to refresh the page after every change.

Step 7: Update home Form.js component.

Then add the following component to the Form.js file.

Form.js
import React, { useState, useEffect } from 'react';
import { Tabs } from 'antd';
import { withKit } from 'components/KitContext';
import Identity from './components/Identity';
import Documents from './components/Documents';

const { TabPane } = Tabs;
const INITIAL_KYC_TAB_KEY = 'initial_kyc_tab';

const Form = ({ strings: STRINGS, setActivePageContent: setPageContent, handleBack, activeLanguage, user, getCountry, getFormatTimestamp, icons: ICONS }) => {
  const kycTabs = [
      {
        key: 'identity',
        title: STRINGS['USER_VERIFICATION.TITLE_IDENTITY'],
      },
      {
        key: 'documents',
        title: STRINGS['USER_VERIFICATION.TITLE_ID_DOCUMENTS'],
      },
  ];

  const [activeKYCTab, setActiveKYCTab] = useState('identity');

  useEffect(() => {
    const kycInitialTab = localStorage.getItem(INITIAL_KYC_TAB_KEY);
    if (kycInitialTab && kycTabs.map(({key}) => key).includes(kycInitialTab)) {
      setActiveKYCTab(kycInitialTab);
    }
  }, []);

  const setActivePageContent = (key) => (page) => {
    localStorage.setItem(INITIAL_KYC_TAB_KEY, key);
    setPageContent(page)
  }

  const renderKYCVerificationHomeContent = (key, user, activeLanguage) => {
    switch (key) {
      case 'identity':
        return (
          <Identity
            activeLanguage={activeLanguage}
            user={user}
            handleBack={handleBack}
            setActivePageContent={setActivePageContent(key)}
            getFormatTimestamp={getFormatTimestamp}
            getCountry={getCountry}
            strings={STRINGS}
          />
        );
      case 'documents':
        return (
          <Documents
            user={user}
            setActivePageContent={setActivePageContent(key)}
            icons={ICONS}
            strings={STRINGS}
          />
        );
      default:
        return <div>No content</div>;
    }
  };

  return (
    <div>
      <Tabs activeKey={activeKYCTab} onTabClick={setActiveKYCTab}>
        {kycTabs.map(({ key, title }) => (
          <TabPane tab={title} key={key}>
            {renderKYCVerificationHomeContent(
              key,
              user,
              activeLanguage
            )}
          </TabPane>
        ))}
      </Tabs>
    </div>
  )
}

const mapContextToProps = ({ strings, setActivePageContent, handleBack, activeLanguage, user, getCountry, getFormatTimestamp, icons }) => ({
  strings,
  setActivePageContent,
  handleBack,
  activeLanguage,
  user,
  getCountry,
  getFormatTimestamp,
  icons,
});

export default withKit(mapContextToProps)(Form);

In this component, we are getting the following common props and target-specific props from the kit context using the withKit HOC.

  • Common props: strings, activeLanguage, user, icons

  • Target-specific props: setActivePageContent, handleBack, getCountry, getFormatTimestamp

See the SmartTarget with REMOTE_COMPONENT__KYC_VERIFICATION_HOME id on the verification container component for more details about the props.

Step 8: Add home components.

mkdir src/plugins/kyc/views/home/components
touch src/plugins/kyc/views/home/components/Documents.js
touch src/plugins/kyc/views/home/components/Identity.js

Documents

Documents.js
import React from 'react';
import moment from 'moment';
import classnames from 'classnames';
import { Button, PanelInformationRow, Editable as EditWrapper, Image } from 'hollaex-web-lib';

const Documents = ({
  user,
  setActivePageContent,
  icons: ICONS,
  strings: STRINGS,
}) => {
  const { id_data } = user;
  let note = '';
  if (id_data.status === 1) {
    note = STRINGS['USER_VERIFICATION.DOCUMENT_PENDING_NOTE'];
  } else if (id_data.status === 2) {
    note = id_data.note;
  } else {
    note = STRINGS['USER_VERIFICATION.DOCUMENT_VERIFIED_NOTE'];
  }
  return (
    <div>
      {id_data.status !== 0 && (
        <div className="d-flex my-3">
          <div
            className={classnames('mr-2', 'd-flex', 'justify-content-center', 'align-items-center')}
            title={
              STRINGS['USER_VERIFICATION.NOTE_FROM_VERIFICATION_DEPARTMENT']
            }
          >
            <Image
              icon={ICONS['NOTE_KYC']}
              iconId="NOTE_KYC"
              stringId="USER_VERIFICATION.NOTE_FROM_VERIFICATION_DEPARTMENT"
              wrapperClassName="document-note-icon"
            />
          </div>
          <PanelInformationRow
            stringId="USER_VERIFICATION.CUSTOMER_SUPPORT_MESSAGE,USER_VERIFICATION.DOCUMENT_PENDING_NOTE,USER_VERIFICATION.DOCUMENT_VERIFIED_NOTE"
            label={STRINGS['USER_VERIFICATION.CUSTOMER_SUPPORT_MESSAGE']}
            information={note}
            className="title-font"
            disable
          />
        </div>
      )}
      {id_data.status === 1 && (
        <div className="my-3">
          <PanelInformationRow
            stringId="USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ID_NUMBER_LABEL"
            label={
              STRINGS[
                'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ID_NUMBER_LABEL'
                ]
            }
            information={id_data.number}
            className="title-font"
            disable
          />
          <div className="d-flex">
            <PanelInformationRow
              stringId="USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ISSUED_DATE_LABEL"
              label={
                STRINGS[
                  'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ISSUED_DATE_LABEL'
                  ]
              }
              information={moment(id_data.issued_date).format('DD, MMMM, YYYY')}
              className="title-font mr-2"
              disable
            />
            <PanelInformationRow
              stringId="USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.EXPIRATION_DATE_LABEL"
              label={
                STRINGS[
                  'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.EXPIRATION_DATE_LABEL'
                  ]
              }
              information={moment(id_data.expiration_date).format(
                'DD, MMMM, YYYY'
              )}
              className="title-font"
              disable
            />
          </div>
        </div>
      )}
      {id_data.status !== 3 && (
        <div className="my-2 btn-wrapper">
          <div className="holla-verification-button static pt-4">
            <EditWrapper stringId="USER_VERIFICATION.START_DOCUMENTATION_SUBMISSION,USER_VERIFICATION.START_DOCUMENTATION_RESUBMISSION" />
            <Button
              label={
                id_data.status === 0
                  ? STRINGS['USER_VERIFICATION.START_DOCUMENTATION_SUBMISSION']
                  : STRINGS[
                    'USER_VERIFICATION.START_DOCUMENTATION_RESUBMISSION'
                    ]
              }
              onClick={() => setActivePageContent('kyc')}
            />
          </div>
        </div>
      )}
    </div>
  );
};

export default Documents;

Identityocalhost:3000/hello-exchange.

Identity.js
import React from 'react';
import { Button, PanelInformationRow, Editable as EditWrapper } from 'hollaex-web-lib';

const formatBirthday = {
  en: 'DD, MMMM, YYYY',
  fa: 'jDD, jMMMM, jYYYY',
};

const Identity = ({
  user,
  activeLanguage,
  setActivePageContent,
  handleBack,
  strings: STRINGS,
  getCountry,
  getFormatTimestamp,
}) => {
  const { address, id_data } = user;
  if (!address.country) {
    return (
      <div className="btn-wrapper">
        <div className="holla-verification-button static pt-4">
          <EditWrapper stringId="USER_VERIFICATION.START_IDENTITY_VERIFICATION" />
          <Button
            label={STRINGS['USER_VERIFICATION.START_IDENTITY_VERIFICATION']}
            onClick={() => setActivePageContent('kyc')}
          />
        </div>
      </div>
    );
  } else {
    return (
      <div>
        <div className="font-weight-bold text-lowercase">
          {STRINGS.formatString(
            STRINGS['USER_VERIFICATION.BANK_VERIFICATION_HELP_TEXT'],
            <span
              className="verification_link pointer"
              onClick={(e) => handleBack('kyc', e)}
            >
							{STRINGS['USER_VERIFICATION.DOCUMENT_SUBMISSION']}
						</span>
          )}
          <EditWrapper stringId="USER_VERIFICATION.BANK_VERIFICATION_HELP_TEXT,USER_VERIFICATION.DOCUMENT_SUBMISSION" />
        </div>
        <div className="my-3">
          <PanelInformationRow
            stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.FULL_NAME_LABEL"
            label={
              STRINGS[
                'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.FULL_NAME_LABEL'
                ]
            }
            information={user.full_name}
            className="title-font"
            disable
          />
          <div className="d-flex">
            <PanelInformationRow
              stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.GENDER_LABEL,USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.GENDER_OPTIONS.MAN,USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.GENDER_OPTIONS.WOMAN"
              label={
                STRINGS[
                  'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.GENDER_LABEL'
                  ]
              }
              information={
                user.gender === false
                  ? STRINGS[
                    'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.GENDER_OPTIONS.MAN'
                    ]
                  : STRINGS[
                    'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.GENDER_OPTIONS.WOMAN'
                    ]
              }
              className="title-font divider"
              disable
            />
            <PanelInformationRow
              stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.DOB_LABEL"
              label={
                STRINGS[
                  'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.DOB_LABEL'
                  ]
              }
              information={getFormatTimestamp(
                user.dob,
                formatBirthday[activeLanguage]
              )}
              className="title-font"
              disable
            />
          </div>
          <PanelInformationRow
            stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.NATIONALITY_LABEL"
            label={
              STRINGS[
                'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.NATIONALITY_LABEL'
                ]
            }
            information={getCountry(user.nationality).name}
            className="title-font"
            disable
          />
          <div className="d-flex">
            <PanelInformationRow
              stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.COUNTRY_LABEL"
              label={
                STRINGS[
                  'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.COUNTRY_LABEL'
                  ]
              }
              information={getCountry(address.country).name}
              className="title-font divider"
              disable
            />
            <PanelInformationRow
              stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.CITY_LABEL"
              label={
                STRINGS[
                  'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.CITY_LABEL'
                  ]
              }
              information={address.city}
              className="title-font"
              disable
            />
          </div>
          <div className="d-flex">
            <div className="w-75">
              <PanelInformationRow
                stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.ADDRESS_LABEL"
                label={
                  STRINGS[
                    'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.ADDRESS_LABEL'
                    ]
                }
                information={address.address}
                className="title-font divider"
                disable
              />
            </div>
            <PanelInformationRow
              stringId="USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.POSTAL_CODE_LABEL"
              label={
                STRINGS[
                  'USER_VERIFICATION.USER_DOCUMENTATION_FORM.FORM_FIELDS.POSTAL_CODE_LABEL'
                  ]
              }
              information={address.postal_code}
              className="title-font"
              disable
            />
          </div>
        </div>
        {id_data.status === 3 ? null : (
          <div className="btn-wrapper">
            <div className="holla-verification-button static pt-4">
              <EditWrapper stringId="USER_VERIFICATION.REVIEW_IDENTITY_VERIFICATION" />
              <Button
                label={
                  STRINGS['USER_VERIFICATION.REVIEW_IDENTITY_VERIFICATION']
                }
                onClick={() => setActivePageContent('kyc')}
              />
            </div>
          </div>
        )}
      </div>
    );
  }
};

export default Identity;

Step 9: Update verification Form.js component.

Form.js
import React, { useState, useEffect } from 'react';
import { Tabs } from 'antd';
import { withKit } from 'components/KitContext';
import Identity from './components/Identity';
import Documents from './components/Documents';

const { TabPane } = Tabs;
const INITIAL_KYC_TAB_KEY = 'initial_kyc_tab';

const Form = ({ handleBack: back, strings: STRINGS, user, constants, identityInitialValues, documentInitialValues }) => {
  const kycTabs = [
    {
      key: 'identity',
      title: STRINGS['USER_VERIFICATION.TITLE_IDENTITY'],
    },
    {
      key: 'documents',
      title: STRINGS['USER_VERIFICATION.TITLE_ID_DOCUMENTS'],
    },
  ];

  const [activeKYCTab, setActiveKYCTab] = useState('identity');

  useEffect(() => {
    const kycInitialTab = localStorage.getItem(INITIAL_KYC_TAB_KEY);
    if (kycInitialTab && kycTabs.map(({key}) => key).includes(kycInitialTab)) {
      setActiveKYCTab(kycInitialTab);
    }
  }, []);

  const handleBack = (key) => (params) => {
    localStorage.setItem(INITIAL_KYC_TAB_KEY, key);
    back(params)
  }

  const renderKYCVerificationContent = (key) => {

    switch (key) {
      case 'identity':
        return (
          <Identity
            fullName={user.full_name}
            initialValues={identityInitialValues(user, constants)}
            handleBack={handleBack(key)}
          />
        );
      case 'documents':
        return (
          <Documents
            idData={user.id_data}
            initialValues={documentInitialValues(user)}
            handleBack={handleBack(key)}
          />
        );
      default:
        return <div>No content</div>;
    }
  };

  return (
    <div className="presentation_container apply_rtl verification_container">
      <Tabs activeKey={activeKYCTab} onTabClick={setActiveKYCTab}>
        {kycTabs.map(({ key, title }) => (
          <TabPane tab={title} key={key}>
            {renderKYCVerificationContent(key)}
          </TabPane>
        ))}
      </Tabs>
    </div>
  )
}

const mapContextToProps = ({ handleBack, strings, user, constants, identityInitialValues, documentInitialValues }) => ({
  handleBack,
  strings,
  user,
  constants,
  identityInitialValues,
  documentInitialValues
});

export default withKit(mapContextToProps)(Form);

In this component, we are getting the following common and target-specific props from the kit context using the withKit HOC.

  • Common props: strings, user, constants

  • Target-specific props: handleBack, identityInitialValues, documentInitialValues

See the SmartTarget with REMOTE_COMPONENT__KYC_VERIFICATION id on the verification container component for more details about the props.

Step 10: Add verification components.

mkdir src/plugins/kyc/views/verification/components
touch src/plugins/kyc/views/verification/components/Documents.js
touch src/plugins/kyc/views/verification/components/Identity.js
touch src/plugins/kyc/views/verification/components/HeaderSection.js

Documents

Documents.js
import React, { Component } from 'react';
import { reduxForm, SubmissionError, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
import moment from 'moment';
import {
  isBefore,
  requiredWithCustomMessage,
} from 'utils';
import { Button, IconTitle, Image, Editable } from 'hollaex-web-lib';
import { HeaderSection } from 'components/HeaderSection';
import { withKit } from 'components/KitContext';
import {
  IdentificationFormSection,
  PORSection,
  SelfieWithPhotoId,
} from './HeaderSection';

import { isMobile } from 'react-device-detect';
const FORM_NAME = 'DocumentsVerification';

const selector = formValueSelector(FORM_NAME);
export const sizeLimitInMB = 6;
const sizeLimit = sizeLimitInMB * 1024 * 1024;


class Documents extends Component {
  state = {
    formFields: {},
    accSize: 0,
  };

  componentDidMount() {
    this.generateFormFields(this.props.activeLanguage);
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.activeLanguage !== this.props.activeLanguage) {
      this.generateFormFields(nextProps.activeLanguage);
    }

    if (JSON.stringify(nextProps.files) !== JSON.stringify(this.props.files)) {
      this.checkTotalFilesSize(nextProps.files)
    }
  }

  getFileSize = (file) => {
    if (file && file.size) {
      return file.size;
    } else {
      return 0;
    }
  }

  checkTotalFilesSize = (files = {}) => {
    let accSize = 0
    Object.entries(files).forEach(([_, file]) => {
      accSize += this.getFileSize(file);
    })

    this.setState({ accSize });
  }

  generateFormFields = (language) => {
    const { strings: STRINGS } = this.props;
    const formFields = {
      idDocument: {
        number: {
          type: 'text',
          stringId:
            'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ID_NUMBER_LABEL,USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ID_NUMBER_PLACEHOLDER,USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.ID_NUMBER',
          label:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ID_NUMBER_LABEL'
              ],
          placeholder:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ID_NUMBER_PLACEHOLDER'
              ],
          validate: [
            requiredWithCustomMessage(
              STRINGS[
                'USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.ID_NUMBER'
                ]
            ),
          ],
          fullWidth: isMobile,
          ishorizontalfield: true,
        },
        issued_date: {
          type: 'date-dropdown',
          stringId:
            'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ISSUED_DATE_LABEL,USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.ISSUED_DATE',
          label:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.ISSUED_DATE_LABEL'
              ],
          validate: [
            requiredWithCustomMessage(
              STRINGS[
                'USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.ISSUED_DATE'
                ]
            ),
            isBefore('', STRINGS['VALIDATIONS.INVALID_DATE']),
          ],
          endDate: moment().add(1, 'days'),
          language,
          fullWidth: isMobile,
          ishorizontalfield: true,
        },
        expiration_date: {
          type: 'date-dropdown',
          stringId:
            'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.EXPIRATION_DATE_LABEL,USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.EXPIRATION_DATE',
          label:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.EXPIRATION_DATE_LABEL'
              ],
          validate: [
            requiredWithCustomMessage(
              STRINGS[
                'USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.EXPIRATION_DATE'
                ]
            ),
            isBefore(moment().add(15, 'years'), STRINGS['VALIDATIONS.INVALID_DATE']),
          ],
          endDate: moment().add(15, 'years'),
          addYears: 15,
          yearsBefore: 5,
          language,
          fullWidth: isMobile,
          ishorizontalfield: true,
        },
      },
      id: {
        type: {
          type: 'hidden',
        },
        front: {
          type: 'file',
          stringId:
            'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.FRONT_LABEL,USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.FRONT_PLACEHOLDER,USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.FRONT',
          label:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.FRONT_LABEL'
              ],
          placeholder:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.FRONT_PLACEHOLDER'
              ],
          validate: [
            requiredWithCustomMessage(
              STRINGS['USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.FRONT']
            ),
          ],
          fullWidth: isMobile,
          ishorizontalfield: true,
        },
      },
      proof_of_residency: {
        type: {
          type: 'hidden',
        },
        back: {
          type: 'file',
          stringId:
            'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.POR_LABEL,USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.POR_PLACEHOLDER,USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.PROOF_OF_RESIDENCY',
          label:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.POR_LABEL'
              ],
          placeholder:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.POR_PLACEHOLDER'
              ],
          validate: [
            requiredWithCustomMessage(
              STRINGS[
                'USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.PROOF_OF_RESIDENCY'
                ]
            ),
          ],
          fullWidth: isMobile,
          ishorizontalfield: true,
        },
      },
      selfieWithNote: {
        type: {
          type: 'hidden',
        },
        proof_of_residency: {
          type: 'file',
          stringId:
            'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.SELFIE_PHOTO_ID_LABEL,USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.SELFIE_PHOTO_ID_PLACEHOLDER,USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.SELFIE_PHOTO_ID',
          label:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.SELFIE_PHOTO_ID_LABEL'
              ],
          placeholder:
            STRINGS[
              'USER_VERIFICATION.ID_DOCUMENTS_FORM.FORM_FIELDS.SELFIE_PHOTO_ID_PLACEHOLDER'
              ],
          validate: [
            requiredWithCustomMessage(
              STRINGS[
                'USER_VERIFICATION.ID_DOCUMENTS_FORM.VALIDATIONS.SELFIE_PHOTO_ID'
                ]
            ),
          ],
          fullWidth: isMobile,
          ishorizontalfield: true,
        },
      },
    };

    this.setState({ formFields });
  };

  handleSubmit = (formValues) => {
    const { moveToNextStep, setActivePageContent, updateDocuments } = this.props;
    return updateDocuments(formValues)
      .then(({ data }) => {
        const values = {
          type: formValues.type,
          number: formValues.number,
          expiration_date: formValues.expiration_date,
          issued_date: formValues.issued_date,
          status: 1,
        };

        moveToNextStep('documents', values);
        setActivePageContent('email');
      })
      .catch((err) => {
        const error = { _error: err.message };
        if (err.response && err.response.data) {
          error._error = err.response.data.message;
        }
        throw new SubmissionError(error);
      });
  };

  onGoBack = () => {
    const { setActivePageContent, handleBack } = this.props;
    setActivePageContent('email');
    handleBack('kyc');
  };

  render() {
    const {
      idData,
      handleSubmit,
      pristine,
      submitting,
      valid,
      error,
      // skip,
      openContactForm,
      icons: ICONS,
      strings: STRINGS,
      getErrorLocalized,
      renderFields,
    } = this.props;
    const { formFields, accSize } = this.state;
    const violated = accSize > sizeLimit;
    return (
      <div>
        <IconTitle
          stringId="USER_VERIFICATION.DOCUMENT_VERIFICATION"
          text={STRINGS['USER_VERIFICATION.DOCUMENT_VERIFICATION']}
          textType="title"
          iconPath={ICONS['VERIFICATION_DOCUMENT_NEW']}
        />
        <form
          className="d-flex flex-column w-100 verification_content-form-wrapper"
          onSubmit={this.handleSubmit}
        >
          <div className="verification-form-panel mt-3">
            <HeaderSection
              stringId="USER_VERIFICATION.DOCUMENT_PROOF_SUBMISSION"
              title={STRINGS['USER_VERIFICATION.DOCUMENT_PROOF_SUBMISSION']}
              openContactForm={openContactForm}
            >
              <IdentificationFormSection strings={STRINGS} />
            </HeaderSection>
            {renderFields(formFields.idDocument)}
            {renderFields(formFields.id)}
          </div>
          <div className="my-4" />

          <div className="verification-form-panel">
            {formFields.proof_of_residency && (
              <div>
                <HeaderSection
                  stringId="USER_VERIFICATION.ID_DOCUMENTS_FORM.INFORMATION.PROOF_OF_RESIDENCY"
                  title={
                    STRINGS[
                      'USER_VERIFICATION.ID_DOCUMENTS_FORM.INFORMATION.PROOF_OF_RESIDENCY'
                      ]
                  }
                >
                  <PORSection strings={STRINGS}/>
                </HeaderSection>
                {renderFields(formFields.proof_of_residency)}
              </div>
            )}
          </div>
          <div className="my-4" />

          <div className="verification-form-panel">
            {formFields.selfieWithNote && (
              <div>
                <HeaderSection
                  stringId="USER_VERIFICATION.ID_DOCUMENTS_FORM.INFORMATION.SELFIE.TITLE"
                  title={
                    STRINGS[
                      'USER_VERIFICATION.ID_DOCUMENTS_FORM.INFORMATION.SELFIE.TITLE'
                      ]
                  }
                >
                  <SelfieWithPhotoId strings={STRINGS} />
                </HeaderSection>
                <div className="my-2">
                  <Image
                    alt="document-sample"
                    iconId="SELF_KYC_ID_EN"
                    icon={ICONS['SELF_KYC_ID_EN']}
                    wrapperClassName="verification_document-sample"
                  />
                </div>
                {renderFields(formFields.selfieWithNote)}
              </div>
            )}
          </div>
          {error && (
            <div className="warning_text">{getErrorLocalized(error)}</div>
          )}
          {
            violated && (
              <div className="warning_text">
                <Editable stringId="USER_VERIFICATION.ID_DOCUMENTS_FORM.INFORMATION.ID_SECTION.VIOLATION_ERROR">
                  {STRINGS.formatString(STRINGS['USER_VERIFICATION.ID_DOCUMENTS_FORM.INFORMATION.ID_SECTION.VIOLATION_ERROR'], sizeLimitInMB)}
                </Editable>
              </div>
            )
          }
          <div className="my-4" />

          <div className="d-flex justify-content-center">
            <div className="d-flex justify-content-end f-1 verification-buttons-wrapper">
              <Button
                type="button"
                onClick={this.onGoBack}
                label={STRINGS['USER_VERIFICATION.GO_BACK']}
                disabled={submitting}
              />
            </div>
            <div className="separator" />
            <div className="d-flex flex-column f-1 verification-buttons-wrapper">
              <div>
                <Button
                  type="button"
                  onClick={handleSubmit(this.handleSubmit)}
                  label={
                    idData.status === 0
                      ? STRINGS['SUBMIT']
                      : `${STRINGS['RESUBMIT']}*`
                  }
                  disabled={pristine || submitting || !valid || !!error || violated}
                />
              </div>
              {idData.status !== 0 && (
                <span className="content-text">
									{STRINGS['USER_VERIFICATION.SUBMISSION_PENDING_TXT']}
								</span>
              )}
            </div>
          </div>
        </form>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  files: selector(
    state,
    'front',
    'back',
    'proof_of_residency'
  ),
});

const mapContextToProps = ({ strings, setActivePageContent, moveToNextStep, activeLanguage, getErrorLocalized, icons, renderFields, updateDocuments, openContactForm }) => ({
  strings,
  setActivePageContent,
  moveToNextStep,
  activeLanguage,
  getErrorLocalized,
  icons,
  renderFields,
  updateDocuments,
  openContactForm
});

const DocumentsForm = reduxForm({
  form: FORM_NAME,
})(withKit(mapContextToProps)(Documents));

export default connect(mapStateToProps)(DocumentsForm);

See the SmartTarget with REMOTE_COMPONENT__KYC_VERIFICATION id on the verification container component for more details about the props passed using the kit context.

Identity

Identity.js
import React, { Component } from 'react';
import moment from 'moment';
import { reduxForm, SubmissionError } from 'redux-form';
import {
  requiredWithCustomMessage as required,
  requiredBoolean,
  isBefore,
} from 'utils';
import { Button, IconTitle, Editable as EditWrapper } from 'hollaex-web-lib';
import { HeaderSection } from 'components/HeaderSection';
import { withKit } from 'components/KitContext';
import { isMobile } from 'react-device-detect';

const FORM_NAME = 'IdentityVerification';

class IdentityVerification extends Component {