ronantakizawa/codereview-bench huggingface dataset:
Repository: chatwoot/chatwoot
File: app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue (Vue)

Code before:
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { I18nT } from 'vue-i18n';
import { useAlert } from 'dashboard/composables';
import Icon from 'next/icon/Icon.vue';
import NextButton from 'next/button/Button.vue';
import LoadingState from 'dashboard/components/widgets/LoadingState.vue';
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
import globalConstants from 'dashboard/constants/globals.js';
import {
  setupFacebookSdk,
  initWhatsAppEmbeddedSignup,
  createMessageHandler,
  isValidBusinessData,
} from './whatsapp/utils';

const store = useStore();
const router = useRouter();
const { t } = useI18n();

// State
const fbSdkLoaded = ref(false);
const isProcessing = ref(false);
const processingMessage = ref('');
const authCodeReceived = ref(false);
const authCode = ref(null);
const businessData = ref(null);
const isAuthenticating = ref(false);

Review comment (suggestion):
```suggestion
import { useI18n, I18nT } from 'vue-i18n';
```

Code after:
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { useI18n, I18nT } from 'vue-i18n';
import { useAlert } from 'dashboard/composables';
import Icon from 'next/icon/Icon.vue';
import NextButton from 'next/button/Button.vue';
import LoadingState from 'dashboard/components/widgets/LoadingState.vue';
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
import globalConstants from 'dashboard/constants/globals.js';
import {
  setupFacebookSdk,
  initWhatsAppEmbeddedSignup,
  createMessageHandler,
  isValidBusinessData,
} from './whatsapp/utils';

const store = useStore();
const router = useRouter();
const { t } = useI18n();

// State
const fbSdkLoaded = ref(false);
const isProcessing = ref(false);
const processingMessage = ref('');
const authCodeReceived = ref(false);
const authCode = ref(null);
const businessData = ref(null);
const isAuthenticating = ref(false);
Repository: chatwoot/chatwoot
File: enterprise/app/services/companies/business_email_detector_service.rb (Ruby)

Code before:
class Companies::BusinessEmailDetectorService
  # FIXME: It should be possible to do this another way,
  # check the ticket
  FREE_DOMAINS = %w[
    gmail.com yahoo.com hotmail.com outlook.com
    aol.com icloud.com mail.com protonmail.com
    live.com msn.com yandex.com zoho.com
    gmx.com inbox.com
    uol.com.br bol.com.br terra.com.br ig.com.br
    sapo.pt iol.pt
    terra.es hotmail.es yahoo.es
  ].freeze

  attr_reader :email

  def initialize(email)
    @email = email
  end

  def perform
    return false if email.blank?

    address = ValidEmail2::Address.new(email)
    return false unless address.valid?
    return false if address.disposable_domain?

    domain = extract_domain(email)
    FREE_DOMAINS.exclude?(domain)
  end

Review comment (refactor):
We should probably move this to a yaml in config folder
Also, include providers like `hey`, `tuta`, `icloud.com`, and alt domains like `proton.me` or `pm.me`

I remember using the data from https://github.com/fnando/email-provider-info/ in a separate project

Code after:
class Companies::BusinessEmailDetectorService
  attr_reader :email

  def initialize(email)
    @email = email
  end

  def perform
    return false if email.blank?

    address = ValidEmail2::Address.new(email)
    return false unless address.valid?
    return false if address.disposable_domain?

    provider = EmailProviderInfo.call(email)

    provider.nil?
  end
end
Repository: chatwoot/chatwoot
File: app/presenters/messages/search_data_presenter.rb (Ruby)

