ronantakizawa/codereview-bench huggingface dataset:
Repository: freeCodeCamp/freeCodeCamp
File: tools/ui-components/src/index.ts (TypeScript)

Code before:
// Use this file as the entry point for component export
export { Alert, type AlertProps } from './alert';
export { Button } from './button';
export { CloseButton } from './close-button';
export { Image } from './image';
export { Table } from './table';
export { Panel } from './panel';
// export { ToggleButton } from './toggle-button';
export { Dropdown } from './drop-down';
export { MenuItem } from './drop-down/menu-item';
export { Container } from './container';
export { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs';
export { Col } from './col';
export { ControlLabel } from './control-label';
export { FormGroup, type FormGroupProps } from './form-group';
export { FormControl } from './form-control';
export { HelpBlock } from './help-block';
export { Row } from './row';


Review comment (suggestion):
```suggestion
// export { Button } from './button';
```
This should not matter, but we ended up with some unpredictable outcomes. I would prefer adding this during the component swap.

Code after:
// Use this file as the entry point for component export
export { Alert, type AlertProps } from './alert';
// export { Button } from './button';
export { CloseButton } from './close-button';
export { Image } from './image';
export { Table } from './table';
export { Panel } from './panel';
// export { ToggleButton } from './toggle-button';
export { Dropdown } from './drop-down';
export { MenuItem } from './drop-down/menu-item';
export { Container } from './container';
export { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs';
export { Col } from './col';
export { ControlLabel } from './control-label';
export { FormGroup, type FormGroupProps } from './form-group';
export { FormControl } from './form-control';
export { HelpBlock } from './help-block';
export { Row } from './row';
Repository: freeCodeCamp/freeCodeCamp
File: client/src/pages/email-sign-up.tsx (TypeScript)

Code before:
      completedChallengeCount
    }: { acceptedPrivacyTerms: boolean; completedChallengeCount: number },
    isSignedIn: boolean,
    showLoading: boolean
  ) => ({
    acceptedPrivacyTerms,
    isSignedIn,
    showLoading,
    completedChallengeCount
  })
);
const mapDispatchToProps = (dispatch: Dispatch) =>
  bindActionCreators({ acceptTerms }, dispatch);
const RedirectToLearn = createRedirect('/learn');

function EmailListOptIn({
  isSignedIn,
  acceptTerms
}: {
  isSignedIn: boolean;
  acceptTerms: (accepted: boolean) => void;
}) {
  const { t } = useTranslation();
  if (isSignedIn) {
    return (
      <Row className='email-list-opt'>
        <Col md={4} mdOffset={2} sm={5} smOffset={1} xs={12}>
          <Button
            block={true}
            bsSize='lg'
            bsStyle='primary'
            className='big-cta-btn'
            onClick={() => acceptTerms(true)}
          >
            {t('buttons.yes-please')}
          </Button>
          <Spacer size='small' />
        </Col>
        <Col md={4} sm={5} xs={12}>
          <Button
            block={true}
            bsSize='lg'
            bsStyle='primary'
            className='big-cta-btn'
            onClick={() => acceptTerms(false)}
          >
            {t('buttons.no-thanks')}
          </Button>
          <Spacer size='small' />
        </Col>
      </Row>

Review comment (suggestion):
We actually want avoid adding CSS overrides to the code. I think the fix should be made to the `Col` component instead, but I'll need to test this a bit before I suggest the change.

Code after:
      completedChallengeCount
    }: { acceptedPrivacyTerms: boolean; completedChallengeCount: number },
    isSignedIn: boolean,
    showLoading: boolean
  ) => ({
    acceptedPrivacyTerms,
    isSignedIn,
    showLoading,
    completedChallengeCount
  })
);
const mapDispatchToProps = (dispatch: Dispatch) =>
  bindActionCreators({ acceptTerms }, dispatch);
const RedirectToLearn = createRedirect('/learn');

