11import { useAuth } from "@/contexts/AuthProvider" ;
2+ import { createBugReport } from "@/api/bugReport" ;
23import Navigationbar from "@/widgets/Navigationbar" ;
34import styles from "@styles/MyPage.module.css" ;
45import { BookmarkWidget } from "./bookmark/Bookmark" ;
@@ -7,7 +8,7 @@ import { useNavigate } from "react-router-dom";
78import { useTimetable } from "@/contexts/TimetableContext" ;
89import { useEffect , useState } from "react" ;
910import { RiPencilFill } from "react-icons/ri" ;
10- import { FaCamera , FaStar } from "react-icons/fa6" ;
11+ import { FaBug , FaCamera , FaStar , FaTrashCan } from "react-icons/fa6" ;
1112import { IoMdDoneAll } from "react-icons/io" ;
1213import { useUserData } from "@/contexts/UserDataContext" ;
1314import Onboarding from "./auth/OnBoarding/Onboarding" ;
@@ -147,16 +148,16 @@ const ProfileCard = ({ onClickInterest }: { onClickInterest: () => void }) => {
147148 < span > 행사 보기 우선순위</ span >
148149 </ div >
149150 { interestCategories && interestCategories . length > 0 ? (
150- < ul className = { styles . preferenceChips } >
151- { interestCategories . map ( ( cat , idx ) => (
152- < li
153- className = { `${ styles . preferenceChip } ${ cat . groupId === 3 && styles . category } ${ cat . groupId === 2 && styles . organization } ` }
154- key = { cat . id }
155- >
156- { `${ idx + 1 } 순위: ${ cat . name } ` }
157- </ li >
158- ) ) }
159- </ ul >
151+ < ul className = { styles . preferenceChips } >
152+ { interestCategories . map ( ( cat , idx ) => (
153+ < li
154+ className = { `${ styles . preferenceChip } ${ cat . groupId === 3 && styles . category } ${ cat . groupId === 2 && styles . organization } ` }
155+ key = { cat . id }
156+ >
157+ { `${ idx + 1 } 순위: ${ cat . name } ` }
158+ </ li >
159+ ) ) }
160+ </ ul >
160161 ) : (
161162 < span className = { styles . notYetText } >
162163 클릭해서 우선순위로 확인할 행사를 설정해보세요!
@@ -187,6 +188,132 @@ const ProfileCard = ({ onClickInterest }: { onClickInterest: () => void }) => {
187188 ) ;
188189} ;
189190
191+ const BugReportSection = ( ) => {
192+ const [ title , setTitle ] = useState ( "" ) ;
193+ const [ content , setContent ] = useState ( "" ) ;
194+ const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
195+
196+ const handleSubmit = async ( event : React . FormEvent < HTMLFormElement > ) => {
197+ event . preventDefault ( ) ;
198+
199+ const trimmedTitle = title . trim ( ) ;
200+ const trimmedContent = content . trim ( ) ;
201+
202+ if ( ! trimmedTitle || ! trimmedContent ) {
203+ alert ( "제목과 내용을 모두 입력해주세요." ) ;
204+ return ;
205+ }
206+
207+ if ( isSubmitting ) return ;
208+
209+ try {
210+ setIsSubmitting ( true ) ;
211+ await createBugReport ( {
212+ title : trimmedTitle ,
213+ content : trimmedContent ,
214+ } ) ;
215+ setTitle ( "" ) ;
216+ setContent ( "" ) ;
217+ alert ( "버그 신고가 접수되었습니다." ) ;
218+ } catch ( error ) {
219+ console . error ( "Bug report submission failed:" , error ) ;
220+ alert ( "버그 신고 접수에 실패했습니다. 잠시 후 다시 시도해주세요." ) ;
221+ } finally {
222+ setIsSubmitting ( false ) ;
223+ }
224+ } ;
225+
226+ return (
227+ < section className = { styles . bugReportSection } >
228+ < div className = { styles . bugReportHeader } >
229+ < div className = { styles . bugReportTitle } >
230+ < FaBug size = { 18 } />
231+ < strong > 버그 신고</ strong >
232+ </ div >
233+ < span > 이용 중 발견한 문제를 알려주세요.</ span >
234+ </ div >
235+ < form className = { styles . bugReportForm } onSubmit = { handleSubmit } >
236+ < input
237+ className = { styles . bugReportInput }
238+ type = "text"
239+ value = { title }
240+ placeholder = "제목"
241+ maxLength = { 100 }
242+ onChange = { ( event ) => setTitle ( event . currentTarget . value ) }
243+ disabled = { isSubmitting }
244+ />
245+ < textarea
246+ className = { styles . bugReportTextarea }
247+ value = { content }
248+ placeholder = "문제가 발생한 상황을 자세히 적어주세요."
249+ rows = { 5 }
250+ maxLength = { 1000 }
251+ onChange = { ( event ) => setContent ( event . currentTarget . value ) }
252+ disabled = { isSubmitting }
253+ />
254+ < button
255+ className = { styles . bugReportSubmitButton }
256+ type = "submit"
257+ disabled = { isSubmitting }
258+ >
259+ { isSubmitting ? "접수 중" : "신고하기" }
260+ </ button >
261+ </ form >
262+ </ section >
263+ ) ;
264+ } ;
265+
266+ const AccountDeletionSection = ( ) => {
267+ const { deleteAccount } = useAuth ( ) ;
268+ const [ isConfirmOpen , setIsConfirmOpen ] = useState ( false ) ;
269+ const [ isDeleting , setIsDeleting ] = useState ( false ) ;
270+ const navigate = useNavigate ( ) ;
271+
272+ const handleDeleteAccount = async ( ) => {
273+ if ( isDeleting ) return ;
274+
275+ try {
276+ setIsDeleting ( true ) ;
277+ await deleteAccount ( ) ;
278+ navigate ( "/" , { replace : true } ) ;
279+ } catch ( error ) {
280+ console . error ( "Account deletion failed:" , error ) ;
281+ alert ( "회원탈퇴에 실패했습니다. 잠시 후 다시 시도해주세요." ) ;
282+ } finally {
283+ setIsDeleting ( false ) ;
284+ setIsConfirmOpen ( false ) ;
285+ }
286+ } ;
287+
288+ return (
289+ < section className = { styles . accountDeletionSection } >
290+ < div className = { styles . accountDeletionText } >
291+ < strong > 회원탈퇴</ strong >
292+ < span > 계정을 삭제하면 저장된 정보가 복구되지 않습니다.</ span >
293+ </ div >
294+ < button
295+ className = { styles . deleteAccountButton }
296+ type = "button"
297+ onClick = { ( ) => setIsConfirmOpen ( true ) }
298+ disabled = { isDeleting }
299+ >
300+ < FaTrashCan size = { 14 } />
301+ < span > { isDeleting ? "탈퇴 처리 중" : "회원탈퇴" } </ span >
302+ </ button >
303+ { isConfirmOpen && (
304+ < Modal
305+ content = "정말 회원탈퇴를 진행하시겠어요? 삭제된 계정은 복구할 수 없습니다."
306+ leftText = { isDeleting ? "처리 중" : "탈퇴하기" }
307+ rightText = "취소"
308+ onLeftClick = { handleDeleteAccount }
309+ onRightClick = { ( ) => setIsConfirmOpen ( false ) }
310+ onClose = { ( ) => setIsConfirmOpen ( false ) }
311+ />
312+ ) }
313+ </ section >
314+ ) ;
315+ } ;
316+
190317const MyPage = ( ) => {
191318 const { user, isLoading } = useAuth ( ) ;
192319 const [ isEditingInterest , setIsEditingInterest ] = useState < boolean > ( false ) ;
@@ -211,6 +338,8 @@ const MyPage = () => {
211338 < BookmarkWidget />
212339 < MemoWidget />
213340 </ div >
341+ < BugReportSection />
342+ < AccountDeletionSection />
214343 </ div >
215344 ) : (
216345 < div className = { styles . notFound } >
0 commit comments