Code before:
        key: key.to_s,
        value: value.to_s,

        value_type: value.class.name.downcase
      }
    end
  end

  def search_attachments_data
    attachments.filter_map do |a|
      { transcribed_text: a.meta&.dig('transcribed_text') }
    end.presence
  end

  def search_content_attributes_data
    {
      email: {
        subject: content_attributes.dig(:email, :subject) || content_attributes.dig('email', 'subject')
      }.compact
    }.compact
  end

  def search_conversation_data
    {
      id: conversation.id,
      display_id: conversation.display_id,
      status: conversation.status,
      assignee_id: conversation.assignee_id,
      team_id: conversation.team_id,
      contact_id: conversation.contact_id,
      custom_attributes: flatten_custom_attributes(conversation.custom_attributes),
      browser: conversation.additional_attributes&.dig('browser'),
      referer: conversation.additional_attributes&.dig('referer'),
      initiated_at: conversation.additional_attributes&.dig('initiated_at')
    }
  end

  def search_inbox_data
    {
      id: inbox.id,
      name: inbox.name,
      channel_type: inbox.channel_type
    }
  end

  def search_sender_data
    return nil unless sender

    {
      id: sender.id,
      type: sender.class.name,

Review comment (suggestion):
```suggestion
      id: conversation.display_id,
```

Code after:
  end

  def attachment_data
    attachments.filter_map do |a|
      { transcribed_text: a.meta&.dig('transcribed_text') }
    end.presence
  end

  def content_attributes_data
    email_subject = content_attributes.dig(:email, :subject)
    return {} if email_subject.blank?

    { email: { subject: email_subject } }
  end

  def conversation_data
    { id: conversation.display_id }
  end

  def search_additional_data
    {
      campaign_id: additional_attributes&.dig('campaign_id'),
      automation_rule_id: content_attributes&.dig('automation_rule_id')
    }
  end
end
Repository: chatwoot/chatwoot
File: enterprise/app/controllers/api/v1/accounts/companies_controller.rb (Ruby)

Code before:
    )
    @companies = fetch_companies(companies)
    @companies_count = @companies.total_count
  end

  def show; end

  def create
    @company = Current.account.companies.build(company_params)
    @company.save!
  end

  def update
    @company.update!(company_params)
  end

  def destroy
    @company.destroy!
    head :ok
  end

  private

  def resolved_companies
    return @resolved_companies if @resolved_companies

    @resolved_companies = Current.account.companies
    @resolved_companies
  end

  def set_current_page
    @current_page = params[:page] || 1
  end

  def fetch_companies(companies)
    filtrate(companies)
      .page(@current_page)
      .per(RESULTS_PER_PAGE)
  end

  def check_authorization
    raise Pundit::NotAuthorizedError unless ChatwootApp.enterprise?

    authorize(Company)
  end

  def fetch_company
    @company = Current.account.companies.find(params[:id])
  end

  def company_params

Review comment (suggestion):
```suggestion
  def resolved_companies
    @resolved_companies ||= Current.account.companies
  end
```

Code after:
    companies = resolved_companies.search_by_name_or_domain(params[:q])
    @companies = fetch_companies(companies)
    @companies_count = @companies.total_count
  end

  def show; end

  def create
    @company = Current.account.companies.build(company_params)
    @company.save!
  end

  def update
    @company.update!(company_params)
  end

  def destroy
    @company.destroy!
    head :ok
  end

  private

  def resolved_companies
    @resolved_companies ||= Current.account.companies
  end

  def set_current_page
    @current_page = params[:page] || 1
  end

  def fetch_companies(companies)
    filtrate(companies)
      .page(@current_page)
      .per(RESULTS_PER_PAGE)
  end

  def check_authorization
    raise Pundit::NotAuthorizedError unless ChatwootApp.enterprise?

    authorize(Company)
  end

  def fetch_company
    @company = Current.account.companies.find(params[:id])
  end

  def company_params
    params.require(:company).permit(:name, :domain, :description, :avatar)
  end
end
Repository: chatwoot/chatwoot
File: app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue (Vue)

Code before:
import LinearSetupCTA from 'dashboard/components/widgets/conversation/linear/LinearSetupCTA.vue';

const props = defineProps({
  conversationId: {
    type: [Number, String],
    required: true,
  },
  inboxId: {
    type: Number,
    default: undefined,
  },
});

const {
  updateUISettings,
  isContactSidebarItemOpen,
  conversationSidebarItemsOrder,
  toggleSidebarUIState,
} = useUISettings();

const store = useStore();

const dragging = ref(false);
const conversationSidebarItems = ref([]);

const accountId = computed(() => store.getters.getCurrentAccountId);

const shopifyIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'shopify'
);

const isShopifyFeatureEnabled = computed(
  () => shopifyIntegration.value.enabled
);

const isLinearFeatureEnabled = useFunctionGetter(
  'accounts/isFeatureEnabledonAccount',
  accountId,
  'linear_integration'
);

const linearIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'linear'
);

const isLinearClientIdConfigured = computed(() => {
  return !!linearIntegration.value?.id;
});


Review comment (suggestion):
```suggestion
const accountId = useMapGetter('getCurrentAccountId');
```

Code after:
import SidebarActionsHeader from 'dashboard/components-next/SidebarActionsHeader.vue';
import LinearIssuesList from 'dashboard/components/widgets/conversation/linear/IssuesList.vue';
import LinearSetupCTA from 'dashboard/components/widgets/conversation/linear/LinearSetupCTA.vue';

const props = defineProps({
  conversationId: {
    type: [Number, String],
    required: true,
  },
  inboxId: {
    type: Number,
    default: undefined,
  },
});

