page.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. 'use client';
  2. import './style.scss';
  3. import Link from 'next/link';
  4. import { useState, useEffect } from 'react';
  5. import { useConfigContext } from '@/contexts/configProvider';
  6. import { useMemberContext } from '@/contexts/memberProvider';
  7. import { ChangeIntroRequest } from '@/dtos/request/account';
  8. import { fetchChangeIntro, fetchRemoveIntro } from '@/lib/api/account';
  9. import { throwError } from '@/lib/utils/client';
  10. import Editor from '@/app/component/Editor';
  11. export default function ChangeIntro()
  12. {
  13. const config = useConfigContext();
  14. const { member, setMember } = useMemberContext();
  15. const [error, setError] = useState<string>('');
  16. const [newIntro, setNewIntro] = useState<string|null>('');
  17. useEffect(() => {
  18. if (error) {
  19. alert(error);
  20. setError('');
  21. }
  22. }, [error]);
  23. useEffect(() => {
  24. if (member?.intro && !newIntro) {
  25. const intro = member.intro.replace(/<img\b[^>]*?\bsrc=[''](\/[^'']*)['']/gi, (match, srcPath) => {
  26. return match.replace(srcPath, process.env.API_URL + srcPath);
  27. });
  28. setNewIntro(intro);
  29. }
  30. }, [member, newIntro]);
  31. const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
  32. e.preventDefault();
  33. if (!member) {
  34. return;
  35. }
  36. fetchChangeIntro({ Intro: newIntro } as ChangeIntroRequest).then((res) => {
  37. throwError(res);
  38. member.intro = newIntro;
  39. setMember(member);
  40. setNewIntro(newIntro);
  41. localStorage.setItem('member', JSON.stringify(member));
  42. alert('자기소개가 변경되었습니다.');
  43. }).catch(err => {
  44. setError(err.message);
  45. setNewIntro(member.intro);
  46. });
  47. }
  48. const handleDelete = async () => {
  49. if (confirm("자기소개를 삭제하시겠습니까?")) {
  50. if (!member || !member.intro) {
  51. return;
  52. }
  53. fetchRemoveIntro().then((res) => {
  54. throwError(res);
  55. member.intro = null;
  56. setMember(member);
  57. localStorage.setItem('member', JSON.stringify(member));
  58. alert("자기소개가 삭제되었습니다.");
  59. }).catch(err => {
  60. setError(err.message);
  61. }).finally(() => {
  62. setNewIntro('');
  63. });
  64. }
  65. };
  66. return (
  67. <>
  68. <div id='changeIntro'>
  69. <h1>자기소개 변경</h1>
  70. <form id='fChangeIntro' method='post' acceptCharset='utf-8' autoComplete='off' onSubmit={handleSubmit}>
  71. <table className='table-auto max-xl:w-full xl:w-[800px]'>
  72. <caption>
  73. 커뮤니티 프로필에 표시되는 자기소개를 설정할 수 있습니다.
  74. </caption>
  75. <tbody>
  76. <tr>
  77. <td>
  78. <Editor data={newIntro ?? ''} onChange={setNewIntro} />
  79. </td>
  80. </tr>
  81. </tbody>
  82. <tfoot>
  83. <tr>
  84. <td colSpan={3}>
  85. <div className='flex justify-center gap-2'>
  86. <button type='submit' className='btn btn-submit'>확인</button>
  87. { member?.intro && (
  88. <button type="button" className="btn btn-delete" onClick={handleDelete}>삭제</button>
  89. )}
  90. <Link href='/profile' className='btn btn-default'>취소</Link>
  91. </div>
  92. </td>
  93. </tr>
  94. </tfoot>
  95. </table>
  96. </form>
  97. <br />
  98. <dl className='max-xl:w-full xl:w-[800px]'>
  99. <dt hidden>&nbsp;</dt>
  100. <dd>
  101. <ol>
  102. <li>자기소개는 최대 1000자 이내로 입력 가능합니다.</li>
  103. <li>부적절한 내용은 별도의 고지 없이 삭제될 수 있습니다.</li>
  104. {config.account.changeIntroDay > 0 && <li>한마디 변경 주기는 {config.account.changeIntroDay}일입니다.</li>}
  105. </ol>
  106. </dd>
  107. </dl>
  108. </div>
  109. </>
  110. );
  111. }