diff --git a/src/layouts/myPageLayout.tsx b/src/layouts/myPageLayout.tsx index d9340d2..06b24c7 100644 --- a/src/layouts/myPageLayout.tsx +++ b/src/layouts/myPageLayout.tsx @@ -1,4 +1,4 @@ -import { Outlet, NavLink } from "react-router-dom"; +import { Outlet, NavLink, Link } from "react-router-dom"; import { Calendar, CreditCard, @@ -24,10 +24,13 @@ export default function MyPageLayout() { {/* Header */}
-
- 잇츠파인 Eatsfine -
-
diff --git a/src/pages/myPage/myInfoPage.tsx b/src/pages/myPage/myInfoPage.tsx index d21fda3..c981e30 100644 --- a/src/pages/myPage/myInfoPage.tsx +++ b/src/pages/myPage/myInfoPage.tsx @@ -1,74 +1,112 @@ +import { phoneNumber } from "@/utils/phoneNumber"; import { Camera, Save } from "lucide-react"; -import { useRef, useState } from "react"; +import { useEffect, useRef, useState, type ChangeEvent } from "react"; + +type Form = { + email: string; + nickname: string; + phone: string; +}; export default function MyInfoPage() { const [isEditing, setIsEditing] = useState(false); const fileInputRef = useRef(null); - const [profileImage, setProfileImage] = useState(null); - const [form, setForm] = useState({ + const [originalImageFile, setOriginalImageFile] = useState(null); + const [draftImageFile, setDraftImageFile] = useState(null); + // const objectUrlRef = useRef(null); + + const shownFile = isEditing ? draftImageFile : originalImageFile; + const [shownUrl, setShownUrl] = useState(null); + + useEffect(() => { + if (!shownFile) { + setShownUrl(null); + return; + } + const url = URL.createObjectURL(shownFile); + setShownUrl(url); + + return () => URL.revokeObjectURL(url); + }, [shownFile]); + + const [original, setOriginal] = useState
({ email: "user@example.com", nickname: "맛있는유저", phone: "010-1234-5678", }); + const [draft, setDraft] = useState(original); - const handleChange = (key: keyof typeof form, value: string) => { - setForm((prev) => ({ ...prev, [key]: value })); + const handleEditStart = () => { + setDraft(original); + setDraftImageFile(originalImageFile); + setIsEditing(true); + }; + + const handleChange = (key: keyof Form, value: string) => { + setDraft((prev) => ({ ...prev, [key]: value })); }; const handleImageClick = () => { fileInputRef.current?.click(); }; - const handleImageChange = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - - const previewUrl = URL.createObjectURL(file); - setProfileImage(previewUrl); + const handleImageChange = (e: ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + setDraftImageFile(file); //TODO: 서버 업로드에 사용 + e.target.value = ""; //같은 파일 재선택 가능하도록 설정 + }; - // TODO: 서버 업로드용 file은 따로 저장해도 됨 -}; + const isValidPhone = (value: string) => { + const digits = value.replace(/\D/g, ""); + return digits.length === 10 || digits.length === 11; + }; const handleSave = () => { // TODO: API 호출 + if (!isValidPhone(draft.phone)) { + alert("전화번호를 올바르게 입력해주세요."); + return; + } + // TODO: API 성공후 + setOriginal(draft); + setOriginalImageFile(draftImageFile); setIsEditing(false); }; const handleCancel = () => { - // TODO: 원래 값으로 롤백하고 싶으면 여기서 처리 + setDraft(original); + setDraftImageFile(originalImageFile); setIsEditing(false); }; - return ( -
+
{/* title */} -
-

내 정보

+
+

내 정보

{!isEditing ? ( ) : ( -
+
)} @@ -78,49 +116,46 @@ export default function MyInfoPage() {
{/* avatar */}
-
- {profileImage ? ( - + {shownUrl ? ( + 프로필 이미지 + /> ) : ( - + )} -
+
- {isEditing && ( + {isEditing && ( <> - - + + /> - )} + )}
- {/* form */}
{/* 아이디 */}
- +

아이디는 변경할 수 없습니다 @@ -129,14 +164,12 @@ export default function MyInfoPage() { {/* 이메일 */}

- + handleChange("email", e.target.value)} - className={`w-full rounded-lg border px-4 py-3 text-md ${ + className={`w-full rounded-lg border px-4 py-3 ${ isEditing ? "border-gray-300 bg-white" : "border-gray-200 bg-gray-50 text-gray-500" @@ -146,14 +179,12 @@ export default function MyInfoPage() { {/* 닉네임 */}
- + handleChange("nickname", e.target.value)} - className={`w-full rounded-lg border px-4 py-3 text-md ${ + className={`w-full rounded-lg border px-4 py-3 ${ isEditing ? "border-gray-300 bg-white" : "border-gray-200 bg-gray-50 text-gray-500" @@ -163,14 +194,16 @@ export default function MyInfoPage() { {/* 전화번호 */}
- + handleChange("phone", e.target.value)} - className={`w-full rounded-lg border px-4 py-3 text-md ${ + value={draft.phone} + onChange={(e) => + handleChange("phone", phoneNumber(e.target.value)) + } + inputMode="numeric" + autoComplete="tel" + className={`w-full rounded-lg border px-4 py-3 ${ isEditing ? "border-gray-300 bg-white" : "border-gray-200 bg-gray-50 text-gray-500" diff --git a/src/pages/myPage/myPage.tsx b/src/pages/myPage/myPage.tsx index edf6d51..80edcf8 100644 --- a/src/pages/myPage/myPage.tsx +++ b/src/pages/myPage/myPage.tsx @@ -11,7 +11,7 @@ export default function MyPage() { return ( }> - } /> + } /> } /> } /> } /> diff --git a/src/pages/myPage/paymentPage.tsx b/src/pages/myPage/paymentPage.tsx index 686e508..73f1249 100644 --- a/src/pages/myPage/paymentPage.tsx +++ b/src/pages/myPage/paymentPage.tsx @@ -5,7 +5,7 @@ import PaymentAddModal from "@/components/modals/paymentAddModal"; interface PaymentMethod { id: string; - type: "easy-pay"; + type: "간편결제"; name: string; detail: string; isDefault: boolean; @@ -16,7 +16,7 @@ export default function PaymentPage() { const [methods, setMethods] = useState([ { id: "1", - type: "easy-pay", + type: "간편결제", name: "카카오페이", detail: "", isDefault: false, @@ -41,20 +41,20 @@ export default function PaymentPage() { const [isModalOpen, setIsModalOpen] = useState(false); return ( -
+
{/* 헤더 영역 */}
-

결제수단 관리

-

+

결제수단 관리

+

예약 시 사용할 결제수단을 관리하세요

@@ -65,10 +65,10 @@ export default function PaymentPage() {
@@ -80,7 +80,7 @@ export default function PaymentPage() { {/* 정보 영역 */}
- {method.name} + {method.name} {method.detail} {method.isDefault && ( @@ -88,7 +88,7 @@ export default function PaymentPage() { )}
-

간편결제

+

{method.type}

@@ -97,26 +97,27 @@ export default function PaymentPage() { {!method.isDefault && ( )}
))}
- - setIsModalOpen(false)} - /> + {isModalOpen && ( + setIsModalOpen(false)} + /> + )} {methods.length === 0 && (
diff --git a/src/pages/myPage/reservationPage.tsx b/src/pages/myPage/reservationPage.tsx index 2dd82dc..dd5ce56 100644 --- a/src/pages/myPage/reservationPage.tsx +++ b/src/pages/myPage/reservationPage.tsx @@ -70,8 +70,8 @@ export default function ReservationPage() { return (
-

예약 현황

-

+

예약 현황

+

내 예약 내역을 확인하고 관리하세요

@@ -83,10 +83,8 @@ export default function ReservationPage() { key={tab} onClick={() => setActiveTab(tab as ReservationStatus)} className={cn( - "cursor-pointer pb-4 text-sm font-medium transition-all relative", - activeTab === tab - ? "text-blue-600" - : "text-gray-800 hover:text-gray-600", + "cursor-pointer pb-4 font-medium transition-all relative", + activeTab === tab ? "text-blue-600" : "hover:text-gray-700", )} > {tab} @@ -107,11 +105,11 @@ export default function ReservationPage() { >
-
-

{res.shopName}

+
+

{res.shopName}

-

{res.address}

+

{res.address}

@@ -156,7 +154,7 @@ export default function ReservationPage() {
{res.step} -
+
{res.status === "예약 확정" && ( <> - - )} {res.status === "방문 완료" && ( - )} @@ -210,13 +208,8 @@ function InfoItem({
{icon}
-

{label}

-

+

{label}

+

{value}

diff --git a/src/pages/myPage/settingPage.tsx b/src/pages/myPage/settingPage.tsx index 0db0379..132e03c 100644 --- a/src/pages/myPage/settingPage.tsx +++ b/src/pages/myPage/settingPage.tsx @@ -17,109 +17,101 @@ export default function SettingsPage() { }; return ( -
-
-

계정 설정

+
+

계정 설정

- {/* 비밀번호 변경 섹션 */} -
-
- -
-
-
-

- 비밀번호 변경 -

-

- 정기적인 비밀번호 변경으로 계정을 안전하게 보호하세요 -

-
- + {/* 비밀번호 변경 섹션 */} +
+
+ +
+
+
+

비밀번호 변경

+

+ 정기적인 비밀번호 변경으로 계정을 안전하게 보호하세요 +

+ +
+
+ + {/* 알림 설정 섹션 */} +
+
+
+
+
+

알림 설정

+

+ 받고 싶은 알림을 선택하세요 +

+
- {/* 알림 설정 섹션 */} -
-
- +
+ toggleNotification("reservation")} + /> + toggleNotification("promotion")} + /> + toggleNotification("review")} + />
-
-
-

알림 설정

-

- 받고 싶은 알림을 선택하세요 -

-
-
- toggleNotification("reservation")} +
+

알림 수신 방법

+
+ 이메일 + toggleNotification("email")} /> - toggleNotification("promotion")} - /> - toggleNotification("review")} + SMS + toggleNotification("sms")} />
- -
-

- 알림 수신 방법 -

-
- 이메일 - toggleNotification("email")} - /> -
-
- SMS - toggleNotification("sms")} - /> -
-
- -
+ +
+
- {/* 계정 탈퇴 섹션 */} -
-
- -
-
-
-

계정 탈퇴

-

- 계정을 삭제하면 모든 데이터가 영구적으로 삭제됩니다 -

-
- + {/* 계정 탈퇴 섹션 */} +
+
+ +
+
+
+

계정 탈퇴

+

+ 계정을 삭제하면 모든 데이터가 영구적으로 삭제됩니다 +

+
-
-
+
+
); } @@ -138,7 +130,7 @@ function ToggleButton({ return (
-

{label}

+

{label}

{description}

diff --git a/src/pages/myPage/storePage.tsx b/src/pages/myPage/storePage.tsx index 904e214..75e558b 100644 --- a/src/pages/myPage/storePage.tsx +++ b/src/pages/myPage/storePage.tsx @@ -73,14 +73,14 @@ export default function StorePage() {
-

내 가게 관리

-

+

내 가게 관리

+

등록한 식당을 관리하고 대시보드로 이동하세요

새 가게 등록 @@ -119,10 +119,10 @@ export default function StorePage() {
-

{shop.name}

+

{shop.name}

-

{shop.address}

- +

{shop.address}

+ {shop.category}
@@ -147,18 +147,14 @@ export default function StorePage() { {shop.status !== "승인 대기" && (
-

+

총 예약

-

- {shop.totalReservations} -

+

{shop.totalReservations}

-

- 평점 -

-

+

평점

+

-

- 리뷰 -

-

{shop.reviews}

+

리뷰

+

{shop.reviews}

)} @@ -178,7 +172,7 @@ export default function StorePage() { {/* 승인 대기 알림창: 승인 대기일 때만 표시 */} {shop.status === "승인 대기" && shop.notice && (
-

+

{shop.notice}

@@ -194,16 +188,14 @@ export default function StorePage() {
-

- 더 많은 데이터가 필요하신가요? -

-

+

더 많은 데이터가 필요하신가요?

+

프리미엄 플랜으로 업그레이드하고 AI 데이터 인사이트, 상세 분석 리포트를 받아보세요.

-
diff --git a/src/pages/myPage/subscriptionPage.tsx b/src/pages/myPage/subscriptionPage.tsx index c31b2ee..3b4c39e 100644 --- a/src/pages/myPage/subscriptionPage.tsx +++ b/src/pages/myPage/subscriptionPage.tsx @@ -93,8 +93,8 @@ export default function SubscriptionPage() { return (
-

구독 관리

-

+

구독 관리

+

나에게 맞는 플랜을 선택하세요