function EmailListOptIn({
  isSignedIn,
  acceptTerms
}: {
  isSignedIn: boolean;
  acceptTerms: (accepted: boolean) => void;
}) {
  const { t } = useTranslation();
  if (isSignedIn) {
    return (
      <Container>
        <Row className='email-list-opt'>
          <Col md={4} mdOffset={2} sm={5} smOffset={1} xs={12}>
            <Button
              block={true}
              bsSize='lg'
              bsStyle='primary'
              className='big-cta-btn'
              onClick={() => acceptTerms(true)}
            >
              {t('buttons.yes-please')}
            </Button>
            <Spacer size='small' />
          </Col>
          <Col md={4} sm={5} xs={12}>
            <Button
              block={true}
              bsSize='lg'
              bsStyle='primary'
              className='big-cta-btn'
              onClick={() => acceptTerms(false)}
            >
              {t('buttons.no-thanks')}
            </Button>
            <Spacer size='small' />
          </Col>
Repository: freeCodeCamp/freeCodeCamp
File: client/src/components/SolutionViewer/project-modal.tsx (TypeScript)

Code before:
  projectTitle,
  challengeFiles,
  solution,
  handleSolutionModalHide
}: ProjectModalProps): JSX.Element => {
  const { t } = useTranslation();
  return (
    <Modal
      data-playwright-test-label='project-solution-viewer-modal'
      aria-labelledby='solution-viewer-modal-title'
      bsSize='large'
      onHide={handleSolutionModalHide}
      show={isOpen}
      size='lg'
    >
      <Modal.Header closeButton={true}>
        <Modal.Title id='solution-viewer-modal-title'>
          {t('settings.labels.solution-for', { projectTitle })}
        </Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <SolutionViewer challengeFiles={challengeFiles} solution={solution} />
      </Modal.Body>
      <Modal.Footer>
        <Button
          data-playwright-test-label='close-project-solution-viewer'
          onClick={handleSolutionModalHide}
        >
          {t('buttons.close')}
        </Button>
      </Modal.Footer>
    </Modal>
  );
};

ProjectModal.displayName = 'ProjectModal';

export default ProjectModal;


Review comment (suggestion):
We should use `getByRole()` to query the element rather than adding test ID to the DOM.

Ref: https://contribute.freecodecamp.org/#/how-to-add-playwright-tests?id=-identifying-a-dom-element.

Code after:
  projectTitle,
  challengeFiles,
  solution,
  handleSolutionModalHide
}: ProjectModalProps): JSX.Element => {
  const { t } = useTranslation();
  return (
    <Modal
      data-playwright-test-label='project-solution-viewer-modal'
      aria-labelledby='solution-viewer-modal-title'
      bsSize='large'
      onHide={handleSolutionModalHide}
      show={isOpen}
      size='lg'
    >
      <Modal.Header closeButton={true}>
        <Modal.Title id='solution-viewer-modal-title'>
          {t('settings.labels.solution-for', { projectTitle })}
        </Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <SolutionViewer challengeFiles={challengeFiles} solution={solution} />
      </Modal.Body>
      <Modal.Footer>
        <Button onClick={handleSolutionModalHide}>{t('buttons.close')}</Button>
      </Modal.Footer>
    </Modal>
  );
};

ProjectModal.displayName = 'ProjectModal';

export default ProjectModal;
Repository: freeCodeCamp/freeCodeCamp
File: api-server/src/server/utils/donation.js (JavaScript)

Code before:
  let donation = {
    email,
    amount,
    duration,
    provider: 'stripe',
    subscriptionId,
    customerId,
    startDate: new Date().toISOString()
  };
  await createAsyncUserDonation(user, donation);
  return res.status(200).json({ isDonating: true });
}

export async function handleStripeCardUpdateSession(req, app, stripe) {
  const {
    user: { id }
  } = req;

  const { Donation } = app.models;
  log('Updating stripe card for user: ', ObjectId(id));

  /* eslint-disable camelcase */
  try {
    // multiple donations support should be added
    const donation = await Donation.findOne({
      where: { userId: ObjectId(id) }
    });

    if (!donation) throw Error();

    const { customerId, subscriptionId } = donation;

    // updating customer payment method is handled by webhook handler
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      mode: 'setup',
      customer: customerId,
      setup_intent_data: {
        metadata: {
          customer_id: customerId,
          subscription_id: subscriptionId
        }
      },
      success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
    });
    // /* eslint-enable camelcase */
    return { session_id: session.id };
  } catch (err) {
    throw Error(err);
  }

Review comment (suggestion):
```suggestion
      where: { userId: id }
```
Also here, it should be okay to use `id`.

Code after:
    email,
    amount,
    duration,
    provider: 'stripe',
    subscriptionId,
    customerId,
    startDate: new Date().toISOString()
  };
  await createAsyncUserDonation(user, donation);
  return res.status(200).json({ isDonating: true });
}

