- 견적 재생성(다음 견적) 엔드포인트 연동 모달: 연결 공급사+협상단계 표시, 마지막 차수에서만 - 차수 체인 타임라인/견적번호 클릭 필터, 생성 응답 슬림(qt_id/session_count) 반영 - 상세 시트 스크롤 수정(min-h-0)·배경 스크롤 락(useScrollLock)·헤더2:탭1 비율 - 담당자=로그인 유저(store) 자동 전송 + 생성 모달 메모 입력 추가 - 목업/가짜 폴백 제거(담당자·견적명·번호·메모 → '-'), 시드 회사 placeholder UUID→실 UUID - 견적상태 CREATED 칩 pulse 제거 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.6 KiB
TypeScript
63 lines
2.6 KiB
TypeScript
import { useLocation, useNavigate, useSearchParams } from 'react-router';
|
|
|
|
// 시트/드로어/모달 같은 "오버레이" 열림 상태를 쿼리스트링으로 표현하는 단일 출처.
|
|
// 로컬 useState 대신 URL 에 담아 딥링크·뒤로가기·새로고침을 지원한다.
|
|
// 같은 그룹(keys) 안에서는 한 번에 하나만 연다(상호배타: 열 때 나머지 키 제거).
|
|
//
|
|
// const overlay = useOverlayRouter(['detail', 'new', 'modal']);
|
|
// overlay.get('detail') // ?detail=<id> 의 값(없으면 null) — 값 있는 오버레이
|
|
// overlay.has('new') // ?new 존재 여부 — 플래그 오버레이
|
|
// overlay.open('detail', id) // ?detail=<id> (다른 오버레이 키는 지움) — 히스토리 push
|
|
// overlay.open('new') // ?new (값 생략 시 '1')
|
|
// overlay.open('detail', id, { replace: true }) // 같은 오버레이 내 값만 교체(히스토리 안 쌓음)
|
|
// overlay.close() // 그룹 내 모든 오버레이 키 제거
|
|
const OVERLAY_PUSHED = '__overlayPushed';
|
|
|
|
export function useOverlayRouter<K extends string>(keys: readonly K[]) {
|
|
const [searchParams] = useSearchParams();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
const get = (key: K) => searchParams.get(key);
|
|
const has = (key: K) => searchParams.has(key);
|
|
|
|
// 현재 쿼리에서 그룹 키를 모두 지운 뒤 mutate 를 적용해 search 문자열을 만든다.
|
|
const buildSearch = (mutate: (params: URLSearchParams) => void) => {
|
|
const next = new URLSearchParams(searchParams);
|
|
keys.forEach((k) => next.delete(k));
|
|
mutate(next);
|
|
const s = next.toString();
|
|
return s ? `?${s}` : '';
|
|
};
|
|
|
|
const open = (key: K, value = '1', opts?: { replace?: boolean }) => {
|
|
const replace = opts?.replace ?? false;
|
|
navigate(
|
|
{ pathname: location.pathname, search: buildSearch((p) => p.set(key, value)) },
|
|
{
|
|
replace,
|
|
// push: 새 히스토리 → 닫을 때 navigate(-1)로 빠지게 표식.
|
|
// replace(같은 오버레이 안에서 값만 교체, 예: 차수 전환): 히스토리를 안 쌓고,
|
|
// 원래 열림이 push였는지 표식을 그대로 물려준다(닫기 동작이 일관되게 리스트로 빠지도록).
|
|
state: {
|
|
...(location.state ?? {}),
|
|
[OVERLAY_PUSHED]: replace ? location.state?.[OVERLAY_PUSHED] ?? false : true,
|
|
},
|
|
},
|
|
);
|
|
};
|
|
|
|
const close = () => {
|
|
if (location.state?.[OVERLAY_PUSHED]) {
|
|
navigate(-1);
|
|
return;
|
|
}
|
|
navigate(
|
|
{ pathname: location.pathname, search: buildSearch(() => {}) },
|
|
{ replace: true },
|
|
);
|
|
};
|
|
|
|
return { get, has, open, close };
|
|
}
|