<commit_before>import { Autocomplete, Box, Typography } from '@mui/material';
import { createFilterOptions } from '@mui/material/Autocomplete';
import { Controller, useForm } from 'react-hook-form';

import CourseAPI from 'api/course';
import { AchievementMiniEntity } from 'types/course/achievements';
import { AchievementConditionData } from 'types/course/conditions';
import TextField from 'lib/components/TextField';
import { getAchievementId } from 'lib/helpers/url-helpers';
import LoadingIndicator from 'lib/components/LoadingIndicator';
import Prompt from 'lib/components/Prompt';
import Preload from 'lib/components/Preload';
import useTranslation from 'lib/hooks/useTranslation';
import { AnyConditionProps } from '../AnyCondition';
import { formatErrorMessage } from '../../form/fields/utils/mapError';
import translations from '../translations';

type AchievementOptions = Record<
  AchievementMiniEntity['id'],
  {
    title: AchievementMiniEntity['title'];
    description: AchievementMiniEntity['description'];
    badge: AchievementMiniEntity['badge'];
  }
>;

const AchievementConditionForm = (
  props: AnyConditionProps<AchievementConditionData> & {
    achievements: AchievementOptions;
  },
): JSX.Element => {
  const { achievements } = props;
  const autocompleteOptions = Object.keys(achievements);

  const { t } = useTranslation();

  const { control, handleSubmit, setError, formState } = useForm({
    defaultValues: props.condition ?? {
      achievementId: parseInt(autocompleteOptions[0], 10),
    },
  });

  const updateAchievement = (data: AchievementConditionData): void => {
    props.onUpdate(data, (errors) =>
      setError('achievementId', {
        message: errors.errors.achievement,
      }),
    );
  };

  const isNewCondition = !props.condition;

  return (
    <Prompt
      open={props.open}
      onClose={props.onClose}
      title={t(translations.chooseAnAchievement)}
      onClickPrimary={handleSubmit(updateAchievement)}
      primaryLabel={
        isNewCondition
          ? t(translations.createCondition)
          : t(translations.updateCondition)
      }
      primaryDisabled={!isNewCondition && !formState.isDirty}
    >
      <Controller
        name="achievementId"
        control={control}
        render={({ field, fieldState: { error } }): JSX.Element => (
          <Autocomplete
            {...field}
            value={field.value?.toString()}
            onChange={(_, value): void => field.onChange(parseInt(value, 10))}
            disableClearable
            options={autocompleteOptions}
            fullWidth
            getOptionLabel={(id): string =>
              achievements[parseInt(id, 10)].title ?? ''
            }
            filterOptions={createFilterOptions({
              stringify: (option) => {
                const achievement = achievements[parseInt(option, 10)];
                return `${achievement.title} ${achievement.description}`;
              },
            })}
            renderOption={(optionProps, option): JSX.Element => {
              const achievement = achievements[parseInt(option, 10)];

              return (
                <Box
                  component="li"
                  {...optionProps}
                  className={`${optionProps.className} space-x-8`}
                >
                  <img
                    src={achievement.badge.url}
                    alt={achievement.badge.name}
                    className="max-h-20 w-20"
                  />

                  <div>
                    <Typography variant="body1">{achievement.title}</Typography>

                    <Typography
                      variant="body2"
                      color="text.secondary"
                      className="line-clamp-3"
                      dangerouslySetInnerHTML={{
                        __html: achievement.description,
                      }}
                    />
                  </div>
                </Box>
              );
            }}
            renderInput={(inputProps): JSX.Element => (
              <TextField
                {...inputProps}
                variant="filled"
                label={t(translations.achievement)}
                error={Boolean(error)}
                helperText={error && formatErrorMessage(error.message)}
              />
            )}
          />
        )}
      />
    </Prompt>
  );
};

