Search code examples
javascriptreactjssimplemodal

Get the ID of a selected Card element and pass it to a modal to select the element


I have a code which renders several plans in Cards. Here is the code:

import React, { useEffect, useState } from 'react';
import { I18nText, Card, BaseModal, Button } from '@wtag/react-comp-lib';
import PropTypes from 'prop-types';
import greenCheck from 'affiliateIcons/green-check.svg';
import doneMark from '../../../../assets/images/done-24px.svg';
import routes from '../../../shared/routes';
import httpClient from '../../../shared/libraries/httpClient';

const Plans = ({ affiliateId }) => {
  const [plans, setPlans] = useState([]);
  const [currentPlan, setCurrentPlan] = useState([]);
  const [modalOpen, setModalOpen] = useState(false);

  const fetchPlans = async () => {
    const { data } = await httpClient.get(
      routes.billing.changePlan.fetch({ affiliate_id: affiliateId }),
    );
    setPlans(data.plans);
    setCurrentPlan(data.currentPlan);
  };

  const selectPlan = async plan => {
    const { data } = await httpClient.put(
      routes.billing.changePlan.update({
        id: plan.id,
        affiliate_id: affiliateId,
      }),
    );

    if (data.error === null) {
      fetchPlans(data.currentPlan);
      setModalOpen(false);
    }
  };

  useEffect(() => {
    fetchPlans();
  }, []);

  const data = [
    {
      id: 0,
      identifier: 'free-2020',
      name: <I18nText id="pricing.plans.name.free" />,
      planTitle: 'free',
      price: '00',
      freeSearches: <I18nText id="pricing.plans.features.unlimited" />,
      freeBookings: <I18nText id="pricing.plans.features.unlimited" />,
      travelerProfiles: <I18nText id="pricing.plans.features.unlimited" />,
      supportTickets: <I18nText id="pricing.plans.features.available" />,
      supportByPhone: '-',
      supplierChannels: '-',
      customDomain: '-',
      active: true,
    },
    {
      id: 1,
      identifier: 'basic-2020',
      name: <I18nText id="pricing.plans.name.basic" />,
      planTitle: 'basic',
      price: '29',
      freeSearches: <I18nText id="pricing.plans.features.unlimited" />,
      freeBookings: <I18nText id="pricing.plans.features.unlimited" />,
      travelerProfiles: <I18nText id="pricing.plans.features.unlimited" />,
      supportTickets: <I18nText id="pricing.plans.features.available" />,
      supportByPhone: '-',
      supplierChannels: '1',
      customDomain: '-',
      active: false,
    },
    {
      id: 2,
      identifier: 'standard-2020',
      name: <I18nText id="pricing.plans.name.standard" />,
      planTitle: 'standard',
      price: '59',
      freeSearches: <I18nText id="pricing.plans.features.unlimited" />,
      freeBookings: <I18nText id="pricing.plans.features.unlimited" />,
      travelerProfiles: <I18nText id="pricing.plans.features.unlimited" />,
      supportTickets: <I18nText id="pricing.plans.features.available" />,
      supportByPhone: '1hr/mo',
      supplierChannels: '2',
      customDomain: '-',
      active: false,
    },
    {
      id: 3,
      identifier: 'professional-2020',
      name: <I18nText id="pricing.plans.name.professional" />,
      planTitle: 'professional',
      price: '99',
      freeSearches: <I18nText id="pricing.plans.features.unlimited" />,
      freeBookings: <I18nText id="pricing.plans.features.unlimited" />,
      travelerProfiles: <I18nText id="pricing.plans.features.unlimited" />,
      supportTickets: <I18nText id="pricing.plans.features.available" />,
      supportByPhone: '3hr/mo',
      supplierChannels: '5',
      customDomain: 'Yes',
      active: false,
    },
    {
      id: 4,
      identifier: 'custom-2020',
      name: <I18nText id="pricing.plans.name.custom" />,
      planTitle: 'custom',
      price: '-',
      freeSearches: <I18nText id="pricing.plans.features.unlimited" />,
      freeBookings: <I18nText id="pricing.plans.features.unlimited" />,
      travelerProfiles: <I18nText id="pricing.plans.features.unlimited" />,
      supportTickets: <I18nText id="pricing.plans.features.available" />,
      supportByPhone: <I18nText id="pricing.plans.features.unlimited" />,
      supplierChannels: <I18nText id="pricing.plans.features.unlimited" />,
      customDomain: <I18nText id="pricing.plans.features.available" />,
      active: false,
    },
  ];

  const planData = data.map(d => {
    const correspondingPlan = plans.filter(
      plan => d.identifier === plan.identifier,
    )[0];
    if (correspondingPlan) {
      return { ...d, ...correspondingPlan };
    }
    return d;
  });

  const tableHeader = [
    { id: 0, name: <I18nText id="pricing.table.header.free" /> },
    { id: 1, name: <I18nText id="pricing.table.header.basic" /> },
    { id: 2, name: <I18nText id="pricing.table.header.standard" /> },
    { id: 3, name: <I18nText id="pricing.table.header.professional" /> },
    { id: 4, name: <I18nText id="pricing.table.header.custom" /> },
  ];

  const planOptions = [
    {
      id: 0,
      name: <I18nText id="pricing.table.feature.subscription.search" />,
      isFree: doneMark,
      isBasic: doneMark,
      isStandard: doneMark,
      isProfessional: doneMark,
      isCustom: doneMark,
    },
    {
      id: 1,
      name: <I18nText id="pricing.table.feature.subscription.bookings" />,
      isFree: doneMark,
      isBasic: doneMark,
      isStandard: doneMark,
      isProfessional: doneMark,
      isCustom: doneMark,
    },
    {
      id: 2,
      name: <I18nText id="pricing.table.feature.subscription.traveler" />,
      isFree: doneMark,
      isBasic: doneMark,
      isStandard: doneMark,
      isProfessional: doneMark,
      isCustom: doneMark,
    },
    {
      id: 3,
      name: <I18nText id="pricing.table.feature.subscription.corporate" />,
      isFree: doneMark,
      isBasic: doneMark,
      isStandard: doneMark,
      isProfessional: doneMark,
      isCustom: doneMark,
    },
    {
      id: 4,
      name: <I18nText id="pricing.table.feature.subscription.quotes" />,
      isFree: doneMark,
      isBasic: doneMark,
      isStandard: doneMark,
      isProfessional: doneMark,
      isCustom: doneMark,
    },
    {
      id: 5,
      name: <I18nText id="pricing.table.feature.subscription.ndc" />,
      isFree: doneMark,
      isBasic: doneMark,
      isStandard: doneMark,
      isProfessional: doneMark,
      isCustom: doneMark,
    },
  ];

  const getCurrentPlan = currentPlan && currentPlan.name;

  const newPlansCard = planData.map(plan => {
    const matchPlan = plan.name === getCurrentPlan;
    return (
      <>
        <Card
          className="billing-plans__plans-card"
          size="normal"
          version="v2"
          title={plan.name}
          headerCenter={true}
          key={plan.id}
        >
          {matchPlan ? (
            <div className="billing-plans__current-plan-icon">
              <img src={greenCheck} alt="check" />
            </div>
          ) : null}
          <div className="billing-plans__plans-card-title">
            <sup>US$</sup>
            {plan.price}
            <sub>
              /<I18nText id="pricing.plans.misc.month" />
            </sub>
          </div>

          <div className="billing-plans__plans-card-features-wrapper">
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.searches" />
              <span className="billing-plans__features-access">
                {plan.freeSearches}
              </span>
            </div>
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.bookings" />
              <span className="billing-plans__features-access">
                {plan.freeBookings}
              </span>
            </div>
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.profiles" />
              <span className="billing-plans__features-access">
                {plan.travelerProfiles}
              </span>
            </div>
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.tickets" />
              <span className="billing-plans__features-access">
                {plan.supportTickets}
              </span>
            </div>
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.phone" />
              <span className="billing-plans__features-access">
                {plan.supportByPhone}
              </span>
            </div>
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.channels" />
              <span className="billing-plans__features-access">
                {plan.supplierChannels}
              </span>
            </div>
            <div className="billing-plans__plans-card-features">
              <I18nText id="pricing.plans.features.domain" />
              <span className="billing-plans__features-access">
                {plan.customDomain}
              </span>
            </div>
          </div>

          <div className="billing-plans__plans-card-actions">
            {!matchPlan ? (
              <div>
                <BaseModal
                  linkText={<I18nText id="pricing.plans.actions.select" />}
                  linkClassName="billing-plans__plans-card-actions-button"
                  open={modalOpen}
                  onModalClose={() => setModalOpen(false)}
                  onModalOpen={() => setModalOpen(true)}
                  header={
                    <I18nText id="pricing.plans.actions.confirmationText" />
                  }
                  size="small"
                  showCloseIcon={false}
                  key={plan.id}
                >
                  <div className="col-12">
                    <Button
                      version="v2"
                      type="accent"
                      size="normal"
                      label={I18n.t('shared.action.confirm')}
                      key={plan.id}
                      onClick={() => {
                        selectPlan(plan);
                      }}
                    />
                    <Button
                      version="v2"
                      size="normal"
                      label={I18n.t('shared.action.cancel')}
                      onClick={() => setModalOpen(false)}
                    />
                  </div>
                </BaseModal>
              </div>
            ) : null}
          </div>
        </Card>
      </>
    );
  });

  const pricingTable = () => {
    return (
      <div>
        <div className="container-full billing-plans__pricing-table">
          <Card size="full" version="v2" key={planOptions.id}>
            <div className="billing-plans__table-header">
              <I18nText id="pricing.plans.sub_title" />
            </div>
            <table className="billing-plans__table-body">
              <thead>
                <tr>
                  <th className="billing-plans__table-sub-header">
                    <I18nText id="pricing.table.feature.header" />
                  </th>
                  {tableHeader.map(({ name, id }) => (
                    <th className="billing-plans__table-sub-header" key={id}>
                      {name}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {planOptions.map(
                  ({
                    id,
                    name,
                    isFree,
                    isBasic,
                    isStandard,
                    isProfessional,
                    isCustom,
                  }) => (
                    <tr key={id}>
                      <td className="billing-plans__table-data">{name}</td>
                      <td className="billing-plans__table-plan">
                        <img src={isFree} alt="check" />
                      </td>
                      <td className="billing-plans__table-plan">
                        <img src={isBasic} alt="check" />
                      </td>
                      <td className="billing-plans__table-plan">
                        <img src={isStandard} alt="check" />
                      </td>
                      <td className="billing-plans__table-plan">
                        <img src={isProfessional} alt="check" />
                      </td>
                      <td className="billing-plans__table-plan">
                        <img src={isCustom} alt="check" />
                      </td>
                    </tr>
                  ),
                )}
              </tbody>
            </table>
          </Card>
        </div>
      </div>
    );
  };

  return (
    <div className="billing-plans">
      <div>
        <div className="grid">
          <div className="col-12 offset-xlg-1 offset-lg-1 offset-md-1 offset-sm-1 offset-xs-1 offset-xxs-1">
            <div className="billing-plans__plans-header">
              <I18nText id="pricing.plans.title" />
            </div>
          </div>
        </div>
        <div className="billing-plans__plans-wrapper">
          {currentPlan && newPlansCard}
        </div>
      </div>
      {pricingTable()}
    </div>
  );
};

Plans.propTypes = {
  currentPlan: PropTypes.shape({
    name: PropTypes.string,
    identifier: PropTypes.string,
    id: PropTypes.number,
  }).isRequired,
  affiliateId: PropTypes.number.isRequired,
};

export default Plans;

I want to get the ID of the selected card and pass it to the modal. While the first button i.e: the confirmation button of the modal of a Card is clicked then the Card will be shown as selected with the tick mark. Like in each Card there are Select Plan button which opens the modal dialog to get confirmation if the user really wants to select the plan or not. If he selects Yes then the ID of the particular Card is fetched and then the ID is passed to the selectPlan function to Select the particular Card as Plan. In the existing code base in the first button of the modal I have an onClick function which sends the particular selected Plan to the selectPlan function like this selectPlan(plan). But now I only get either Card with ID 2 or 6 no matter which Card I select using the respective modal, although there are 5 Cards with ID 2, 3, 4, 5, 6. That means there are some problem in getting the particular selected Card ID in the current implementation. I want to know what should I change in the current code to get the particular selected Card ID and pass it to the respective modal button using an onClick function to denote the plan as selected using the selectPlan(plan) function. Here is the Plans UI:

enter image description here


Solution

  •   const [plans, setPlans] = useState([]);
      const [currentPlan, setCurrentPlan] = useState([]);
      const [modalOpen, setModalOpen] = useState(false);
      const [selectedPlan, setSelectedPlan] = useState();
    
      const fetchPlans = async () => {
        const { data } = await httpClient.get(
        const Plans = ({ affiliateId }) => {
                      linkClassName="billing-plans__plans-card-actions-button"
                      open={modalOpen}
                      onModalClose={() => setModalOpen(false)}
                      onModalOpen={() => {
                        setSelectedPlan(plan);
                        setModalOpen(true);
                      }}
                      header={
                        <I18nText id="pricing.plans.actions.confirmationText" />
                      }
        const Plans = ({ affiliateId }) => {
                          label={I18n.t('shared.action.confirm')}
                          key={plan.id}
                          onClick={() => {
                            selectPlan(selectedPlan);
                          }}
                        />
                        <Button