<commit_before>/* global paypal */
/* The `paypal` global comes from a script included in _document.js */

import React from 'react';
import moment from 'moment';
import PropTypes from 'prop-types';

import { pick, get } from 'lodash';
import { withApollo } from 'react-apollo';
import { Button, Col, Form, FormGroup, Row } from 'react-bootstrap';

import colors from '../constants/colors';
import TierComponent from './Tier';
import InputField from './InputField';
import MatchingFundWithData from './MatchingFundWithData';
import ActionButton from './Button';
import SectionTitle from './SectionTitle';
import CreateOrganizationForm from './CreateOrganizationForm';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage, FormattedDate, FormattedTime } from 'react-intl';
import { capitalize, formatCurrency, isValidEmail, getEnvVar } from '../lib/utils';
import { getStripeToken } from '../lib/stripe';
import { checkUserExistence, signin } from '../lib/api';
import { getOcCardBalanceQuery } from '../graphql/queries';

class OrderForm extends React.Component {

  static propTypes = {
    order: PropTypes.object.isRequired, // { tier: {}, quantity: Int, interval: String, totalAmount: Int }
    collective: PropTypes.object.isRequired,
    LoggedInUser: PropTypes.object,
    onSubmit: PropTypes.func,
    matchingFund: PropTypes.string,
    redeemFlow: PropTypes.bool,
    intl: PropTypes.object,
    client: PropTypes.object,
  };