export async function handleStripeCardUpdateSession(req, app, stripe) {
  const {
    user: { id }
  } = req;

  const { Donation } = app.models;
  log('Updating stripe card for user: ', id);

  // multiple donations support should be added
  const donation = await Donation.findOne({
    where: { userId: id, provider: 'stripe' }
  });

  if (!donation) throw Error('Stripe donation record not found');

  const { customerId, subscriptionId } = donation;

  log(subscriptionId);

  // Create a Stripe checkout session
  // updating customer payment method is handled by webhook handler
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    mode: 'setup',
    customer: customerId,
    setup_intent_data: {
      metadata: {
        customer_id: customerId,
        subscription_id: subscriptionId
      }
    },
    success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
  });
  return { sessionId: session.id };
}
Repository: freeCodeCamp/freeCodeCamp
File: api-server/src/server/utils/donation.js (JavaScript)

Code before:
    duration,
    provider: 'stripe',
    subscriptionId,
    customerId,
    startDate: new Date().toISOString()
  };
  await createAsyncUserDonation(user, donation);
  return res.status(200).json({ isDonating: true });
}

export async function handleStripeCardUpdateSession(req, app, stripe) {
  const {
    user: { id }
  } = req;

  const { Donation } = app.models;
  log('Updating stripe card for user: ', ObjectId(id));

  /* eslint-disable camelcase */
  try {
    // multiple donations support should be added
    const donation = await Donation.findOne({
      where: { userId: ObjectId(id) }
    });

    if (!donation) throw Error();

    const { customerId, subscriptionId } = donation;

    // updating customer payment method is handled by webhook handler
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      mode: 'setup',
      customer: customerId,
      setup_intent_data: {
        metadata: {
          customer_id: customerId,
          subscription_id: subscriptionId
        }
      },
      success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
    });
    // /* eslint-enable camelcase */
    return { session_id: session.id };
  } catch (err) {
    throw Error(err);
  }
}


Review comment (suggestion):
```suggestion
    if (!donation) throw Error('Donation record not found');
```
Something like that, so the logs are little clearer.

Code after:
    provider: 'stripe',
    subscriptionId,
    customerId,
    startDate: new Date().toISOString()
  };
  await createAsyncUserDonation(user, donation);
  return res.status(200).json({ isDonating: true });
}

export async function handleStripeCardUpdateSession(req, app, stripe) {
  const {
    user: { id }
  } = req;

  const { Donation } = app.models;
  log('Updating stripe card for user: ', id);

  // multiple donations support should be added
  const donation = await Donation.findOne({
    where: { userId: id, provider: 'stripe' }
  });

  if (!donation) throw Error('Stripe donation record not found');

  const { customerId, subscriptionId } = donation;

  log(subscriptionId);

  // Create a Stripe checkout session
  // updating customer payment method is handled by webhook handler
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    mode: 'setup',
    customer: customerId,
    setup_intent_data: {
      metadata: {
        customer_id: customerId,
        subscription_id: subscriptionId
      }
    },
    success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
  });
  return { sessionId: session.id };
}
Repository: freeCodeCamp/freeCodeCamp
File: api-server/src/server/utils/donation.js (JavaScript)

Code before:
  try {
    // multiple donations support should be added
    const donation = await Donation.findOne({
      where: { userId: ObjectId(id) }
    });

    if (!donation) throw Error();

    const { customerId, subscriptionId } = donation;

    // updating customer payment method is handled by webhook handler
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      mode: 'setup',
      customer: customerId,
      setup_intent_data: {
        metadata: {
          customer_id: customerId,
          subscription_id: subscriptionId
        }
      },
      success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
    });
    // /* eslint-enable camelcase */
    return { session_id: session.id };
  } catch (err) {
    throw Error(err);
  }
}


Review comment (suggestion):
```suggestion
    return { sessionId: session.id };
```
Our client side code is cleaner if we send `sessionId`, so we might as well.

Code after:
    where: { userId: id, provider: 'stripe' }
  });

  if (!donation) throw Error('Stripe donation record not found');

  const { customerId, subscriptionId } = donation;

  log(subscriptionId);

  // Create a Stripe checkout session
  // updating customer payment method is handled by webhook handler
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    mode: 'setup',
    customer: customerId,
    setup_intent_data: {
      metadata: {
        customer_id: customerId,
        subscription_id: subscriptionId
      }
    },
    success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
  });
  return { sessionId: session.id };
}
Repository: freeCodeCamp/freeCodeCamp
File: client/src/components/landing/components/big-call-to-action.tsx (TypeScript)

Code before:
import React from 'react';
import { useTranslation } from 'react-i18next';
import Login from '../../Header/components/login';

const BigCallToAction = ({ text }: { text: string }): JSX.Element => {
  const { t } = useTranslation();

  return (
    <Login
      block={true}
      data-test-label='landing-big-cta'
      data-playwright-test-label='landing-big-cta'
    >
      {text ? text : t('buttons.logged-in-cta-btn')}
    </Login>
  );
};

