Report.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. 'use client';
  2. import './style.scss';
  3. import { useState, useCallback, useRef } from 'react';
  4. import Loading from '@/app/component/Loading';
  5. import { Button } from '@/components/ui/button';
  6. import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
  7. import { fetchApi } from '@/lib/utils/client';
  8. import { ReportTypeLabels } from '@/constants/forum';
  9. type Props = {
  10. isEnable: boolean;
  11. open: boolean;
  12. onChange: (_: boolean) => void;
  13. onComplete: (_: boolean) => void;
  14. postID: number;
  15. commentID?: number;
  16. memberID?: number;
  17. }
  18. export default function Report({ isEnable, open, onChange, onComplete, postID, commentID, memberID }: Props)
  19. {
  20. const [loading, setLoading] = useState<boolean>(false);
  21. const [form, setForm] = useState<{
  22. type: string;
  23. reason: string;
  24. }>({
  25. type: '',
  26. reason: ''
  27. });
  28. const typeRef = useRef<HTMLSelectElement>(null);
  29. const reasonRef = useRef<HTMLTextAreaElement>(null);
  30. const handleChange = useCallback((e: React.ChangeEvent<HTMLSelectElement|HTMLTextAreaElement>) => {
  31. const { name, value } = e.target;
  32. setForm((prev) => ({
  33. ...prev,
  34. [name]: value
  35. }));
  36. }, []);
  37. const handleSubmit = useCallback(async () => {
  38. if (!form.type) {
  39. alert('신고 유형을 선택하세요.');
  40. typeRef.current?.focus();
  41. return;
  42. }
  43. if (!form.reason) {
  44. alert('신고 내용을 입력해주세요.');
  45. reasonRef.current?.focus();
  46. return;
  47. }
  48. if (!memberID) {
  49. alert('로그인 후 이용해주세요.');
  50. return;
  51. }
  52. setLoading(true);
  53. onChange(false);
  54. try {
  55. const url = commentID
  56. ? '/api/forum/comments/' + commentID + '/report'
  57. : '/api/forum/posts/' + postID + '/report';
  58. const res = await fetchApi(url, {
  59. method: 'POST',
  60. body: { type: Number(form.type), reason: form.reason }
  61. });
  62. if (res.success) {
  63. alert('신고가 접수되었습니다.');
  64. setForm({ type: '', reason: '' });
  65. onComplete(true);
  66. } else {
  67. }
  68. } catch (err) {
  69. if (err instanceof Error) {
  70. alert(err.message);
  71. }
  72. } finally {
  73. setLoading(false);
  74. }
  75. }, [form, postID, commentID, memberID, onChange, onComplete]);
  76. if (!isEnable) {
  77. return null;
  78. }
  79. return (
  80. <>
  81. {loading && <Loading />}
  82. <Dialog open={open} onOpenChange={onChange}>
  83. <DialogContent className='w-3xs sm:max-w-md'>
  84. <DialogHeader>
  85. <DialogTitle>{commentID ? '댓글 신고' : '게시글 신고'}</DialogTitle>
  86. </DialogHeader>
  87. <div id='report' className='flex flex-col items-center gap-4'>
  88. <select ref={typeRef} name='type' value={form.type} title='신고 유형' onChange={handleChange}>
  89. {Object.entries(ReportTypeLabels).map(([key, label]) => (
  90. <option key={key} value={key}>
  91. {label}
  92. </option>
  93. ))}
  94. </select>
  95. <textarea ref={reasonRef} name='reason' rows={4} value={form.reason} title='신고 내용' onChange={handleChange} maxLength={500} placeholder='신고 내용을 구체적으로 입력해주세요.(500자 이하)'></textarea>
  96. <Button type='button' variant='outline' className='hover:bg-[#E07D0A] hover:text-white hover:border-[#b96606] sm:w-24' onClick={handleSubmit}>
  97. 신고하기
  98. </Button>
  99. </div>
  100. </DialogContent>
  101. </Dialog>
  102. </>
  103. );
  104. }