page.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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 useErrorAlert from '@/hooks/useErrorAlert';
  8. import { ChangePasswordRequest } from '@/types/request/account';
  9. import { fetchApi, getPasswordPolicyMessage } from '@/lib/utils/client';
  10. import Loading from '@/app/component/Loading';
  11. import NavTabs from '../navTabs';
  12. export default function ChangePassword()
  13. {
  14. const config = useConfigContext();
  15. const { member } = useMemberContext();
  16. const { setError } = useErrorAlert();
  17. const [loading, setLoading] = useState<boolean>(false);
  18. const [isComplete, setComplete] = useState<boolean>(false);
  19. const [formData, setFormData] = useState({
  20. currentPassword: '',
  21. newPassword: '',
  22. confirmPassword: ''
  23. });
  24. const currentPasswordRef = useRef<HTMLInputElement>(null);
  25. const newPasswordRef = useRef<HTMLInputElement>(null);
  26. const confirmPasswordRef = useRef<HTMLInputElement>(null);
  27. const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
  28. e.preventDefault();
  29. if (!member) {
  30. return;
  31. }
  32. if (!formData.currentPassword) {
  33. setError("현재 비밀번호를 입력하세요.");
  34. currentPasswordRef.current?.focus();
  35. return;
  36. }
  37. if (!formData.newPassword) {
  38. setError("새 비밀번호를 입력하세요.");
  39. newPasswordRef.current?.focus();
  40. return;
  41. }
  42. if (!formData.confirmPassword) {
  43. setError("새 비밀번호 확인을 입력하세요.");
  44. confirmPasswordRef.current?.focus();
  45. return;
  46. }
  47. if (formData.newPassword !== formData.confirmPassword) {
  48. setError("새 비밀번호가 일치하지 않습니다.");
  49. return;
  50. }
  51. if (formData.currentPassword === formData.newPassword) {
  52. setError("새 비밀번호는 현재 비밀번호와 달라야 합니다.");
  53. return;
  54. }
  55. setLoading(true);
  56. fetchApi('/api/mypage/password', {
  57. method: 'POST',
  58. body: {
  59. CurrentPassword: formData.currentPassword,
  60. NewPassword: formData.newPassword,
  61. ConfirmPassword: formData.confirmPassword
  62. } as ChangePasswordRequest
  63. }).then(() => {
  64. setComplete(true);
  65. }).catch(err => {
  66. setError(err.message);
  67. }).finally(() => {
  68. setLoading(false);
  69. });
  70. }
  71. const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
  72. const { id, value } = e.target;
  73. setFormData(prev => ({ ...prev, [id]: value}));
  74. }, []);
  75. useEffect(() => {
  76. if (isComplete)
  77. {
  78. // 입력 초기화
  79. setFormData({
  80. currentPassword: '',
  81. newPassword: '',
  82. confirmPassword: ''
  83. });
  84. setComplete(false);
  85. alert("비밀번호가 변경되었습니다.");
  86. }
  87. }, [isComplete]);
  88. const txtPasswordGuide = getPasswordPolicyMessage({
  89. passwordUppercaseLength: config.account.passwordUppercaseLength ?? 0,
  90. passwordNumbersLength: config.account.passwordNumbersLength ?? 0,
  91. passwordSpecialcharsLength: config.account.passwordSpecialcharsLength ?? 0
  92. });
  93. return (
  94. <>
  95. <NavTabs />
  96. <div id="changePassword">
  97. { loading && <Loading /> }
  98. <h1>비밀번호 변경</h1>
  99. <form method="post" acceptCharset="utf-8" autoComplete="off" onSubmit={handleSubmit}>
  100. <table className="table-auto max-xl:w-full lg:w-[600px]">
  101. <caption>
  102. 비밀번호는 { config.account.passwordMinLength }자 이상 20자 이하로 설정 가능합니다.<br />
  103. { txtPasswordGuide }
  104. </caption>
  105. <colgroup>
  106. <col width="30%"/>
  107. <col width="60%"/>
  108. <col width="10%"/>
  109. </colgroup>
  110. <tbody>
  111. <tr>
  112. <th><label htmlFor='currentPassword'>현재 비밀번호</label></th>
  113. <td>
  114. <input type="password" id="currentPassword" value={formData.currentPassword} onChange={handleChange} placeholder="현재 비밀번호" ref={currentPasswordRef} />
  115. </td>
  116. <td>&nbsp;</td>
  117. </tr>
  118. <tr>
  119. <th><label htmlFor='newPassword'>새 비밀번호</label></th>
  120. <td>
  121. <input type="password" id="newPassword" value={formData.newPassword} onChange={handleChange} placeholder="새 비밀번호" ref={newPasswordRef} />
  122. </td>
  123. <td>&nbsp;</td>
  124. </tr>
  125. <tr>
  126. <th><label htmlFor='confirmPassword'>새 비밀번호 확인</label></th>
  127. <td>
  128. <input type="password" id="confirmPassword" value={formData.confirmPassword} onChange={handleChange} placeholder="새 비밀번호 확인" ref={confirmPasswordRef} />
  129. </td>
  130. <td>&nbsp;</td>
  131. </tr>
  132. </tbody>
  133. <tfoot>
  134. <tr>
  135. <td colSpan={3}>
  136. <div className="flex justify-center gap-2">
  137. <button type="submit" className="btn btn-submit">확인</button>
  138. <Link href="/profile" className="btn btn-default">취소</Link>
  139. </div>
  140. </td>
  141. </tr>
  142. </tfoot>
  143. </table>
  144. </form>
  145. </div>
  146. </>
  147. );
  148. }