page.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. 'use client';
  2. import './style.scss';
  3. import Link from 'next/link';
  4. import { useState, useEffect, useCallback, useRef } from 'react';
  5. import { useConfigContext } from '@/contexts/configProvider';
  6. import { useMemberContext } from '@/contexts/memberProvider';
  7. import { ChangeSummaryRequest } from '@/dtos/request/account';
  8. import { fetchApi, throwError } from '@/lib/utils/client';
  9. import Loading from '@/app/component/Loading';
  10. export default function ChangeSummary()
  11. {
  12. const config = useConfigContext();
  13. const { member, setMember } = useMemberContext();
  14. const [error, setError] = useState<string>('');
  15. const [loading, setLoading] = useState<boolean>(false);
  16. const [newSummary, setNewSummary] = useState<string|null>('');
  17. const newSummaryRef = useRef<HTMLInputElement>(null);
  18. useEffect(() => {
  19. if (error) {
  20. alert(error);
  21. setError('');
  22. }
  23. }, [error]);
  24. const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
  25. e.preventDefault();
  26. if (!member) {
  27. return;
  28. }
  29. if (!newSummary) {
  30. newSummaryRef.current?.focus();
  31. return setError('한마디를 입력하세요.');
  32. }
  33. setLoading(true);
  34. fetchApi('/api/mypage/summary', {
  35. method: 'POST',
  36. body: { Summary: newSummary } as ChangeSummaryRequest
  37. }).then((res) => {
  38. throwError(res);
  39. member.summary = newSummary;
  40. setMember(member);
  41. localStorage.setItem('member', JSON.stringify(member));
  42. alert('한마디가 변경되었습니다.');
  43. }).catch(err => {
  44. setError(err.message);
  45. }).finally(() => {
  46. setLoading(false);
  47. setNewSummary('');
  48. });
  49. }
  50. const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
  51. setNewSummary(e.target.value.trim());
  52. }, []);
  53. const handleDelete = async () => {
  54. if (confirm("한마디를 삭제하시겠습니까?")) {
  55. if (!member || !member.summary) {
  56. return;
  57. }
  58. setLoading(true);
  59. fetchApi('/api/mypage/summary', { method: 'DELETE' }).then((res) => {
  60. throwError(res);
  61. member.summary = null;
  62. setMember(member);
  63. localStorage.setItem('member', JSON.stringify(member));
  64. alert("한마디를 삭제되었습니다.");
  65. }).catch(err => {
  66. setError(err.message);
  67. }).finally(() => {
  68. setLoading(false);
  69. setNewSummary('');
  70. });
  71. }
  72. };
  73. return (
  74. <>
  75. <div id='changeSummary'>
  76. { loading && <Loading /> }
  77. <h1>한마디 변경</h1>
  78. <form method='post' acceptCharset='utf-8' autoComplete='off' onSubmit={handleSubmit}>
  79. <table className='table-auto max-xl:w-full lg:w-[600px]'>
  80. <caption>
  81. 커뮤니티 프로필에 표시되는 한마디를 설정할 수 있습니다.
  82. </caption>
  83. <colgroup>
  84. <col width='30%'/>
  85. <col width='60%'/>
  86. <col width='10%'/>
  87. </colgroup>
  88. <tbody>
  89. <tr>
  90. <th>현재 한마디</th>
  91. <td>{member?.summary || '-'}</td>
  92. <td>&nbsp;</td>
  93. </tr>
  94. <tr>
  95. <th>새 한마디</th>
  96. <td>
  97. <input type='text' name='new_summary' id='newSummary' ref={newSummaryRef} value={newSummary ?? ''} placeholder='변경할 한마디' maxLength={50} autoFocus autoComplete='off' onChange={handleChange} />
  98. </td>
  99. <td>&nbsp;</td>
  100. </tr>
  101. </tbody>
  102. <tfoot>
  103. <tr>
  104. <td colSpan={3}>
  105. <div className='flex justify-center gap-2'>
  106. <button type='submit' className='btn btn-submit'>확인</button>
  107. { member?.summary && (
  108. <button type='button' className='btn btn-delete' onClick={handleDelete}>삭제</button>
  109. )}
  110. <Link href='/profile' className='btn btn-default'>취소</Link>
  111. </div>
  112. </td>
  113. </tr>
  114. </tfoot>
  115. </table>
  116. </form>
  117. <br />
  118. <dl className='max-xl:w-full lg:w-[600px]'>
  119. <dt>등록할 수 없는 한마디</dt>
  120. <dd>
  121. <ol>
  122. <li>한마디는 최대 50자 이내로 입력 가능합니다.</li>
  123. <li>부적절한 내용은 별도의 고지 없이 변경될 수 있습니다.</li>
  124. {config.account.changeSummaryDay > 0 && <li>자기소개개 변경 주기는 {config.account.changeSummaryDay}일입니다.</li>}
  125. </ol>
  126. </dd>
  127. </dl>
  128. </div>
  129. </>
  130. );
  131. }