Skip to content

Commit e2fd9ad

Browse files
committed
feat: 유저프로필 추가
1 parent f480b99 commit e2fd9ad

File tree

3 files changed

+134
-133
lines changed

3 files changed

+134
-133
lines changed

src/apis/updateUserApi.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export const updateUserPassword = async (password: string) => {
1010
return data;
1111
};
1212

13-
1413
interface UpdateUserProfileParams {
1514
id: string;
1615
user_nickname: string | null;
@@ -26,20 +25,14 @@ export const updateUserProfile = async ({ id, user_nickname, file }: UpdateUserP
2625
const filePath = `profile_images/${id}/${file.name}`;
2726
console.log(`Uploading file to path: ${filePath}`);
2827

29-
const { error: uploadError } = await supabase
30-
.storage
31-
.from(bucketName)
32-
.upload(filePath, file, { upsert: true });
28+
const { error: uploadError } = await supabase.storage.from(bucketName).upload(filePath, file, { upsert: true });
3329

3430
if (uploadError) {
3531
console.error(`File upload error: ${uploadError.message}`);
3632
throw new Error(`File upload error: ${uploadError.message}`);
3733
}
3834

39-
const { data } = supabase
40-
.storage
41-
.from(bucketName)
42-
.getPublicUrl(filePath);
35+
const { data } = supabase.storage.from(bucketName).getPublicUrl(filePath);
4336

4437
if (!data || !data.publicUrl) {
4538
console.error(`Unable to get public URL for file: ${filePath}`);
@@ -60,4 +53,4 @@ export const updateUserProfile = async ({ id, user_nickname, file }: UpdateUserP
6053
}
6154

6255
return updatedData;
63-
};
56+
};

src/assets/svgs/ProfileIcon.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
const ProfileIcon = ({
22
imageUrl,
3-
width = "49", // 기본 가로 크기
4-
height = "49", // 기본 세로 크기
5-
fill = "white" // 기본 색상
3+
width = '49', // 기본 가로 크기
4+
height = '49', // 기본 세로 크기
5+
fill = 'white', // 기본 색상
66
}: {
7-
imageUrl: string | null,
8-
width?: string,
9-
height?: string,
10-
fill?: string
7+
imageUrl: string | null;
8+
width?: string;
9+
height?: string;
10+
fill?: string;
1111
}) => {
1212
return imageUrl === null ? (
1313
<svg width={width} height={height} viewBox="0 0 49 49" fill="none" xmlns="http://www.w3.org/2000/svg">

src/pages/ProfilePage.tsx

Lines changed: 124 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -6,116 +6,126 @@ import ProfileIcon from '@/assets/svgs/ProfileIcon';
66
import { updateUserProfile } from '@/apis/updateUserApi';
77

88
interface Props {
9-
userProfile?: {
10-
imageUrl: string | null;
11-
};
9+
userProfile?: {
10+
imageUrl: string | null;
11+
};
1212
}
1313

1414
const ProfilePage: FC<Props> = ({ userProfile }) => {
15-
const { data: user, error, isLoading, isError } = useGetProfile();
16-
const [editMode, setEditMode] = useState(false);
17-
const [imageUrl, setImageUrl] = useState(userProfile ? userProfile?.imageUrl : null);
18-
const [nickname, setNickname] = useState(user ? user?.user_nickname : '');
19-
const [selectedFile, setSelectedFile] = useState<File | null>(null);
15+
const { data: user, error, isLoading, isError } = useGetProfile();
16+
const [editMode, setEditMode] = useState(false);
17+
const [imageUrl, setImageUrl] = useState(userProfile ? userProfile?.imageUrl : null);
18+
const [nickname, setNickname] = useState(user ? user?.user_nickname : '');
19+
const [selectedFile, setSelectedFile] = useState<File | null>(null);
2020

21-
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
22-
const file = event.target.files ? event.target.files[0] : null;
23-
if (file) {
24-
const reader = new FileReader();
25-
reader.onloadend = () => {
26-
setImageUrl(reader.result as string);
27-
};
28-
reader.readAsDataURL(file);
29-
setSelectedFile(file);
30-
}
31-
};
21+
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
22+
const file = event.target.files ? event.target.files[0] : null;
23+
if (file) {
24+
const reader = new FileReader();
25+
reader.onloadend = () => {
26+
setImageUrl(reader.result as string);
27+
};
28+
reader.readAsDataURL(file);
29+
setSelectedFile(file);
30+
}
31+
};
3232

33+
const toggleEditMode = () => {
34+
setEditMode(!editMode);
35+
};
3336

34-
const toggleEditMode = () => {
35-
setEditMode(!editMode);
36-
};
37+
const resetData = () => {
38+
if (user) {
39+
setNickname(user.user_nickname);
40+
setImageUrl(userProfile ? userProfile.imageUrl : null);
41+
setSelectedFile(null);
42+
}
43+
setEditMode(false);
44+
};
3745

38-
const resetData = () => {
39-
if (user) {
40-
setNickname(user.user_nickname);
41-
setImageUrl(userProfile ? userProfile.imageUrl : null);
42-
setSelectedFile(null);
43-
}
44-
setEditMode(false);
45-
};
46+
const updateProfile = async () => {
47+
if (!user) return;
4648

47-
const updateProfile = async () => {
48-
if (!user) return;
49-
50-
const param = {
51-
id: user.id,
52-
user_nickname: nickname,
53-
file: selectedFile,
54-
};
55-
56-
try {
57-
const updatedData = await updateUserProfile(param);
58-
console.log('프로필 업데이트 성공:', updatedData);
59-
setEditMode(false);
60-
} catch (error) {
61-
console.error('프로필 업데이트 실패:', error);
62-
}
63-
};
64-
65-
if (isError) {
66-
// TODO: 추후 에러 처리
67-
console.error(error);
49+
const param = {
50+
id: user.id,
51+
user_nickname: nickname,
52+
file: selectedFile,
53+
};
54+
55+
try {
56+
const updatedData = await updateUserProfile(param);
57+
console.log('프로필 업데이트 성공:', updatedData);
58+
setEditMode(false);
59+
} catch (error) {
60+
console.error('프로필 업데이트 실패:', error);
6861
}
62+
};
63+
64+
if (isError) {
65+
// TODO: 추후 에러 처리
66+
console.error(error);
67+
}
6968

70-
return (
71-
<div className="flex min-h-dvh w-screen flex-col overflow-x-hidden px-6">
72-
{isLoading && <span className="loading" />}
73-
<nav className="navHeight w-full py-5 flex justify-between items-center px-5">
74-
<HistoryBackButton />
75-
<h1 className="text-xl font-semibold" hidden>프로필</h1>
76-
{/* 편집버튼 */}
77-
<button type="button" onClick={toggleEditMode} hidden={editMode}>
78-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
79-
<path d="M16.8617 4.48667L18.5492 2.79917C19.2814 2.06694 20.4686 2.06694 21.2008 2.79917C21.9331 3.53141 21.9331 4.71859 21.2008 5.45083L6.83218 19.8195C6.30351 20.3481 5.65144 20.7368 4.93489 20.9502L2.25 21.75L3.04978 19.0651C3.26323 18.3486 3.65185 17.6965 4.18052 17.1678L16.8617 4.48667ZM16.8617 4.48667L19.5 7.12499" stroke="#0F172A" strokeWidth="1.5" strokeLinejoin="round"/>
80-
</svg>
81-
</button>
82-
</nav>
83-
<div className="container mx-auto flex max-w-sm flex-1 flex-col gap-4 pb-[50px] pt-4">
84-
<div className="w-80 h-80 flex justify-center items-center relative mx-auto my-5">
85-
{/* <button type="button" hidden={!editMode} className="absolute right-2.5 top-0" onClick={() => handleImageButton()}>
69+
return (
70+
<div className="flex min-h-dvh w-screen flex-col overflow-x-hidden px-6">
71+
{isLoading && <span className="loading" />}
72+
<nav className="navHeight flex w-full items-center justify-between px-5 py-5">
73+
<HistoryBackButton />
74+
<h1 className="text-xl font-semibold" hidden>
75+
프로필
76+
</h1>
77+
{/* 편집버튼 */}
78+
<button type="button" onClick={toggleEditMode} hidden={editMode}>
79+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
80+
<path
81+
d="M16.8617 4.48667L18.5492 2.79917C19.2814 2.06694 20.4686 2.06694 21.2008 2.79917C21.9331 3.53141 21.9331 4.71859 21.2008 5.45083L6.83218 19.8195C6.30351 20.3481 5.65144 20.7368 4.93489 20.9502L2.25 21.75L3.04978 19.0651C3.26323 18.3486 3.65185 17.6965 4.18052 17.1678L16.8617 4.48667ZM16.8617 4.48667L19.5 7.12499"
82+
stroke="#0F172A"
83+
strokeWidth="1.5"
84+
strokeLinejoin="round"
85+
/>
86+
</svg>
87+
</button>
88+
</nav>
89+
<div className="container mx-auto flex max-w-sm flex-1 flex-col gap-4 pb-[50px] pt-4">
90+
<div className="relative mx-auto my-5 flex h-80 w-80 items-center justify-center">
91+
{/* <button type="button" hidden={!editMode} className="absolute right-2.5 top-0" onClick={() => handleImageButton()}>
8692
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
8793
<path d="M16.8617 4.48667L18.5492 2.79917C19.2814 2.06694 20.4686 2.06694 21.2008 2.79917C21.9331 3.53141 21.9331 4.71859 21.2008 5.45083L6.83218 19.8195C6.30351 20.3481 5.65144 20.7368 4.93489 20.9502L2.25 21.75L3.04978 19.0651C3.26323 18.3486 3.65185 17.6965 4.18052 17.1678L16.8617 4.48667ZM16.8617 4.48667L19.5 7.12499" stroke="#0F172A" strokeWidth="1.5" strokeLinejoin="round"/>
8894
</svg>
8995
</button> */}
90-
<ProfileIcon imageUrl={imageUrl} width="100%" height="100%" fill="#CCCFC4" />
91-
{editMode &&
92-
<label htmlFor="img" className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-50 text-white flex-col gap-2.5 rounded-full">
93-
<span>이미지를 드래그해서 넣어주세요!</span>
94-
<input type="file" id="img" className="file-input file-input-bordered file-input-success file-input-sm w-4/5 max-w-xs" accept="image/*" onChange={(e) => handleImageChange(e)} />
95-
</label>
96-
}
97-
</div>
98-
<div className="info-form flex flex-col gap-1 mt-8">
99-
<div className="flex justify-between items-center">
100-
<span>이름</span>
101-
<input
102-
type="text"
103-
value={user?.user_name}
104-
readOnly
105-
className="p-2 bg-transparent"
106-
/>
107-
</div>
108-
<div className="flex justify-between items-center">
109-
<span>닉네임</span>
110-
<input
111-
type="text"
112-
value={nickname as string}
113-
onChange={(e) => setNickname(e.target.value)}
114-
readOnly={!editMode}
115-
className={`p-2 ${!editMode ? 'bg-transparent' : ''}`}
116-
/>
117-
</div>
118-
{/* <div className="flex justify-between items-center">
96+
<ProfileIcon imageUrl={imageUrl} width="100%" height="100%" fill="#CCCFC4" />
97+
{editMode && (
98+
<label
99+
htmlFor="img"
100+
className="absolute inset-0 flex flex-col items-center justify-center gap-2.5 rounded-full bg-black bg-opacity-50 text-white"
101+
>
102+
<span>이미지를 드래그해서 넣어주세요!</span>
103+
<input
104+
type="file"
105+
id="img"
106+
className="file-input file-input-bordered file-input-success file-input-sm w-4/5 max-w-xs"
107+
accept="image/*"
108+
onChange={(e) => handleImageChange(e)}
109+
/>
110+
</label>
111+
)}
112+
</div>
113+
<div className="info-form mt-8 flex flex-col gap-1">
114+
<div className="flex items-center justify-between">
115+
<span>이름</span>
116+
<input type="text" value={user?.user_name} readOnly className="bg-transparent p-2" />
117+
</div>
118+
<div className="flex items-center justify-between">
119+
<span>닉네임</span>
120+
<input
121+
type="text"
122+
value={nickname as string}
123+
onChange={(e) => setNickname(e.target.value)}
124+
readOnly={!editMode}
125+
className={`p-2 ${!editMode ? 'bg-transparent' : ''}`}
126+
/>
127+
</div>
128+
{/* <div className="flex justify-between items-center">
119129
<span>이메일</span>
120130
<input
121131
type="text"
@@ -125,27 +135,25 @@ const ProfilePage: FC<Props> = ({ userProfile }) => {
125135
className="p-2 bg-transparent"
126136
/>
127137
</div> */}
128-
<div className="flex justify-between items-center">
129-
<span>전화번호</span>
130-
<input
131-
type="text"
132-
value={user?.phone}
133-
readOnly
134-
className="p-2 bg-transparent"
135-
/>
136-
</div>
137-
</div>
138-
139-
{ editMode &&
140-
<div className="flex gap-2 mt-20">
141-
<button className="btn btn-outline btn-primary flex-1" onClick={() => resetData()}>취소</button>
142-
<button type="submit" className="btn btn-outline btn-primary flex-1" onClick={() => updateProfile()}>수정완료</button>
143-
</div>
144-
}
145-
</div>
146-
138+
<div className="flex items-center justify-between">
139+
<span>전화번호</span>
140+
<input type="text" value={user?.phone} readOnly className="bg-transparent p-2" />
141+
</div>
147142
</div>
148-
);
143+
144+
{editMode && (
145+
<div className="mt-20 flex gap-2">
146+
<button className="btn btn-outline btn-primary flex-1" onClick={() => resetData()}>
147+
취소
148+
</button>
149+
<button type="submit" className="btn btn-outline btn-primary flex-1" onClick={() => updateProfile()}>
150+
수정완료
151+
</button>
152+
</div>
153+
)}
154+
</div>
155+
</div>
156+
);
149157
};
150158

151159
export default ProfilePage;

0 commit comments

Comments
 (0)