const {
  updateUISettings,
  isContactSidebarItemOpen,
  conversationSidebarItemsOrder,
  toggleSidebarUIState,
} = useUISettings();

const dragging = ref(false);
const conversationSidebarItems = ref([]);

const shopifyIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'shopify'
);

const isShopifyFeatureEnabled = computed(
  () => shopifyIntegration.value.enabled
);

const { isCloudFeatureEnabled } = useAccount();

const isLinearFeatureEnabled = computed(() =>
  isCloudFeatureEnabled(FEATURE_FLAGS.LINEAR)
);

const linearIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'linear'
);

const isLinearClientIdConfigured = computed(() => {
  return !!linearIntegration.value?.id;
});

const isLinearConnected = computed(
  () => linearIntegration.value?.enabled || false
Repository: chatwoot/chatwoot
File: app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue (Vue)

Code before:
  updateUISettings,
  isContactSidebarItemOpen,
  conversationSidebarItemsOrder,
  toggleSidebarUIState,
} = useUISettings();

const store = useStore();

const dragging = ref(false);
const conversationSidebarItems = ref([]);

const accountId = computed(() => store.getters.getCurrentAccountId);

const shopifyIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'shopify'
);

const isShopifyFeatureEnabled = computed(
  () => shopifyIntegration.value.enabled
);

const isLinearFeatureEnabled = useFunctionGetter(
  'accounts/isFeatureEnabledonAccount',
  accountId,
  'linear_integration'
);

const linearIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'linear'
);

const isLinearClientIdConfigured = computed(() => {
  return !!linearIntegration.value?.id;
});

const isLinearConnected = computed(
  () => linearIntegration.value?.enabled || false
);

const currentChat = useMapGetter('getSelectedChat');
const conversationId = computed(() => props.conversationId);
const conversationMetadataGetter = useMapGetter(
  'conversationMetadata/getConversationMetadata'
);
const currentConversationMetaData = computed(() =>
  conversationMetadataGetter.value(conversationId.value)
);
const conversationAdditionalAttributes = computed(
  () => currentConversationMetaData.value.additional_attributes || {}

Review comment (suggestion):
@digiorgiu Let us use `isCloudFeatureEnabled` from `app/javascript/dashboard/composables/useAccount.js`. In that case, we won’t need to import `const accountId = computed(() => store.getters.getCurrentAccountId);`
Also, please use the `FEATURE_FLAGS.LINEAR` constant from `app/javascript/dashboard/featureFlags.js`

Code after:

const {
  updateUISettings,
  isContactSidebarItemOpen,
  conversationSidebarItemsOrder,
  toggleSidebarUIState,
} = useUISettings();

const dragging = ref(false);
const conversationSidebarItems = ref([]);

const shopifyIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'shopify'
);

const isShopifyFeatureEnabled = computed(
  () => shopifyIntegration.value.enabled
);

const { isCloudFeatureEnabled } = useAccount();

const isLinearFeatureEnabled = computed(() =>
  isCloudFeatureEnabled(FEATURE_FLAGS.LINEAR)
);

const linearIntegration = useFunctionGetter(
  'integrations/getIntegration',
  'linear'
);

const isLinearClientIdConfigured = computed(() => {
  return !!linearIntegration.value?.id;
});

const isLinearConnected = computed(
  () => linearIntegration.value?.enabled || false
);

const store = useStore();
const currentChat = useMapGetter('getSelectedChat');
const conversationId = computed(() => props.conversationId);
const conversationMetadataGetter = useMapGetter(
  'conversationMetadata/getConversationMetadata'
);
const currentConversationMetaData = computed(() =>
  conversationMetadataGetter.value(conversationId.value)
);
const conversationAdditionalAttributes = computed(
  () => currentConversationMetaData.value.additional_attributes || {}
);
Repository: chatwoot/chatwoot
File: app/javascript/dashboard/routes/dashboard/settings/attributes/Index.vue (Vue)

Code before:
    {
      key: 0,
      name: t('ATTRIBUTES_MGMT.TABS.CONVERSATION'),
    },
    {
      key: 1,
      name: t('ATTRIBUTES_MGMT.TABS.CONTACT'),
    },
  ];
});

onMounted(() => {
  store.dispatch('attributes/get');
});

const attributeModel = computed(() =>
  selectedTabIndex.value ? 'contact_attribute' : 'conversation_attribute'
);

const attributes = computed(() =>
  getters['attributes/getAttributesByModel'].value(attributeModel.value)
);

const onClickTabChange = payload => {
  selectedTabIndex.value = typeof payload === 'number' ? payload : payload?.key;
};