  constructor(props) {
    super(props);
    const { intl, order } = props;
    const tier = { ...order.tier };

    this.state = {
      isNewUser: true,
      loginSent: false,
      user: {},
      fromCollective: {},
      paymentMethod: {
        type: 'creditcard',
      },
      creditcard: {
        show: !this.props.redeemFlow,
        save: true,
      },
      ocCard: {
        applySent: false,
        loading: false,
        expanded: this.props.redeemFlow,
      },
      orgDetails: {
        show: false,
      },
      /* Set by loadPayPalButton(). This field stores the data from
         the authorization so we can ask the user's confirmation
         before processing the payment. */
      paypalOrderRequest: null,
      order: order || {},
      result: {},
    };

    this.state.order.totalAmount = this.state.order.totalAmount || tier.amount * (tier.quantity || 1);

    this.paymentMethodsOptions = [];
    this.allowOrganizations = (order.tier.type !== 'TICKET');

    this.messages = defineMessages({
      'order.contributeAs': { id: 'tier.order.contributeAs', defaultMessage: 'Contribute as' },
      'order.rsvpAs': { id: 'tier.order.rsvpAs', defaultMessage: 'RSVP as' },
      'order.profile.myself': { id: 'tier.order.profile.myself', defaultMessage: 'myself' },
      'order.success': { id: 'tier.order.success', defaultMessage: '🎉 Your order has been processed successfully' },
      'order.error': { id: 'tier.order.error', defaultMessage: 'An error occured 😳. The order didn\'t go through. Please try again in a few.' },
      'order.button': { id: 'tier.order.button', defaultMessage: 'place order' },
      'order.organization.create': { id: 'tier.order.organization.create', defaultMessage: 'create an organization' },
      'order.profile.logout': { id: 'tier.order.profile.logout', defaultMessage: 'logout to create a new profile' },
      'error.email.invalid': { id: 'error.email.invalid', defaultMessage: 'Invalid email address' },
      'creditcard.label': { id: 'creditcard.label', defaultMessage: 'Credit Card' },
      'creditcard.save': { id: 'creditcard.save', defaultMessage: 'Save credit card to {type, select, user {my account} other {{type} account}}' },
      'creditcard.missing': { id: 'creditcard.missing', defaultMessage: 'Credit card missing' },
      'creditcard.error': { id: 'creditcard.error', defaultMessage: 'Invalid credit card' },
      'paymentMethod.type': { id: 'paymentMethod.type', defaultMessage: 'Payment method' },
      'paymentMethod.creditcard': { id: 'paymentMethod.creditcard', defaultMessage: 'credit card' },
      'paymentMethod.bitcoin': { id: 'paymentMethod.bitcoin', defaultMessage: 'bitcoin' },
      'paymentMethod.paypal': { id: 'paymentMethod.paypal', defaultMessage: 'paypal' },
      'paymentMethod.manual': { id: 'paymentMethod.manual', defaultMessage: 'bank transfer' },
      'ocCard.label': { id: 'occard.label', defaultMessage: 'Gift Card' },
      'ocCard.apply': { id: 'occard.apply', defaultMessage: 'Apply' },
      'ocCard.invalid': { id: 'occard.invalid', defaultMessage: 'Invalid code' },
      'ocCard.expired': { id: 'occard.expired', defaultMessage: 'Expired code' },
      'ocCard.loading': { id: 'occard.loading', defaultMessage: 'Please wait...' },
      'ocCard.amountremaining': { id: 'occard.amountremaining', defaultMessage: 'Valid code. Amount available: ' },
      'ocCard.amounterror': { id: 'occard.amounterror', defaultMessage: 'You can only contribute up to the amount available on your gift card.' },
      'ticket.title': { id: 'tier.order.ticket.title', defaultMessage: 'RSVP' },
      'backer.title': { id: 'tier.order.backer.title', defaultMessage: 'Become a {name}' },
      'sponsor.title': { id: 'tier.order.sponsor.title', defaultMessage: 'Become a {name}' },
      'type.label': { id: 'tier.type.label', defaultMessage: 'type' },
      'firstName.label': { id: 'user.firstName.label', defaultMessage: 'first name' },
      'lastName.label': { id: 'user.lastName.label', defaultMessage: 'last name' },
      'company.label': { id: 'user.company.label', defaultMessage: 'company' },
      'website.label': { id: 'user.website.label', defaultMessage: 'website' },
      'twitterHandle.label': { id: 'user.twitterHandle.label', defaultMessage: 'twitter' },
      'twitterHandle.description': { id: 'user.twitterHandle.description', defaultMessage: 'If any' },
      'email.label': { id: 'user.email.label', defaultMessage: 'email' },
      'email.description': { id: 'user.email.description', defaultMessage: '* required' },
      'email.description.login': { id: 'signin.login.description', defaultMessage: 'Welcome back! Click on "Login" (or hit Enter) and we will send you a link to login by email.' },
      'email.description.signup': { id: 'signin.emailSent.description', defaultMessage: 'Login email sent. Please follow the instructions in that email to proceed.' },
      'description.label': { id: 'user.description.label', defaultMessage: 'Short bio' },
      'description.description': { id: 'user.description.description', defaultMessage: 'Present yourself in 60 characters or less, if you can!' },
      'totalAmount.label': { id: 'tier.totalAmount.label', defaultMessage: 'Total amount' },
      'startsAt.label': { id: 'tier.startsAt.label', defaultMessage: 'start date and time' },
      'endsAt.label': { id: 'tier.endsAt.label', defaultMessage: 'end date and time' },
      'order.error.organization.name.required': { id: 'order.error.organization.name.required', defaultMessage: 'Please provide a name for the new organization' },
      'order.error.organization.website.required': { id: 'order.error.organization.website.required', defaultMessage: 'Please provide a website for the new organization' },
      'order.publicMessage.placeholder': { id: 'order.publicMessage.placeholder', defaultMessage: 'Use this space to add a personal message (public)' },
      'newsletterOptIn.description': { id: 'user.newsletterOptIn.description', defaultMessage: 'Subscribe to the Open Collective newsletter.' },
    });

    this.fields = [
      {
        name: 'firstName',
        maxLength: 127,
      },
      {
        name: 'lastName',
        maxLength: 128,
      },
      {
        name: 'company',
        maxLength: 255,
      },
      {
        name: 'website',
        maxLength: 255,
      },
      {
        name: 'twitterHandle',
        pre: '@',
        maxLength: 255,
        validate: (val) => val.match(/^[A-Za-z0-9_]{1,15}$/),
      },
      {
        name: 'description',
        maxLength: 255,
      },
    ];

    this.fields = this.fields.map(field => {
      if (this.messages[`${field.name}.label`]) {
        field.label = intl.formatMessage(this.messages[`${field.name}.label`]);
      }
      if (this.messages[`${field.name}.description`]) {
        field.description = intl.formatMessage(this.messages[`${field.name}.description`]);
      }
      return field;
    });

    this.populateProfiles();
  }

  componentDidMount() {
    this._isMounted = true;
    this.UNSAFE_componentWillReceiveProps(this.props);
  }

  UNSAFE_componentWillReceiveProps(props) {
    const { LoggedInUser } = props;
    if (!LoggedInUser) return;
    if (!this._isMounted) return; // Fixes error: Can only update a mounted or mounting component
    this.setState({ LoggedInUser, isNewUser: !LoggedInUser });
    this.populateProfiles(LoggedInUser);
    setTimeout(() => this.selectProfile(LoggedInUser.CollectiveId), 0); // need to pass a cycle to let setState take effect
  }