BigCallToAction.displayName = 'BigCallToAction';
export default BigCallToAction;


Review comment (suggestion):
```suggestion
const BigCallToAction = ({ text }: { text?: string }): JSX.Element => {
```

Code after:
import React from 'react';
import { useTranslation } from 'react-i18next';
import Login from '../../Header/components/login';

const BigCallToAction = ({ text }: { text?: string }): JSX.Element => {
  const { t } = useTranslation();

  return (
    <Login
      block={true}
      data-test-label='landing-big-cta'
      data-playwright-test-label='landing-big-cta'
    >
      {text ? text : t('buttons.logged-in-cta-btn')}
    </Login>
  );
};

BigCallToAction.displayName = 'BigCallToAction';
export default BigCallToAction;
Repository: freeCodeCamp/freeCodeCamp
File: client/src/pages/update-stripe-card.tsx (TypeScript)

Code before:
      );
    } else
      return (
        <>
          <Spacer size='medium' />
          <Button block={true} onClick={handleClick}>
            {t('buttons.update-card')}
          </Button>
        </>
      );
  } else
    return (
      <>
        <h1 className='text-center'>{t('learn.sign-in-card-update')}</h1>
        <Spacer size='medium' />
        <BigCallToAction text={t('buttons.sign-in')} />
      </>
    );
}

function UpdateStripeCard({
  t,
  isSignedIn,
  isDonating,
  updateCardState,
  updateCard,
  updateCardComplete
}: UpdateStripeCardProps) {
  function handleClick(event?: FormEvent) {
    updateCard();
    if (event) event.preventDefault();
  }
  const location = useLocation();
  const searchParams = new URLSearchParams(location.search);
  const isUpdateSuccessful = searchParams.get('session_id') !== null;

  useEffect(() => {
    if (isUpdateSuccessful) {
      updateCardComplete();
    }
  }, [isUpdateSuccessful, updateCardComplete]);

  return (
    <>
      <Helmet>
        <title>{t('misc.update-your-card')} | freeCodeCamp.org</title>
      </Helmet>
      <Container className='page-wrapper-80'>
        <Row>
          <Col sm={6} smOffset={3}>
            <Spacer size='large' />

Review comment (suggestion):
```suggestion
  const { t } = useTranslation();
  function handleClick(event?: FormEvent) {
```
This is all we need, instead of the props.

Code after:
      return (
        <>
          <Spacer size='medium' />
          <Button block={true} onClick={handleClick}>
            {t('buttons.update-card')}
          </Button>
        </>
      );
  } else
    return (
      <>
        <h1 className='text-center'>{t('learn.sign-in-card-update')}</h1>
        <Spacer size='medium' />
        <BigCallToAction text={t('buttons.sign-in')} />
      </>
    );
}

function UpdateStripeCard({
  isSignedIn,
  isDonating,
  updateCardState,
  updateCard,
  updateCardComplete
}: UpdateStripeCardProps) {
  function handleClick(event?: FormEvent) {
    updateCard();
    if (event) event.preventDefault();
  }
  const { t } = useTranslation();
  const location = useLocation();
  const searchParams = new URLSearchParams(location.search);
  const isUpdateSuccessful = searchParams.get('session_id') !== null;

  useEffect(() => {
    if (isUpdateSuccessful) {
      updateCardComplete();
    }
  }, [isUpdateSuccessful, updateCardComplete]);

  return (
    <>
      <Helmet>
        <title>{t('misc.update-your-card')} | freeCodeCamp.org</title>
      </Helmet>
      <Container className='page-wrapper-80'>
        <Row>
          <Col sm={6} smOffset={3}>
            <Spacer size='large' />
            <ConditionalContent
              isSignedIn={isSignedIn}
Repository: freeCodeCamp/freeCodeCamp
File: client/src/redux/donation-saga.js (JavaScript)

Code before:
        throw error;
      });
  } else {
    throw error;
  }
}

export function* setDonationCookie() {
  if (document?.cookie) {
    const isDonating = yield select(isDonatingSelector);
    const isDonorCookieSet = document.cookie
      .split(';')
      .some(item => item.trim().startsWith('isDonor=true'));
    if (isDonating) {
      if (!isDonorCookieSet) {
        document.cookie = 'isDonor=true';
      }
    }
  }
}