const fetchAchievements =
  (exclude: (id: number) => boolean): (() => Promise<AchievementOptions>) =>
  async () => {
    const response = await CourseAPI.achievements.index();
    const fetchedAchievements = response.data.achievements;
    return fetchedAchievements.reduce<AchievementOptions>(
      (options, achievement) => {
        if (!exclude(achievement.id))
          options[achievement.id] = {
            title: achievement.title,
            description: achievement.description,
            badge: achievement.badge,
          };
        return options;
      },
      {},<commit_msg>fix(achievementcondition): mui renderoption missing built-in key<commit_after>import { Autocomplete, Box, Typography } from '@mui/material';
import { createFilterOptions } from '@mui/material/Autocomplete';
import { Controller, useForm } from 'react-hook-form';

import CourseAPI from 'api/course';
import { AchievementMiniEntity } from 'types/course/achievements';
import { AchievementConditionData } from 'types/course/conditions';
import TextField from 'lib/components/TextField';
import { getAchievementId } from 'lib/helpers/url-helpers';
import LoadingIndicator from 'lib/components/LoadingIndicator';
import Prompt from 'lib/components/Prompt';
import Preload from 'lib/components/Preload';
import useTranslation from 'lib/hooks/useTranslation';
import { AnyConditionProps } from '../AnyCondition';
import { formatErrorMessage } from '../../form/fields/utils/mapError';
import translations from '../translations';

type AchievementOptions = Record<
  AchievementMiniEntity['id'],
  {
    id: AchievementMiniEntity['id'];
    title: AchievementMiniEntity['title'];
    description: AchievementMiniEntity['description'];
    badge: AchievementMiniEntity['badge'];
  }
>;

const AchievementConditionForm = (
  props: AnyConditionProps<AchievementConditionData> & {
    achievements: AchievementOptions;
  },
): JSX.Element => {
  const { achievements } = props;
  const autocompleteOptions = Object.keys(achievements);

  const { t } = useTranslation();

  const { control, handleSubmit, setError, formState } = useForm({
    defaultValues: props.condition ?? {
      achievementId: parseInt(autocompleteOptions[0], 10),
    },
  });

  const updateAchievement = (data: AchievementConditionData): void => {
    props.onUpdate(data, (errors) =>
      setError('achievementId', {
        message: errors.errors.achievement,
      }),
    );
  };

  const isNewCondition = !props.condition;

  return (
    <Prompt
      open={props.open}
      onClose={props.onClose}
      title={t(translations.chooseAnAchievement)}
      onClickPrimary={handleSubmit(updateAchievement)}
      primaryLabel={
        isNewCondition
          ? t(translations.createCondition)
          : t(translations.updateCondition)
      }
      primaryDisabled={!isNewCondition && !formState.isDirty}
    >
      <Controller
        name="achievementId"
        control={control}
        render={({ field, fieldState: { error } }): JSX.Element => (
          <Autocomplete
            {...field}
            value={field.value?.toString()}
            onChange={(_, value): void => field.onChange(parseInt(value, 10))}
            disableClearable
            options={autocompleteOptions}
            fullWidth
            getOptionLabel={(id): string =>
              achievements[parseInt(id, 10)].title ?? ''
            }
            filterOptions={createFilterOptions({
              stringify: (option) => {
                const achievement = achievements[parseInt(option, 10)];
                return `${achievement.title} ${achievement.description}`;
              },
            })}
            renderOption={(optionProps, option): JSX.Element => {
              const achievement = achievements[parseInt(option, 10)];

              return (
                <Box
                  component="li"
                  {...optionProps}
                  className={`${optionProps.className} space-x-8`}
                  key={achievement.id}
                >
                  <img
                    src={achievement.badge.url}
                    alt={achievement.badge.name}
                    className="max-h-20 w-20"
                  />

                  <div>
                    <Typography variant="body1">{achievement.title}</Typography>

                    <Typography
                      variant="body2"
                      color="text.secondary"
                      className="line-clamp-3"
                      dangerouslySetInnerHTML={{
                        __html: achievement.description,
                      }}
                    />
                  </div>
                </Box>
              );
            }}
            renderInput={(inputProps): JSX.Element => (
              <TextField
                {...inputProps}
                variant="filled"
                label={t(translations.achievement)}
                error={Boolean(error)}
                helperText={error && formatErrorMessage(error.message)}
              />
            )}
          />
        )}
      />
    </Prompt>
  );
};

const fetchAchievements =
  (exclude: (id: number) => boolean): (() => Promise<AchievementOptions>) =>
  async () => {
    const response = await CourseAPI.achievements.index();
    const fetchedAchievements = response.data.achievements;
    return fetchedAchievements.reduce<AchievementOptions>(
      (options, achievement) => {
        if (!exclude(achievement.id))
          options[achievement.id] = {
            id: achievement.id,
            title: achievement.title,
            description: achievement.description,
            badge: achievement.badge,
          };
        return options;
      },
      {},
{'fraction_non_alphanumeric': 0.1209939848141209, 'fraction_numerical': 0.003747164973868455, 'mean_word_length': 2.097739767868051, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 65, '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': '5529108', 'n_tokens_mistral': 2714, 'n_tokens_neox': 2605, 'n_words': 466}