const handleEditAttribute = attribute => {
  selectedAttribute.value = attribute;
  showEditPopup.value = true;
};

const handleDeleteAttribute = attribute => {
  selectedAttribute.value = attribute;
  showDeletePopup.value = true;
};

const requiredAttributeKeys = computed(
  () => currentAccount.value?.settings?.conversation_required_attributes || []
);

const hasPreChatBadge = attribute => {
  return (inboxes.value || []).some(inbox => {
    const fields =
      inbox?.pre_chat_form_options?.pre_chat_fields ||
      inbox?.channel?.pre_chat_form_options?.pre_chat_fields ||
      [];
    return fields.some(field => field.name === attribute.attribute_key);
  });
};


Review comment (suggestion):
Let us use `tabChange` event from TabBar component
```
const onClickTabChange = tab => {
  selectedTabIndex.value = tab.key;
};
```

Code after:
const tabs = computed(() => {
  return [
    {
      key: 0,
      name: t('ATTRIBUTES_MGMT.TABS.CONVERSATION'),
    },
    {
      key: 1,
      name: t('ATTRIBUTES_MGMT.TABS.CONTACT'),
    },
  ];
});

const tabsForTabBar = computed(() =>
  tabs.value.map(tab => ({ label: tab.name, key: tab.key }))
);

onMounted(() => {
  store.dispatch('attributes/get');
});

const attributeModel = computed(() =>
  selectedTabIndex.value ? 'contact_attribute' : 'conversation_attribute'
);

const attributes = computed(() =>
  getters['attributes/getAttributesByModel'].value(attributeModel.value)
);

const onClickTabChange = tab => {
  selectedTabIndex.value = tab.key;
};

const handleEditAttribute = attribute => {
  selectedAttribute.value = attribute;
  toggleEditPopup(true);
};

const handleDeleteAttribute = attribute => {
  selectedAttribute.value = attribute;
  toggleDeletePopup(true);
};