export function* updateCardSaga() {
  yield put(updateCardRedirecting());
  try {
    const {
      data: { session_id }
    } = yield call(updateStripeCard);

    if (!session_id) throw new Error('No session_id');
    const stripe = yield call(loadStripe, stripePublicKey);
    stripe.redirectToCheckout({
      sessionId: session_id
    });
  } catch (error) {
    console.log('error');
    yield put(updateCardError(updateCardErrorMessage));
  }
}

export function createDonationSaga(types) {
  return [
    takeEvery(types.tryToShowDonationModal, showDonateModalSaga),
    takeLeading(types.postCharge, postChargeSaga),
    takeEvery(types.fetchUserComplete, setDonationCookie),
    takeLeading(types.updateCard, updateCardSaga)
  ];
}


Review comment (suggestion):
```suggestion
      data: { sessionId }
```
If we update the api to return this

Code after:
        throw error;
      });
  } else {
    throw error;
  }
}

export function* setDonationCookie() {
  if (document?.cookie) {
    const isDonating = yield select(isDonatingSelector);
    const isDonorCookieSet = document.cookie
      .split(';')
      .some(item => item.trim().startsWith('isDonor=true'));
    if (isDonating) {
      if (!isDonorCookieSet) {
        document.cookie = 'isDonor=true';
      }
    }
  }
}

export function* updateCardSaga() {
  yield put(updateCardRedirecting());
  try {
    const {
      data: { sessionId }
    } = yield call(updateStripeCard);

    if (!sessionId) throw new Error('No sessionId');
    const stripe = yield call(loadStripe, stripePublicKey);
    stripe.redirectToCheckout({ sessionId });
  } catch (error) {
    yield put(updateCardError(updateCardErrorMessage));
  }
}

export function createDonationSaga(types) {
  return [
    takeEvery(types.tryToShowDonationModal, showDonateModalSaga),
    takeLeading(types.postCharge, postChargeSaga),
    takeEvery(types.fetchUserComplete, setDonationCookie),
    takeLeading(types.updateCard, updateCardSaga)
  ];
}
Repository: freeCodeCamp/freeCodeCamp
File: client/src/redux/donation-saga.js (JavaScript)

Code before:
    throw error;
  }
}

export function* setDonationCookie() {
  if (document?.cookie) {
    const isDonating = yield select(isDonatingSelector);
    const isDonorCookieSet = document.cookie
      .split(';')
      .some(item => item.trim().startsWith('isDonor=true'));
    if (isDonating) {
      if (!isDonorCookieSet) {
        document.cookie = 'isDonor=true';
      }
    }
  }
}

export function* updateCardSaga() {
  yield put(updateCardRedirecting());
  try {
    const {
      data: { session_id }
    } = yield call(updateStripeCard);

    if (!session_id) throw new Error('No session_id');
    const stripe = yield call(loadStripe, stripePublicKey);
    stripe.redirectToCheckout({
      sessionId: session_id
    });
  } catch (error) {
    console.log('error');
    yield put(updateCardError(updateCardErrorMessage));
  }
}

export function createDonationSaga(types) {
  return [
    takeEvery(types.tryToShowDonationModal, showDonateModalSaga),
    takeLeading(types.postCharge, postChargeSaga),
    takeEvery(types.fetchUserComplete, setDonationCookie),
    takeLeading(types.updateCard, updateCardSaga)
  ];
}


Review comment (suggestion):
```suggestion
    if (!sessionId) throw new Error('No sessionId');
```

Code after:
    throw error;
  }
}

export function* setDonationCookie() {
  if (document?.cookie) {
    const isDonating = yield select(isDonatingSelector);
    const isDonorCookieSet = document.cookie
      .split(';')
      .some(item => item.trim().startsWith('isDonor=true'));
    if (isDonating) {
      if (!isDonorCookieSet) {
        document.cookie = 'isDonor=true';
      }
    }
  }
}

export function* updateCardSaga() {
  yield put(updateCardRedirecting());
  try {
    const {
      data: { sessionId }
    } = yield call(updateStripeCard);

    if (!sessionId) throw new Error('No sessionId');
    const stripe = yield call(loadStripe, stripePublicKey);
    stripe.redirectToCheckout({ sessionId });
  } catch (error) {
    yield put(updateCardError(updateCardErrorMessage));
  }
}

export function createDonationSaga(types) {
  return [
    takeEvery(types.tryToShowDonationModal, showDonateModalSaga),
    takeLeading(types.postCharge, postChargeSaga),
    takeEvery(types.fetchUserComplete, setDonationCookie),
    takeLeading(types.updateCard, updateCardSaga)
  ];
}