| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- 'use client';
- import './style.scss';
- import Link from 'next/link';
- import Image from 'next/image';
- import { useState, useEffect } from 'react';
- import { useMemberContext } from '@/contexts/memberProvider';
- import { fetchApi, throwError } from '@/lib/utils/client';
- import Loading from '@/app/component/Loading';
- export default function ChangeThumb()
- {
- const { member, setMember } = useMemberContext();
- const [error, setError] = useState<string>('');
- const [loading, setLoading] = useState<boolean>(false);
- const [newThumb, setNewThumb] = useState<File|null>(null);
- const [preview, setPreview] = useState<string|null>(null);
- useEffect(() => {
- if (error) {
- alert(error);
- setError('');
- }
- }, [error]);
- const resetFileInput = () => {
- const fileInput = document.getElementById("thumb") as HTMLInputElement;
- if (fileInput) fileInput.value = "";
- };
- const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
- if (!member) {
- return;
- }
- if (!newThumb) {
- return setError('사진을 선택하세요.');
- }
- setLoading(true);
- const formData = new FormData();
- formData.append('thumb', newThumb);
- fetchApi<{thumbUrl: string}>('/api/mypage/thumb', {
- method: 'POST',
- body: formData
- }).then((res) => {
- throwError(res);
- member.thumb = (res.data?.thumbUrl || null);
- setMember(member);
- localStorage.setItem('member', JSON.stringify(member));
- alert("사진이 변경되었습니다.");
- }).catch(err => {
- setError(err.message);
- }).finally(() => {
- setLoading(false);
- setNewThumb(null);
- setPreview(null);
- resetFileInput();
- });
- }
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- if (e.target.files && e.target.files[0]) {
- const selectedFile = e.target.files[0];
- setNewThumb(selectedFile);
- // 이미지 미리보기
- const reader = new FileReader();
- reader.onloadend = () => setPreview(reader.result as string);
- reader.readAsDataURL(selectedFile);
- }
- };
- const handleRemove = async () => {
- // 미리보기만 있고 서버에 저장된 사진이 아닌 경우 로컬만 초기화
- if (!member?.thumb) {
- setNewThumb(null);
- setPreview(null);
- resetFileInput();
- return;
- }
- if (!confirm('사진을 삭제하시겠습니까?')) {
- return;
- }
- setLoading(true);
- fetchApi('/api/mypage/thumb', {
- method: 'DELETE'
- }).then((res) => {
- throwError(res);
- member.thumb = null;
- setMember(member);
- localStorage.setItem('member', JSON.stringify(member));
- setNewThumb(null);
- setPreview(null);
- resetFileInput();
- alert("사진이 삭제되었습니다.");
- }).catch(err => {
- setError(err.message);
- }).finally(() => {
- setLoading(false);
- });
- };
- return (
- <div id="changeThumb">
- { loading && <Loading /> }
- <h1>회원 사진</h1>
- <form method="post" acceptCharset="utf-8" autoComplete="off" onSubmit={handleSubmit}>
- <table className="table-auto max-xl:w-full lg:w-[600px]">
- <caption>
- 커뮤니티 프로필에 대표 사진을 설정할 수 있습니다.
- </caption>
- <colgroup>
- <col width="140px"/>
- <col width="*"/>
- </colgroup>
- <tbody>
- <tr>
- <th>
- <figure>
- {preview ?
- <Image src={preview} alt="미리보기" width={140} height={140} layout="responsive" style={{objectFit: "cover"}} />
- :
- member?.thumb ?
- <Image src={member.thumb} alt={member.email} width={140} height={140} style={{objectFit: "cover"}} unoptimized={true} />
- :
- <Image src="/resources/thumb.gif" alt="기본 사진" width={140} height={140} style={{objectFit: "cover"}} />
- }
- </figure>
- </th>
- <td>
- <div className="flex justify-start gap-2">
- <label htmlFor="thumb" className="btn btn-default">사진 변경</label>
- <input type="file" id="thumb" accept="image/jpg,image/jpeg,image/png,image/gif" hidden onChange={handleChange} />
- {(preview || member?.thumb) &&
- <button type="button" className="btn btn-default" onClick={handleRemove}>삭제</button>
- }
- </div>
- <p className="mt-3">
- 98x98 크기 이상, 4MB 이하의 사진이 권장됩니다. 이미지 파일을 사용하세요. 사진이 {process.env.NEXT_PUBLIC_SITE_NAME} 커뮤니티 가이드를 준수해야 합니다.
- </p>
- </td>
- </tr>
- </tbody>
- <tfoot>
- <tr>
- <td colSpan={2}>
- <div className="flex justify-center gap-2">
- <button type="submit" className="btn btn-submit">확인</button>
- <Link href="/profile" className="btn btn-default">취소</Link>
- </div>
- </td>
- </tr>
- </tfoot>
- </table>
- </form>
- </div>
- );
- }
|