const confirmDeleteAttribute = async () => {
  try {
    await store.dispatch('attributes/delete', selectedAttribute.value.id);
    useAlert(t('ATTRIBUTES_MGMT.DELETE.API.SUCCESS_MESSAGE'));
    closeDelete();
  } catch (error) {
    const errorMessage =
      error?.response?.message || t('ATTRIBUTES_MGMT.DELETE.API.ERROR_MESSAGE');
Repository: chatwoot/chatwoot
File: app/models/concerns/account_email_rate_limitable.rb (Ruby)

Code before:
module AccountEmailRateLimitable
  extend ActiveSupport::Concern

  OUTBOUND_EMAIL_TTL = 25.hours.to_i

  def email_rate_limit
    return self[:limits]['emails'].to_i if self[:limits].present? && self[:limits]['emails'].present?

    config_name = 'ACCOUNT_EMAILS_LIMIT'
    global_limit = GlobalConfig.get(config_name)[config_name]
    return global_limit.to_i if global_limit.present?

    ChatwootApp.max_limit.to_i
  end

  def emails_sent_today
    key = format(Redis::Alfred::ACCOUNT_OUTBOUND_EMAIL_COUNT_KEY, account_id: id, date: Time.zone.today.to_s)
    Redis::Alfred.get(key).to_i
  end

  def within_email_rate_limit?
    emails_sent_today < email_rate_limit
  end

  def increment_email_sent_count
    key = format(Redis::Alfred::ACCOUNT_OUTBOUND_EMAIL_COUNT_KEY, account_id: id, date: Time.zone.today.to_s)

Review comment (performance):
Can we make it a bit more readable? something like this?

```rb
module AccountEmailRateLimitable
  extend ActiveSupport::Concern

  OUTBOUND_EMAIL_TTL = 25.hours.to_i
  EMAIL_LIMIT_CONFIG_KEY = 'ACCOUNT_EMAILS_LIMIT'

  def email_rate_limit
    account_limit || global_limit || default_limit
  end

  def emails_sent_today
    Redis::Alfred.get(email_count_cache_key).to_i
  end

  def within_email_rate_limit?
    emails_sent_today < email_rate_limit
  end

  def increment_email_sent_count
    Redis::Alfred.incr(email_count_cache_key).tap do |count|
      Redis::Alfred.expire(email_count_cache_key, OUTBOUND_EMAIL_TTL) if count == 1
    end
  end

  private

  def email_count_cache_key
    @email_count_cache_key ||= format(
      Redis::Alfred::ACCOUNT_OUTBOUND_EMAIL_COUNT_KEY,
      account_id: id,
      date: Time.zone.today.to_s
    )
  end

  def account_limit
    self[:limits]&.dig('emails')&.to_i
  end

  def global_limit
    GlobalConfig.get(EMAIL_LIMIT_CONFIG_KEY)[EMAIL_LIMIT_CONFIG_KEY]&.to_i
  end

  def default_limit
    ChatwootApp.max_limit.to_i
  end
end
```

Code after:
module AccountEmailRateLimitable
  extend ActiveSupport::Concern

  OUTBOUND_EMAIL_TTL = 25.hours.to_i
  EMAIL_LIMIT_CONFIG_KEY = 'ACCOUNT_EMAILS_LIMIT'.freeze

  def email_rate_limit
    account_limit || global_limit || default_limit
  end

  def emails_sent_today
    Redis::Alfred.get(email_count_cache_key).to_i
  end

  def within_email_rate_limit?
    return true if emails_sent_today < email_rate_limit

    Rails.logger.warn("Account #{id} reached daily email rate limit of #{email_rate_limit}. Sent: #{emails_sent_today}")
    false
  end

  def increment_email_sent_count
    Redis::Alfred.incr(email_count_cache_key).tap do |count|
      Redis::Alfred.expire(email_count_cache_key, OUTBOUND_EMAIL_TTL) if count == 1
    end
  end
Repository: chatwoot/chatwoot
File: app/presenters/mail_presenter.rb (Ruby)

Code before:
  def in_reply_to
    return if @mail.in_reply_to.blank?

    # Although the "in_reply_to" field in the email can potentially hold multiple values,
    # our current system does not have the capability to handle this.
    # FIX ME: Address this issue by returning the complete results and utilizing them for querying conversations.
    @mail.in_reply_to.is_a?(Array) ? @mail.in_reply_to.first : @mail.in_reply_to
  end

  def references
    return [] if @mail.references.blank?

    Array.wrap(@mail.references)
  end

  def from
    # changing to downcase to avoid case mismatch while finding contact
    Array.wrap(@mail.reply_to.presence || @mail.from).map(&:downcase)
  end

  def sender_name
    parse_mail_address((@mail[:reply_to] || @mail[:from]).value)&.name
  end

  def original_sender
    parse_mail_address(@mail[:reply_to].try(:value))&.address ||
      @mail['X-Original-Sender'].try(:value) ||
      parse_mail_address(@mail[:from].try(:value))&.address
  end

  def headers_data
    headers = {
      'x-original-from' => @mail['X-Original-From']&.value,
      'x-original-sender' => @mail['X-Original-Sender']&.value,
      'x-forwarded-for' => @mail['X-Forwarded-For']&.value
    }.compact

    headers.presence
  end

  def email_forwarded_for
    @mail['X-Forwarded-For'].try(:value)
  end

  def mail_receiver
    if @mail.to.blank?
      return [email_forwarded_for] if email_forwarded_for.present?

      []
    else
      @mail.to

Review comment (suggestion):
from -> `Array.wrap(@mail.reply_to.presence || @mail.from).map(&:downcase)`  .. since we are checking reply_to already here ..  changing to '@mail[:from] check from from.first

Code after:
  def in_reply_to
    return if @mail.in_reply_to.blank?

    # Although the "in_reply_to" field in the email can potentially hold multiple values,
    # our current system does not have the capability to handle this.
    # FIX ME: Address this issue by returning the complete results and utilizing them for querying conversations.
    @mail.in_reply_to.is_a?(Array) ? @mail.in_reply_to.first : @mail.in_reply_to
  end

  def references
    return [] if @mail.references.blank?

    Array.wrap(@mail.references)
  end

  def from
    # changing to downcase to avoid case mismatch while finding contact
    Array.wrap(@mail.reply_to.presence || @mail.from).map(&:downcase)
  end

  def sender_name
    parse_mail_address((@mail[:reply_to] || @mail[:from]).value)&.name
  end

  def original_sender
    [
      @mail[:reply_to]&.value,
      @mail['X-Original-Sender']&.value,
      @mail[:from]&.value
    ].filter_map { |email| parse_mail_address(email)&.address }.first
  end

  def headers_data
    headers = {
      'x-original-from' => @mail['X-Original-From']&.value,
      'x-original-sender' => @mail['X-Original-Sender']&.value,
      'x-forwarded-for' => @mail['X-Forwarded-For']&.value
    }.compact

    headers.presence
  end

  def email_forwarded_for
    @mail['X-Forwarded-For'].try(:value)
  end

  def mail_receiver
    if @mail.to.blank?
      return [email_forwarded_for] if email_forwarded_for.present?

      []