  // All the following methods are arrow functions and auto-bind

  /** Interval set either in the tier or in the order object */
  interval = () =>
    this.state.order.interval || (this.state.tier && this.state.tier.interval);

  /** Populate the combo of payment methods */
  populatePaymentMethodTypes = (host) => {
    const { intl } = this.props;
    const paymentMethodTypeOptions = [
      { creditcard: intl.formatMessage(this.messages['paymentMethod.creditcard']) },
    ];
    /* We only support paypal for one time donations to the open
       source collective for now. */
    if (host.id === 11004 && this.interval() === null) {
      paymentMethodTypeOptions.push({
        payment: intl.formatMessage(this.messages['paymentMethod.paypal']),
      });
    }
    // Add the option to pay by wire transfer for the BrusselsTogether host
    if (get(host, 'settings.paymentMethods.manual') && this.interval() === null) {
      paymentMethodTypeOptions.push({
        manual: get(host, 'settings.paymentMethods.manual.title') || intl.formatMessage(this.messages['paymentMethod.manual']),
      });
    }
    this.paymentMethodTypeOptions = paymentMethodTypeOptions;
  };

  paymentMethodsOptionsForCollective = (paymentMethods, collective) => {
    return paymentMethods.map(pm => {
      const value = pm.uuid;
      const brand = get(pm, 'data.brand') || get(pm, 'type');
      /* The expiryDate field will show up for prepaid cards */
      const expiration = pm.expiryDate
        ? `- exp ${moment(pm.expiryDate).format('MM/Y')}`
        : (get(pm, 'data.expMonth') || get(pm, 'data.expYear'))
            ? `- exp ${get(pm, 'data.expMonth')}/${get(pm, 'data.expYear')}`
            : '';
      /* Prepaid cards have their balance available */
      const balance = pm.balance
        ? `(${formatCurrency(pm.balance, pm.currency)})`<commit_msg>fix get interval<commit_after>/* global paypal */
/* The `paypal` global comes from a script included in _document.js */

import React from 'react';
import moment from 'moment';
import PropTypes from 'prop-types';

import { pick, get } from 'lodash';
import { withApollo } from 'react-apollo';
import { Button, Col, Form, FormGroup, Row } from 'react-bootstrap';

import colors from '../constants/colors';
import TierComponent from './Tier';
import InputField from './InputField';
import MatchingFundWithData from './MatchingFundWithData';
import ActionButton from './Button';
import SectionTitle from './SectionTitle';
import CreateOrganizationForm from './CreateOrganizationForm';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage, FormattedDate, FormattedTime } from 'react-intl';
import { capitalize, formatCurrency, isValidEmail, getEnvVar } from '../lib/utils';
import { getStripeToken } from '../lib/stripe';
import { checkUserExistence, signin } from '../lib/api';
import { getOcCardBalanceQuery } from '../graphql/queries';

class OrderForm extends React.Component {

  static propTypes = {
    order: PropTypes.object.isRequired, // { tier: {}, quantity: Int, interval: String, totalAmount: Int }
    collective: PropTypes.object.isRequired,
    LoggedInUser: PropTypes.object,
    onSubmit: PropTypes.func,
    matchingFund: PropTypes.string,
    redeemFlow: PropTypes.bool,
    intl: PropTypes.object,
    client: PropTypes.object,
  };

  constructor(props) {
    super(props);
    const { intl, order } = props;
    const tier = { ...order.tier };

    this.state = {
      isNewUser: true,
      loginSent: false,
      user: {},
      fromCollective: {},
      paymentMethod: {
        type: 'creditcard',
      },
      creditcard: {
        show: !this.props.redeemFlow,
        save: true,
      },
      ocCard: {
        applySent: false,
        loading: false,
        expanded: this.props.redeemFlow,
      },
      orgDetails: {
        show: false,
      },
      /* Set by loadPayPalButton(). This field stores the data from
         the authorization so we can ask the user's confirmation
         before processing the payment. */
      paypalOrderRequest: null,
      order: order || {},
      result: {},
    };

    this.state.order.totalAmount = this.state.order.totalAmount || tier.amount * (tier.quantity || 1);
    this.paymentMethodsOptions = [];
    this.allowOrganizations = (order.tier.type !== 'TICKET');

    this.messages = defineMessages({
      'order.contributeAs': { id: 'tier.order.contributeAs', defaultMessage: 'Contribute as' },
      'order.rsvpAs': { id: 'tier.order.rsvpAs', defaultMessage: 'RSVP as' },
      'order.profile.myself': { id: 'tier.order.profile.myself', defaultMessage: 'myself' },
      'order.success': { id: 'tier.order.success', defaultMessage: '🎉 Your order has been processed successfully' },
      'order.error': { id: 'tier.order.error', defaultMessage: 'An error occured 😳. The order didn\'t go through. Please try again in a few.' },
      'order.button': { id: 'tier.order.button', defaultMessage: 'place order' },
      'order.organization.create': { id: 'tier.order.organization.create', defaultMessage: 'create an organization' },
      'order.profile.logout': { id: 'tier.order.profile.logout', defaultMessage: 'logout to create a new profile' },
      'error.email.invalid': { id: 'error.email.invalid', defaultMessage: 'Invalid email address' },
      'creditcard.label': { id: 'creditcard.label', defaultMessage: 'Credit Card' },
      'creditcard.save': { id: 'creditcard.save', defaultMessage: 'Save credit card to {type, select, user {my account} other {{type} account}}' },
      'creditcard.missing': { id: 'creditcard.missing', defaultMessage: 'Credit card missing' },
      'creditcard.error': { id: 'creditcard.error', defaultMessage: 'Invalid credit card' },
      'paymentMethod.type': { id: 'paymentMethod.type', defaultMessage: 'Payment method' },
      'paymentMethod.creditcard': { id: 'paymentMethod.creditcard', defaultMessage: 'credit card' },
      'paymentMethod.bitcoin': { id: 'paymentMethod.bitcoin', defaultMessage: 'bitcoin' },
      'paymentMethod.paypal': { id: 'paymentMethod.paypal', defaultMessage: 'paypal' },
      'paymentMethod.manual': { id: 'paymentMethod.manual', defaultMessage: 'bank transfer' },
      'ocCard.label': { id: 'occard.label', defaultMessage: 'Gift Card' },
      'ocCard.apply': { id: 'occard.apply', defaultMessage: 'Apply' },
      'ocCard.invalid': { id: 'occard.invalid', defaultMessage: 'Invalid code' },
      'ocCard.expired': { id: 'occard.expired', defaultMessage: 'Expired code' },
      'ocCard.loading': { id: 'occard.loading', defaultMessage: 'Please wait...' },
      'ocCard.amountremaining': { id: 'occard.amountremaining', defaultMessage: 'Valid code. Amount available: ' },
      'ocCard.amounterror': { id: 'occard.amounterror', defaultMessage: 'You can only contribute up to the amount available on your gift card.' },
      'ticket.title': { id: 'tier.order.ticket.title', defaultMessage: 'RSVP' },
      'backer.title': { id: 'tier.order.backer.title', defaultMessage: 'Become a {name}' },
      'sponsor.title': { id: 'tier.order.sponsor.title', defaultMessage: 'Become a {name}' },
      'type.label': { id: 'tier.type.label', defaultMessage: 'type' },
      'firstName.label': { id: 'user.firstName.label', defaultMessage: 'first name' },
      'lastName.label': { id: 'user.lastName.label', defaultMessage: 'last name' },
      'company.label': { id: 'user.company.label', defaultMessage: 'company' },
      'website.label': { id: 'user.website.label', defaultMessage: 'website' },
      'twitterHandle.label': { id: 'user.twitterHandle.label', defaultMessage: 'twitter' },
      'twitterHandle.description': { id: 'user.twitterHandle.description', defaultMessage: 'If any' },
      'email.label': { id: 'user.email.label', defaultMessage: 'email' },
      'email.description': { id: 'user.email.description', defaultMessage: '* required' },
      'email.description.login': { id: 'signin.login.description', defaultMessage: 'Welcome back! Click on "Login" (or hit Enter) and we will send you a link to login by email.' },
      'email.description.signup': { id: 'signin.emailSent.description', defaultMessage: 'Login email sent. Please follow the instructions in that email to proceed.' },
      'description.label': { id: 'user.description.label', defaultMessage: 'Short bio' },
      'description.description': { id: 'user.description.description', defaultMessage: 'Present yourself in 60 characters or less, if you can!' },
      'totalAmount.label': { id: 'tier.totalAmount.label', defaultMessage: 'Total amount' },
      'startsAt.label': { id: 'tier.startsAt.label', defaultMessage: 'start date and time' },
      'endsAt.label': { id: 'tier.endsAt.label', defaultMessage: 'end date and time' },
      'order.error.organization.name.required': { id: 'order.error.organization.name.required', defaultMessage: 'Please provide a name for the new organization' },
      'order.error.organization.website.required': { id: 'order.error.organization.website.required', defaultMessage: 'Please provide a website for the new organization' },
      'order.publicMessage.placeholder': { id: 'order.publicMessage.placeholder', defaultMessage: 'Use this space to add a personal message (public)' },
      'newsletterOptIn.description': { id: 'user.newsletterOptIn.description', defaultMessage: 'Subscribe to the Open Collective newsletter.' },
    });

    this.fields = [
      {
        name: 'firstName',
        maxLength: 127,
      },
      {
        name: 'lastName',
        maxLength: 128,
      },
      {
        name: 'company',
        maxLength: 255,
      },
      {
        name: 'website',
        maxLength: 255,
      },
      {
        name: 'twitterHandle',
        pre: '@',
        maxLength: 255,
        validate: (val) => val.match(/^[A-Za-z0-9_]{1,15}$/),
      },
      {
        name: 'description',
        maxLength: 255,
      },
    ];

    this.fields = this.fields.map(field => {
      if (this.messages[`${field.name}.label`]) {
        field.label = intl.formatMessage(this.messages[`${field.name}.label`]);
      }
      if (this.messages[`${field.name}.description`]) {
        field.description = intl.formatMessage(this.messages[`${field.name}.description`]);
      }
      return field;
    });

    this.populateProfiles();
  }

  componentDidMount() {
    this._isMounted = true;
    this.UNSAFE_componentWillReceiveProps(this.props);
  }

  UNSAFE_componentWillReceiveProps(props) {
    const { LoggedInUser } = props;
    if (!LoggedInUser) return;
    if (!this._isMounted) return; // Fixes error: Can only update a mounted or mounting component
    this.setState({ LoggedInUser, isNewUser: !LoggedInUser });
    this.populateProfiles(LoggedInUser);
    setTimeout(() => this.selectProfile(LoggedInUser.CollectiveId), 0); // need to pass a cycle to let setState take effect
  }

  // All the following methods are arrow functions and auto-bind

  /** Interval set either in the tier or in the order object */
  interval = () =>
    this.state.order.interval || (get(this.state, 'order.tier.interval'))

  /** Populate the combo of payment methods */
  populatePaymentMethodTypes = (host) => {
    const { intl } = this.props;
    const paymentMethodTypeOptions = [
      { creditcard: intl.formatMessage(this.messages['paymentMethod.creditcard']) },
    ];
    /* We only support paypal for one time donations to the open
       source collective for now. */
    if (host.id === 11004 && this.interval() === null) {
      paymentMethodTypeOptions.push({
        payment: intl.formatMessage(this.messages['paymentMethod.paypal']),
      });
    }
    // Add the option to pay by wire transfer for the BrusselsTogether host
    if (get(host, 'settings.paymentMethods.manual') && !this.interval()) {
      paymentMethodTypeOptions.push({
        manual: get(host, 'settings.paymentMethods.manual.title') || intl.formatMessage(this.messages['paymentMethod.manual']),
      });
    }
    this.paymentMethodTypeOptions = paymentMethodTypeOptions;
  };

  paymentMethodsOptionsForCollective = (paymentMethods, collective) => {
    return paymentMethods.map(pm => {
      const value = pm.uuid;
      const brand = get(pm, 'data.brand') || get(pm, 'type');
      /* The expiryDate field will show up for prepaid cards */
      const expiration = pm.expiryDate
        ? `- exp ${moment(pm.expiryDate).format('MM/Y')}`
        : (get(pm, 'data.expMonth') || get(pm, 'data.expYear'))
            ? `- exp ${get(pm, 'data.expMonth')}/${get(pm, 'data.expYear')}`
            : '';
      /* Prepaid cards have their balance available */
      const balance = pm.balance
        ? `(${formatCurrency(pm.balance, pm.currency)})`
{'fraction_non_alphanumeric': 0.14938294345183276, 'fraction_numerical': 0.002947135752440597, 'mean_word_length': 3.409543147208122, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '213633', 'n_tokens_mistral': 6524, 'n_tokens_neox': 6323, 'n_words': 1618}