ronantakizawa/codereview-bench huggingface dataset:
Repository: answerdev/answer
File: ui/src/pages/Admin/Write/index.tsx (TypeScript)

Code before:
        type: 'boolean',
        title: t('restrict_answer.title'),
        description: t('restrict_answer.text'),
      },
      recommend_tags: {
        type: 'string',
        title: t('recommend_tags.label'),
        description: t('recommend_tags.text'),
      },
      required_tag: {
        type: 'boolean',
        title: t('required_tag.title'),
        description: t('required_tag.text'),
      },
      reserved_tags: {
        type: 'string',
        title: t('reserved_tags.label'),
        description: t('reserved_tags.text'),
      },
    },
  };
  const uiSchema: UISchema = {
    restrict_answer: {
      'ui:widget': 'switch',
      'ui:options': {
        label: t('required_tag.label'),
      },
    },
    recommend_tags: {
      'ui:widget': 'textarea',
      'ui:options': {
        rows: 10,
      },
    },
    required_tag: {
      'ui:widget': 'switch',
      'ui:options': {
        label: t('required_tag.label'),
      },
    },
    reserved_tags: {
      'ui:widget': 'textarea',
      'ui:options': {
        rows: 10,
      },
    },
  };
  const [formData, setFormData] = useState(initFormData(schema));

  const onSubmit = (evt) => {
    evt.preventDefault();

Review comment (suggestion):
This should be
```
label: t('restrict_answer.label')
```

Code after:
        type: 'boolean',
        title: t('restrict_answer.title'),
        description: t('restrict_answer.text'),
      },
      recommend_tags: {
        type: 'string',
        title: t('recommend_tags.label'),
        description: t('recommend_tags.text'),
      },
      required_tag: {
        type: 'boolean',
        title: t('required_tag.title'),
        description: t('required_tag.text'),
      },
      reserved_tags: {
        type: 'string',
        title: t('reserved_tags.label'),
        description: t('reserved_tags.text'),
      },
    },
  };
  const uiSchema: UISchema = {
    restrict_answer: {
      'ui:widget': 'switch',
      'ui:options': {
        label: t('restrict_answer.label'),
      },
    },
    recommend_tags: {
      'ui:widget': 'textarea',
      'ui:options': {
        rows: 10,
      },
    },
    required_tag: {
      'ui:widget': 'switch',
      'ui:options': {
        label: t('required_tag.label'),
      },
    },
    reserved_tags: {
      'ui:widget': 'textarea',
      'ui:options': {
        rows: 10,
      },
    },
  };
  const [formData, setFormData] = useState(initFormData(schema));

  const onSubmit = (evt) => {
    evt.preventDefault();
Repository: answerdev/answer
File: ui/src/pages/Questions/Detail/index.tsx (TypeScript)

Code before:
  const initPage = (type: string) => {
    if (type === 'delete_question') {
      setTimeout(() => {
        navigate('/', { replace: true });
      }, 1000);
      return;
    }
    if (type === 'default') {
      scrollToDocTop();
      getDetail();
      return;
    }
    requestAnswers();
  };

  const writeAnswerCallback = (obj: AnswerItem) => {
    setAnswers({
      count: answers.count + 1,
      list: [...answers.list, obj],
    });

    if (question) {
      setQuestion({
        ...question,
        answered: true,
        answer_ids: [...question.answer_ids, obj.id],
      });
    }
  };

  useEffect(() => {
    if (!qid) {
      return;
    }
    scrollToDocTop();
    getDetail();
    requestAnswers();
  }, [qid]);

  useEffect(() => {
    if (page || order) {
      requestAnswers();
    }
  }, [page, order]);
  usePageTags({
    title: question?.title,
    description: question?.description,
    keywords: question?.tags.map((_) => _.slug_name).join(','),
  });

  const showInviteToAnswer = question?.id;

Review comment (suggestion):
For users who have already answered many times before when limit answer is turned on, the answer_ids of the seat need to be adjusted to first_answer_id. Edit my existing answer button to edit the earliest answer, the answer_ids should be adjusted, we suggest to change it to first_answer_id.

Code after:
    if (type === 'delete_question') {
      setTimeout(() => {
        navigate('/', { replace: true });
      }, 1000);
      return;
    }
    if (type === 'default') {
      scrollToDocTop();
      getDetail();
      return;
    }
    if (type === 'delete_answer') {
      getDetail();
    }
    requestAnswers();
  };

  const writeAnswerCallback = (obj: AnswerItem) => {
    setAnswers({
      count: answers.count + 1,
      list: [...answers.list, obj],
    });

    if (question) {
      setQuestion({
        ...question,
        answered: true,
        first_answer_id: question.first_answer_id
          ? question.first_answer_id
          : obj.id,
      });
    }
  };

  useEffect(() => {
    if (!qid) {
      return;
    }
    scrollToDocTop();
    getDetail();
    requestAnswers();
  }, [qid]);

  useEffect(() => {
    if (page || order) {
      requestAnswers();
    }
  }, [page, order]);
  usePageTags({
    title: question?.title,
    description: question?.description,
Repository: answerdev/answer
File: ui/src/pages/Questions/Detail/index.tsx (TypeScript)

Code before:
  const initPage = (type: string) => {
    if (type === 'delete_question') {
      setTimeout(() => {
        navigate('/', { replace: true });
      }, 1000);
      return;
    }
    if (type === 'default') {
      scrollToDocTop();
      getDetail();
      return;
    }
    requestAnswers();
  };

  const writeAnswerCallback = (obj: AnswerItem) => {
    setAnswers({
      count: answers.count + 1,
      list: [...answers.list, obj],
    });

    if (question) {
      setQuestion({
        ...question,
        answered: true,
        first_answer_id: obj.id,
      });
    }
  };

  useEffect(() => {
    if (!qid) {
      return;
    }
    scrollToDocTop();
    getDetail();
    requestAnswers();
  }, [qid]);

  useEffect(() => {
    if (page || order) {
      requestAnswers();
    }
  }, [page, order]);
  usePageTags({
    title: question?.title,
    description: question?.description,
    keywords: question?.tags.map((_) => _.slug_name).join(','),
  });

  const showInviteToAnswer = question?.id;

Review comment (suggestion):
This value is inaccurate. When the user has multiple answers, the value will be the latest answer every time instead of the id of the first answer, which can be obtained from the callback.

```
// Questions/Detail/index.tsx

const writeAnswerCallback = (obj: AnswerItem, firstAnswerId) => {
    setAnswers({
      count: answers.count + 1,
      list: [...answers.list, obj],
    });

    if (question) {
      setQuestion({
        ...question,
        answered: true,
        first_answer_id: firstAnswerId,
      });
    }
  };
  
  // Questions/Detail/components/WriteAnswer/index.tsx
  
interface Props {
  ....
     callback?: (obj, id) => void;
}

// line 186
 callback?.(res.info, res.question.first_answer_id);
  
  
```

Code after:
  const initPage = (type: string) => {
    if (type === 'delete_question') {
      setTimeout(() => {
        navigate('/', { replace: true });
      }, 1000);
      return;
    }
    if (type === 'default') {
      scrollToDocTop();
      getDetail();
      return;
    }
    if (type === 'delete_answer') {
      getDetail();
    }
    requestAnswers();
  };

  const writeAnswerCallback = (obj: AnswerItem) => {
    setAnswers({
      count: answers.count + 1,
      list: [...answers.list, obj],
    });

    if (question) {
      setQuestion({
        ...question,
        answered: true,
        first_answer_id: question.first_answer_id
          ? question.first_answer_id
          : obj.id,
      });
    }
  };

  useEffect(() => {
    if (!qid) {
      return;
    }
    scrollToDocTop();
    getDetail();
    requestAnswers();
  }, [qid]);

  useEffect(() => {
    if (page || order) {
      requestAnswers();
    }
  }, [page, order]);
  usePageTags({
    title: question?.title,
Repository: answerdev/answer
File: ui/src/components/Comment/index.tsx (TypeScript)

Code before:
    if (pageIndex === 0 && commentId) {
      console.log('scrollCallback');
      setTimeout(() => {
        const el = document.getElementById(commentId);
        console.log(el);
        scrollToElementTop(el);
        bgFadeOut(el);
      }, 100);
    }

    return () => {
      updateCurrentReplyId('');
    };
  }, []);

  useEffect(() => {
    if (!data) {
      return;
    }
    if (pageIndex === 1 || pageIndex === 0) {
      setComments(data?.list);
    } else {
      setComments([...comments, ...data.list]);
    }
    const user: Types.PageUser[] = [];
    if (data.count <= 3) {
      data.list.sort((a, b) => a.created_at - b.created_at);
    }
    data.list.forEach((item) => {
      user.push({
        id: item.user_id,
        displayName: item.user_display_name,
        userName: item.username,
      });
      user.push({
        id: item.reply_comment_id,
        displayName: item.reply_user_display_name,
        userName: item.username,
      });
    });
    pageUsers.setUsers(user);
  }, [data]);

  const handleReply = (id) => {
    if (!tryNormalLogged(true)) {
      return;
    }
    comments.forEach((item) => {
      if (item.comment_id === id) {
        updateCurrentReplyId(id);
      }

Review comment (suggestion):
Should be sorted first and then assigned.

```

  if (pageIndex === 1 || pageIndex === 0) {
  
   if (data.count <= 3) {
      data.list.sort((a, b) => a.created_at - b.created_at);
    }
      setComments(data?.list);
    } else {
      setComments([...comments, ...data.list]);
    }
    
    ... 
```

Code after:
    if (pageIndex === 0 && commentId) {
      console.log('scrollCallback');
      setTimeout(() => {
        const el = document.getElementById(commentId);
        console.log(el);
        scrollToElementTop(el);
        bgFadeOut(el);
      }, 100);
    }

    return () => {
      updateCurrentReplyId('');
    };
  }, []);

  useEffect(() => {
    if (!data) {
      return;
    }
    if (data.count <= 3) {
      data.list.sort((a, b) => a.created_at - b.created_at);
    }
    if (pageIndex === 1 || pageIndex === 0) {
      setComments(data?.list);
    } else {
      setComments([...comments, ...data.list]);
    }
    const user: Types.PageUser[] = [];
    data.list.forEach((item) => {
      user.push({
        id: item.user_id,
        displayName: item.user_display_name,
        userName: item.username,
      });
      user.push({
        id: item.reply_comment_id,
        displayName: item.reply_user_display_name,
        userName: item.username,
      });
    });
    pageUsers.setUsers(user);
  }, [data]);

  const handleReply = (id) => {
    if (!tryNormalLogged(true)) {
      return;
    }
    comments.forEach((item) => {
      if (item.comment_id === id) {
        updateCurrentReplyId(id);
      }
Repository: answerdev/answer
File: ui/src/pages/Install/index.tsx (TypeScript)

Code before:
      errorMsg: '',
    },
    login_required: {
      value: false,
      isInvalid: false,
      errorMsg: '',
    },
    name: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
    password: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
    email: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
  });

  const updateFormData = (params: FormDataType) => {
    if (step === 2) {
      let updatedFormData = formData;
      if (params.db_type.value === 'mysql') {
        updatedFormData = {
          ...updatedFormData,
          db_username: { ...updatedFormData.db_username, value: 'root' },
          db_password: { ...updatedFormData.db_password, value: 'root' },
          db_host: { ...updatedFormData.db_host, value: 'db:3306' },
        };
      } else if (params.db_type.value === 'postgres') {
        updatedFormData = {
          ...updatedFormData,
          db_username: { ...updatedFormData.db_username, value: 'postgres' },
          db_password: { ...updatedFormData.db_password, value: 'postgres' },
          db_host: { ...updatedFormData.db_host, value: 'db:5432' },
        };
      }
      return updatedFormData;
    }
    return formData;
  };
  
  const handleChange = (params: FormDataType) => {
    setErrorData({
      msg: '',
    });

Review comment (suggestion):
Only when the value of db_type changes, do you need to reset the default value

 if (Object.keys(params)?.[0] === 'db_type') {
  ...
}

Code after:
      errorMsg: '',
    },
    login_required: {
      value: false,
      isInvalid: false,
      errorMsg: '',
    },
    name: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
    password: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
    email: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
  });

  const updateFormData = (params: FormDataType) => {
    if (Object.keys(params)?.[0] === 'db_type') {
      let updatedFormData = formData;
      if (params.db_type.value === 'mysql') {
        updatedFormData = {
          ...updatedFormData,
          db_username: { ...updatedFormData.db_username, value: 'root' },
          db_password: { ...updatedFormData.db_password, value: 'root' },
          db_host: { ...updatedFormData.db_host, value: 'db:3306' },
        };
      } else if (params.db_type.value === 'postgres') {
        updatedFormData = {
          ...updatedFormData,
          db_username: { ...updatedFormData.db_username, value: 'postgres' },
          db_password: { ...updatedFormData.db_password, value: 'postgres' },
          db_host: { ...updatedFormData.db_host, value: 'db:5432' },
        };
      }
      return updatedFormData;
    }
    return formData;
  };
  
  const handleChange = (params: FormDataType) => {
    setErrorData({
      msg: '',
    });
Repository: answerdev/answer
File: plugin/importer.go (Go)

Code before:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package plugin

import "github.com/gin-gonic/gin"

type QuestionImporterInfo struct {
	Title     string   `json:"title"`
	Content   string   `json:"content"`
	Tags      []string `json:"tags"`
	UserEmail string   `json:"user_email"`
}

type Importer interface {
	Base
	GetQuestion(ctx *gin.Context) (questionInfo *QuestionImporterInfo, err error)
}

var (
	// CallImporter is a function that calls all registered parsers
	CallImporter,
	registerImporter = MakePlugin[Importer](false)
)

func ImporterEnabled() (enabled bool) {
	_ = CallImporter(func(fn Importer) error {
		enabled = true
		return nil
	})
	return
}
func GetImporter() (ip Importer, ok bool) {
	_ = CallImporter(func(fn Importer) error {
		ip = fn
		ok = true
		return nil
	})
	return
}


Review comment (suggestion):
The Importer interface should be designed primarily for importing data. It is also designed for expansion later. So it should accept the Q&A data directly and add it to the database. May like this

``` go
type Importer interface {
	Base
	RegisterImporter(importer QuestionImporter)
}

type QuestionImporter interface {
        AddQuestions(questions *QuestionInfo) (err error)
}
```

FYI: https://github.com/apache/incubator-answer/blob/8e6fa13c72dae18b8c516bf349928f9d3109988c/plugin/search.go#L106

Code after:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package plugin

import (
	"context"
)

type QuestionImporterInfo struct {
	Title     string   `json:"title"`
	Content   string   `json:"content"`
	Tags      []string `json:"tags"`
	UserEmail string   `json:"user_email"`
}

type Importer interface {
	Base
	RegisterImporterFunc(ctx context.Context, importer ImporterFunc)
}

type ImporterFunc interface {
	AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo) (err error)
}

var (
	// CallImporter is a function that calls all registered parsers
	CallImporter,
	registerImporter = MakePlugin[Importer](false)
)

func ImporterEnabled() (enabled bool) {
	_ = CallImporter(func(fn Importer) error {
		enabled = true
		return nil
	})
	return
}
func GetImporter() (ip Importer, ok bool) {
	_ = CallImporter(func(fn Importer) error {
		ip = fn
		ok = true
		return nil
Repository: answerdev/answer
File: plugin/importer.go (Go)

Code before:
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package plugin

import (
	"context"
)

type QuestionImporterInfo struct {
	Title     string   `json:"title"`
	Content   string   `json:"content"`
	Tags      []string `json:"tags"`
	UserEmail string   `json:"user_email"`
}

type Importer interface {
	Base
	RegisterImporterFunc(ctx context.Context, importer ImporterFunc)
}

type ImporterFunc interface {
	AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo)
}

var (
	// CallImporter is a function that calls all registered parsers
	CallImporter,
	registerImporter = MakePlugin[Importer](false)
)

func ImporterEnabled() (enabled bool) {
	_ = CallImporter(func(fn Importer) error {
		enabled = true
		return nil
	})
	return
}
func GetImporter() (ip Importer, ok bool) {
	_ = CallImporter(func(fn Importer) error {
		ip = fn
		ok = true
		return nil
	})
	return
}


Review comment (bug):
It's better to return an error here. `AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo) (err error)`

Code after:
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package plugin

import (
	"context"
)

type QuestionImporterInfo struct {
	Title     string   `json:"title"`
	Content   string   `json:"content"`
	Tags      []string `json:"tags"`
	UserEmail string   `json:"user_email"`
}

type Importer interface {
	Base
	RegisterImporterFunc(ctx context.Context, importer ImporterFunc)
}

type ImporterFunc interface {
	AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo) (err error)
}

var (
	// CallImporter is a function that calls all registered parsers
	CallImporter,
	registerImporter = MakePlugin[Importer](false)
)

func ImporterEnabled() (enabled bool) {
	_ = CallImporter(func(fn Importer) error {
		enabled = true
		return nil
	})
	return
}
func GetImporter() (ip Importer, ok bool) {
	_ = CallImporter(func(fn Importer) error {
		ip = fn
		ok = true
		return nil
	})
	return
}
Repository: answerdev/answer
File: ui/src/components/Footer/index.tsx (TypeScript)

Code before:

import { siteInfoStore } from '@/stores';

const Index = () => {
  const { t } = useTranslation('translation', { keyPrefix: 'footer' }); // Scoped translations for footer
  const fullYear = dayjs().format('YYYY');
  const siteName = siteInfoStore((state) => state.siteInfo.name);
  const cc = `${fullYear} ${siteName}`;

  return (
    <footer className="bg-light">
      <Container>
        <p className="text-center mb-0 small">
          {/* Link to Terms of Service with right margin */}
          <Link to="/tos" className="me-2">
            {t('label', { keyPrefix: 'admin.legal.terms_of_service' })}
          </Link>

          {/* Link to Privacy Policy with right margin for spacing */}
          <Link to="/privacy">
            {t('label', { keyPrefix: 'admin.legal.privacy_policy' })}
          </Link>
        </p>
        <p className="text-center mb-0 small">
          <Trans i18nKey="build_on" values={{ cc }}>
            Powered by{' '}
            <a
              href="https://answer.apache.org"
              target="_blank"
              rel="noopener noreferrer">
              Apache Answer
            </a>
            - the open-source software that powers Q&A communities.
            <br />
            Made with love © 2022 Answer.
          </Trans>
        </p>
      </Container>
    </footer>
  );
};

export default React.memo(Index);


Review comment (suggestion):


```suggestion
            Powered by
            {/* eslint-disable-next-line react/jsx-no-target-blank */}
```

Code after:

import { siteInfoStore } from '@/stores';

const Index = () => {
  const { t } = useTranslation('translation', { keyPrefix: 'footer' }); // Scoped translations for footer
  const fullYear = dayjs().format('YYYY');
  const siteName = siteInfoStore((state) => state.siteInfo.name);
  const cc = `${fullYear} ${siteName}`;

  return (
    <footer className="bg-light">
      <Container>
        <p className="text-center mb-0 small">
          {/* Link to Terms of Service with right margin */}
          <Link to="/tos" className="me-2">
            {t('label', { keyPrefix: 'admin.legal.terms_of_service' })}
          </Link>

          {/* Link to Privacy Policy with right margin for spacing */}
          <Link to="/privacy">
            {t('label', { keyPrefix: 'admin.legal.privacy_policy' })}
          </Link>
        </p>
        <p className="text-center mb-0 small">
          <Trans i18nKey="footer.build_on" values={{ cc }}>
            Powered by
            {/* eslint-disable-next-line react/jsx-no-target-blank */}
            <a href="https://answer.apache.org" target="_blank">
              Apache Answer
            </a>
            - the open-source software that powers Q&A communities.
            <br />
            Made with love. © 2022 Answer.
          </Trans>
        </p>
      </Container>
    </footer>
  );
};

export default React.memo(Index);