Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
59a6a27
style: 공통 Header 컴포넌트의 AlarmIcon 크기 및 위치 변경
yooseohyeon Jul 31, 2026
e9efc04
refactor: 공통 Header의 프로필 메뉴를 ProfileMenuTrigger로 분리
yooseohyeon Jul 31, 2026
1614f05
feat: 공통 Header 프로필 메뉴에 실제 메뉴 항목 연결 및 디자인 변경
yooseohyeon Jul 31, 2026
ea854de
refactor: 프로필 메뉴(ProfileMenuTrigger) 상태·키보드 로직 단순화
yooseohyeon Jul 31, 2026
775d5b2
feat: 찜한 기사님 목록 페이지 경로 변경 (/favorites/movers → /movers/favorites)
yooseohyeon Jul 31, 2026
a5c5767
feat: 공통 체크박스 컴포넌트(Checkbox) 추가 및 MoverCard에 선택 UI 확장
yooseohyeon Jul 31, 2026
b1067cf
feat: 찜한 기사님 목록 페이지 구현
yooseohyeon Jul 31, 2026
4b08121
feat: 찜한 기사님 목록 로딩 UI를 스켈레톤으로 변경
yooseohyeon Jul 31, 2026
fae25e0
fix: 기사 카드 개별 선택 체크박스가 동작하지 않던 문제 수정
yooseohyeon Jul 31, 2026
10976d7
fix: 찜한 기사님 페이지에서 기사님 찾기 메뉴가 활성으로 표시되던 문제 수정
yooseohyeon Jul 31, 2026
36468a4
feat: 찜한 기사님 페이지에서 limit을 5로 변경
yooseohyeon Jul 31, 2026
22a7118
feat: 페이지 버튼 기본 배경색 제거 및 disabled 버튼에 hover 효과 삭제
yooseohyeon Jul 31, 2026
2ee1977
feat: 찜한 기사님 목록을 더보기 방식으로 변경하고 전체선택 삭제 흐름 개선
yooseohyeon Jul 31, 2026
6b9d9d4
fix: 찜 전체 해제 확인 모달에서 인원 수가 잠시 0으로 표시되던 문제 수정
yooseohyeon Jul 31, 2026
050e5c2
refactor: 찜한 기사님 훅·페이지 컴포넌트 책임 분리
yooseohyeon Jul 31, 2026
3b88ad4
fix: 찜 일괄 해제 시 부분 실패를 구분해 처리하도록 수정
yooseohyeon Jul 31, 2026
e934860
fix: 전체 선택 이후 개별 해제 시 전체에서 제외하는 방식으로 변경
yooseohyeon Jul 31, 2026
5a16326
refactor: 찜한 기사님 페이지에서 PageHeader를 서버 컴포넌트로 이동 및 켈레톤 카드 개수를 실제 limit과 통일
yooseohyeon Jul 31, 2026
9053b74
refactor: 찜한 기사님 카드에서 체크 토글 시 체크 상태가 바뀐 카드만 리렌더링되도록 수정
yooseohyeon Jul 31, 2026
debd49b
fix: 프로필 메뉴가 히스토리 이동·Tab 이탈 시에도 닫히도록 수정
yooseohyeon Jul 31, 2026
310222c
fix: 찜 일괄 해제 부분 성공 시 인증 오류와 토스트가 겹치지 않도록 수정
yooseohyeon Jul 31, 2026
f6f62bc
fix: 프로필 메뉴 렌더 중 상태 업데이트 제거
yooseohyeon Jul 31, 2026
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
40 changes: 0 additions & 40 deletions src/app/favorites/movers/page.tsx

This file was deleted.

18 changes: 18 additions & 0 deletions src/app/movers/favorites/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Metadata } from "next";

import { PageHeader } from "@/components/common/PageHeader";
import FavoriteMoversPageClient from "@/components/mover/FavoriteMoversPageClient";

export const metadata: Metadata = {
title: "찜한 기사님",
description: "찜한 이사 기사님 목록을 확인하세요.",
};

export default function FavoriteMoversPage() {
return (
<div className="bg-background-subtle flex w-full flex-col">
<PageHeader title="찜한 기사님" />
<FavoriteMoversPageClient />
</div>
);
}
5 changes: 4 additions & 1 deletion src/components/auth/CustomerAuthGate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface CustomerAuthGateProps {
children: ReactNode;
/** 세션 확인 중 표시. 기본은 공통 로딩 메시지 */
loadingMessage?: string;
/** 세션 확인 중 커스텀 UI. 있으면 loadingMessage 대신 사용 */
loadingFallback?: ReactNode;
}

/**
Expand All @@ -24,6 +26,7 @@ interface CustomerAuthGateProps {
export default function CustomerAuthGate({
children,
loadingMessage = "로그인 상태를 확인하는 중입니다.",
loadingFallback,
}: CustomerAuthGateProps) {
const router = useRouter();
const pathname = usePathname();
Expand All @@ -47,7 +50,7 @@ export default function CustomerAuthGate({
}, [isPending, isAuthenticated, isMover, pathname, router]);

if (isPending || !canFetch) {
return <EstimatesQueryStatus message={loadingMessage} />;
return loadingFallback ?? <EstimatesQueryStatus message={loadingMessage} />;
}

return children;
Expand Down
66 changes: 66 additions & 0 deletions src/components/common/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import Image from "next/image";
import { type InputHTMLAttributes, type ReactNode } from "react";

import { cn } from "@/lib/utils/cn";

export interface CheckboxProps extends Omit<
InputHTMLAttributes<HTMLInputElement>,
"type" | "onChange" | "checked" | "size"
> {
checked: boolean;
onCheckedChange: (checked: boolean) => void;
/** 체크박스 옆 표시 라벨. 없으면 aria-label 필수 */
label?: ReactNode;
labelClassName?: string;
}

/**
* Figma check-box (hit 36 · box 20 · radius 4)
* ReceivedRequestsPage 체크 UI와 동일 패턴
*/
export default function Checkbox({
checked,
onCheckedChange,
label,
labelClassName,
className,
disabled,
id,
"aria-label": ariaLabel,
...rest
}: CheckboxProps) {
return (
<label
className={cn(
"inline-flex cursor-pointer items-center gap-4",
disabled && "cursor-not-allowed opacity-50",
className,
)}
>
<input
{...rest}
id={id}
type="checkbox"
checked={checked}
disabled={disabled}
aria-label={label ? undefined : ariaLabel}
className="peer sr-only"
onChange={(event) => onCheckedChange(event.target.checked)}
/>
<span className="peer-focus-visible:ring-border-brand rounded-4 flex size-36 shrink-0 items-center justify-center peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2">
<span
className={cn(
"rounded-4 flex size-20 items-center justify-center",
checked ? "bg-background-brand" : "border-border-default bg-background-surface border",
)}
aria-hidden="true"
>
{checked ? <Image src="/icons/checkbox-check.svg" alt="" width={10} height={6} /> : null}
</span>
</span>
{label ? <span className={labelClassName}>{label}</span> : null}
</label>
);
}
Loading