Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/components/common/NavigateHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ export default function NavigationHeader({
const bg = bgClassName ?? "bg-[#FFFFFF]";

return (
<div
className={`h-[60px] flex items-center bg-[#FFFFFF] px-4 py-[10px] ${bg}`}
>
<div className={`nav-header ${bg}`}>
<button
type="button"
onClick={onBack}
Expand All @@ -43,7 +41,9 @@ export default function NavigationHeader({
</button>

<div className="flex-1 text-center">
<div className={`text-Title1 text-semiblod ${titleClassName ?? ""}`}>
<div
className={`text-Title1 font-semibold text-black ${titleClassName ?? ""}`}
>
{title}
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,23 @@
background: linear-gradient(135deg, #b7b7f3 0%, #6666e5 100%);
}

/* NavigationHeader (PWA safe-area 대응) */
.nav-header {
height: 60px;
display: flex;
align-items: center;
padding: 10px 16px;
width: 100%;
background: #ffffff;
}

@media (display-mode: standalone) {
.nav-header {
padding-top: calc(10px + env(safe-area-inset-top));
height: calc(60px + env(safe-area-inset-top));
}
}

.bg-grad-calendar-2 {
background: linear-gradient(135deg, #a7b8fc 0%, #6666e5 100%);
}
Expand Down
43 changes: 39 additions & 4 deletions app/routes/chat/components/ChatSortFilterSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export function SortFilterSheet<T extends string = SortOption>({
options,
title = "정렬 필터",
applyLabel = "적용하기",
titleClassName,
optionClassName,
containerClassName,
applyButtonClassName,
}: {
open: boolean;
value: T;
Expand All @@ -30,6 +34,10 @@ export function SortFilterSheet<T extends string = SortOption>({
options?: SortOptionItem<T>[];
title?: string;
applyLabel?: string;
titleClassName?: string;
optionClassName?: string;
containerClassName?: string;
applyButtonClassName?: string;
}) {
if (!open) return null;

Expand All @@ -46,9 +54,22 @@ export function SortFilterSheet<T extends string = SortOption>({
/>

{/* 시트 */}
<div className="fixed left-1/2 -translate-x-1/2 bottom-[0px] w-full max-w-[480px] h-[500px] bg-white rounded-t-[12px] pt-[20px] px-4 flex flex-col">
<div
className={[
"fixed left-1/2 -translate-x-1/2 bottom-[0px] w-full max-w-[480px] h-[500px] bg-white rounded-t-[12px] pt-[20px] px-4 flex flex-col",
containerClassName ?? "",
]
.join(" ")
.trim()}
>
<div className="w-full max-w-[480px] h-[70px] fixed left-1/2 -translate-x-1/2">
<div className="px-4 font-medium text-[15px] leading-[20px] text-text-black mb-3">{title}</div>
<div
className={["px-4 text-medium text-text-black mb-3", titleClassName ?? ""]
.join(" ")
.trim()}
>
{title}
</div>

<div className="bg-[#F3F4F8] px-4 py-3">
<div className="flex gap-6">
Expand All @@ -58,6 +79,7 @@ export function SortFilterSheet<T extends string = SortOption>({
label={option.label}
active={value === option.value}
onClick={() => onChange(option.value)}
className={optionClassName}
/>
))}
</div>
Expand All @@ -68,7 +90,12 @@ export function SortFilterSheet<T extends string = SortOption>({
<div className="flex justify-center py-6">
<button
onClick={onApply}
className="px-6 w-full h-11 rounded-[12px] bg-[#6666E5] text-white text-[15px] leading-[18px] font-semibold"
className={[
"px-6 w-full h-11 rounded-[12px] bg-[#6666E5] text-white text-SemiBold",
applyButtonClassName ?? "",
]
.join(" ")
.trim()}
>
{applyLabel}
</button>
Expand All @@ -82,15 +109,23 @@ function SortOptionButton({
label,
active,
onClick,
className,
}: {
label: string;
active: boolean;
onClick: () => void;
className?: string;
}) {
return (
<button
onClick={onClick}
className={`font-medium text-[14px] leading-[20px] ${active ? "text-text-black" : "text-text-gray3"}`}
className={[
"text-medium",
active ? "text-text-black" : "text-text-gray3",
className ?? "",
]
.join(" ")
.trim()}
>
{label}
</button>
Expand Down
3 changes: 3 additions & 0 deletions app/routes/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ export default function ChatPage() {
onChange={setPendingSort}
onClose={() => setIsSortOpen(false)}
onApply={applySort}
titleClassName="text-[14px] font-semibold"
optionClassName="text-[12px]"
applyButtonClassName="text-[13px] font-semibold"
/>
</div>
);
Expand Down
6 changes: 4 additions & 2 deletions app/routes/mypage/edit/edit-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ export default function MyPageEdit() {
setOriginalNickname(nextNickname);
setOriginalAddress(nextAddress);
setOriginalDetailAddress(nextDetailAddress);
setSuccessMessage("주소 변경 완료");
setSuccessMessage("회원정보 변경 완료");
} catch (error) {
console.error("Failed to update address:", error);
toast.error("주소 변경에 실패했습니다.");
Expand Down Expand Up @@ -477,7 +477,9 @@ export default function MyPageEdit() {
);

if (!response.data.isSuccess) {
throw new Error(response.data.message || "회원정보 변경 실패");
throw new Error(
response.data.message || "회원정보 변경 실패",
);
}

setNickname(nextNickname);
Expand Down
3 changes: 3 additions & 0 deletions app/routes/mypage/likes/likes-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ export default function MyPageLikes() {
}
title="정렬 필터"
applyLabel="적용하기"
titleClassName="text-[14px] font-semibold"
optionClassName="text-[12px]"
applyButtonClassName="text-[13px] font-semibold"
/>
</div>
</div>
Expand Down
83 changes: 47 additions & 36 deletions app/routes/mypage/notification/notifications-content.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router";
import NavigationHeader from "../../../components/common/NavigateHeader";
import { useHideHeader } from "../../../hooks/useHideHeader";
Expand Down Expand Up @@ -31,7 +31,6 @@ export default function MyPageNotifications() {
const [appPush, setAppPush] = useState(true);
const [emailPush, setEmailPush] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const initialSettingsRef = useRef<NotificationSettingResponse | null>(null);

useEffect(() => {
const fetchSetting = async () => {
Expand All @@ -49,11 +48,6 @@ export default function MyPageNotifications() {
setBenefitPush(Boolean(data.marketingConsent));
setAppPush(Boolean(data.appPushEnabled));
setEmailPush(Boolean(data.emailEnabled));
initialSettingsRef.current = {
marketingConsent: Boolean(data.marketingConsent),
appPushEnabled: Boolean(data.appPushEnabled),
emailEnabled: Boolean(data.emailEnabled),
};
} catch (error) {
console.error("Failed to load notification setting:", error);
}
Expand All @@ -62,13 +56,7 @@ export default function MyPageNotifications() {
fetchSetting();
}, []);

const saveSettings = async () => {
const payload = {
marketingConsent: benefitPush,
appPushEnabled: appPush,
emailEnabled: emailPush,
};

const saveSettings = async (payload: NotificationSettingResponse) => {
const response = await axiosInstance.put<CustomResponseString>(
"/api/v1/users/me/notification-settings",
payload,
Expand All @@ -78,30 +66,24 @@ export default function MyPageNotifications() {
throw new Error(response.data.message || "알림 설정 변경 실패");
}

initialSettingsRef.current = payload;
};

const handleBack = async () => {
const updateSettings = async (next: NotificationSettingResponse) => {
if (isSaving) return;

const initial = initialSettingsRef.current;
const hasChanges = initial
? initial.marketingConsent !== benefitPush ||
initial.appPushEnabled !== appPush ||
initial.emailEnabled !== emailPush
: false;

if (!hasChanges) {
navigate(-1);
return;
}
const prev = {
marketingConsent: benefitPush,
appPushEnabled: appPush,
emailEnabled: emailPush,
};

try {
setIsSaving(true);
await saveSettings();
navigate(-1);
await saveSettings(next);
} catch (error) {
console.error("Failed to update notification setting:", error);
setBenefitPush(prev.marketingConsent);
setAppPush(prev.appPushEnabled);
setEmailPush(prev.emailEnabled);
} finally {
setIsSaving(false);
}
Expand All @@ -110,11 +92,13 @@ export default function MyPageNotifications() {
return (
<div className="h-screen-full bg-[#F3F4F8]">
<div className="w-full bg-white shadow-2xl flex flex-col">
<div className="h-[60px]">
<NavigationHeader title="알림 설정" onBack={handleBack} />
<div className="pt-[env(safe-area-inset-top)]">
<div className="h-[60px]">
<NavigationHeader title="알림 설정" onBack={() => navigate(-1)} />
</div>
</div>

<div style={{ height: `calc(100vh - 60px - 67px)` }}>
<div style={{ height: `calc(100vh - 60px - 67px - env(safe-area-inset-top))` }}>
<div className="bg-white px-4 py-6">
{/* 혜택 푸시 */}
<div className="mb-6">
Expand All @@ -136,7 +120,16 @@ export default function MyPageNotifications() {
</div>
<button
type="button"
onClick={() => setBenefitPush((v) => !v)}
disabled={isSaving}
onClick={() => {
const next = {
marketingConsent: !benefitPush,
appPushEnabled: appPush,
emailEnabled: emailPush,
};
setBenefitPush(next.marketingConsent);
updateSettings(next);
}}
className={`relative w-[36px] h-[24px] rounded-full transition-colors ${
benefitPush ? "bg-[#6D6AFE]" : "bg-[#D4D4D9]"
}`}
Expand All @@ -162,7 +155,16 @@ export default function MyPageNotifications() {
<div className="text-[14px] leading-[20px] font-medium text-[#171718]">앱푸시</div>
<button
type="button"
onClick={() => setAppPush((v) => !v)}
disabled={isSaving}
onClick={() => {
const next = {
marketingConsent: benefitPush,
appPushEnabled: !appPush,
emailEnabled: emailPush,
};
setAppPush(next.appPushEnabled);
updateSettings(next);
}}
className={`relative w-[36px] h-[24px] rounded-full transition-colors ${
appPush ? "bg-[#6D6AFE]" : "bg-[#D4D4D9]"
}`}
Expand All @@ -180,7 +182,16 @@ export default function MyPageNotifications() {
<div className="text-[14px] leading-[20px] font-medium text-[#171718] mb-[8px]">이메일</div>
<button
type="button"
onClick={() => setEmailPush((v) => !v)}
disabled={isSaving}
onClick={() => {
const next = {
marketingConsent: benefitPush,
appPushEnabled: appPush,
emailEnabled: !emailPush,
};
setEmailPush(next.emailEnabled);
updateSettings(next);
}}
className={`relative w-[36px] h-[24px] rounded-full transition-colors ${
emailPush ? "bg-[#6D6AFE]" : "bg-[#D4D4D9]"
}`}
Expand Down
Loading