fix(WEB): 모바일 반응형 UI 개선 및 개소 정보 수정 모달 추가

- CalendarHeader: 모바일에서 주/월 버튼 2줄 레이아웃으로 분리
- MobileCard: 제목 텍스트 overflow 시 truncate 적용
- DetailActions: 모바일 하단 sticky 버튼 바 overflow 수정
- OrderDetailForm: 모바일 하단 sticky 버튼 바 overflow 수정
- LocationDetailPanel: 오픈사이즈 옆 수정 버튼에 모달 연결
- LocationListPanel: 개소 목록에 수정/삭제 버튼 추가
- LocationEditModal: 개소 정보 수정 팝업 신규 생성

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-27 11:28:12 +09:00
parent 6586db4996
commit 55e92bc7b4
8 changed files with 441 additions and 88 deletions

View File

@@ -26,24 +26,75 @@ export function CalendarHeader({
{ value: 'month', label: '월' },
];
return (
<div className="flex flex-col gap-3 pb-3 border-b">
{/* PC: 타이틀 + 네비게이션 | 뷰전환 + 필터 (한 줄) */}
{/* 모바일: 타이틀 / 네비게이션 + 뷰전환 / 필터 (세 줄) */}
// 뷰 전환 버튼 렌더링 (재사용)
const renderViewTabs = (className?: string) => (
<div className={cn('flex rounded-md border', className)}>
{views.map((v) => (
<button
key={v.value}
onClick={() => onViewChange(v.value)}
className={cn(
'px-3 py-1 text-sm font-medium transition-colors',
'first:rounded-l-md last:rounded-r-md',
view === v.value
? 'bg-primary text-primary-foreground'
: 'hover:bg-primary/10 text-foreground'
)}
>
{v.label}
</button>
))}
</div>
);
{/* 1줄(모바일) / 좌측(PC): 타이틀 */}
return (
<div className="flex flex-col gap-2 pb-3 border-b">
{/* 모바일 전용: 타이틀 */}
{titleSlot && (
<div className="xl:hidden text-base font-semibold text-foreground">
{titleSlot}
</div>
)}
{/* 2줄(모바일) / 전체(PC): 네비게이션 + 뷰전환 + 필터 */}
<div className="flex flex-col xl:flex-row xl:items-center xl:justify-between gap-3">
{/* 좌측: (PC에서만 타이틀) + 네비게이션 */}
{/* 모바일: 네비게이션 (1줄) + 뷰전환 (2줄) */}
<div className="flex xl:hidden flex-col gap-2">
{/* 1줄: 네비게이션 < 년월 > */}
<div className="flex items-center justify-center gap-1">
<Button
variant="outline"
size="icon"
className="h-7 w-7 shrink-0 hover:bg-primary/10"
onClick={onPrevMonth}
>
<ChevronLeft className="h-4 w-4" />
</Button>
<span className="text-sm font-bold text-center whitespace-nowrap px-1">
{formatYearMonth(currentDate)}
</span>
<Button
variant="outline"
size="icon"
className="h-7 w-7 shrink-0 hover:bg-primary/10"
onClick={onNextMonth}
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
{/* 2줄: 뷰 전환 탭 */}
<div className="flex justify-center">
{renderViewTabs()}
</div>
</div>
{/* PC: 타이틀 + 네비게이션 | 뷰전환 + 필터 (한 줄) */}
<div className="hidden xl:flex items-center justify-between">
{/* 좌측: 타이틀 + 네비게이션 */}
<div className="flex items-center gap-4">
{titleSlot && (
<span className="hidden xl:block text-base font-semibold text-foreground">
<span className="text-base font-semibold text-foreground">
{titleSlot}
</span>
)}
@@ -70,54 +121,19 @@ export function CalendarHeader({
<ChevronRight className="h-4 w-4" />
</Button>
</div>
{/* 모바일: 뷰 전환 탭 (네비게이션 옆) */}
<div className="flex xl:hidden rounded-md border">
{views.map((v) => (
<button
key={v.value}
onClick={() => onViewChange(v.value)}
className={cn(
'px-4 py-1.5 text-sm font-medium transition-colors',
'first:rounded-l-md last:rounded-r-md',
view === v.value
? 'bg-primary text-primary-foreground'
: 'hover:bg-primary/10 text-foreground'
)}
>
{v.label}
</button>
))}
</div>
</div>
{/* 우측(PC만): 뷰 전환 + 필터 */}
<div className="hidden xl:flex items-center gap-3">
<div className="flex rounded-md border">
{views.map((v) => (
<button
key={v.value}
onClick={() => onViewChange(v.value)}
className={cn(
'px-4 py-1.5 text-sm font-medium transition-colors',
'first:rounded-l-md last:rounded-r-md',
view === v.value
? 'bg-primary text-primary-foreground'
: 'hover:bg-primary/10 text-foreground'
)}
>
{v.label}
</button>
))}
</div>
{/* 우측: 뷰 전환 + 필터 */}
<div className="flex items-center gap-3">
{renderViewTabs('px-4 py-1.5')}
{filterSlot && <div className="flex items-center gap-2">{filterSlot}</div>}
</div>
</div>
{/* 3줄(모바일만): 필터 */}
{/* 모바일 전용: 필터 */}
{filterSlot && (
<div className="flex xl:hidden items-center gap-2">{filterSlot}</div>
)}
</div>
);
}
}