o2o-negosium-original/negodata/front/src/lib/useOverlayRouter.ts
Mina Choi 9f947e4823 [feat] negodata/front: 견적 재생성·차수 체인 UI + 담당자/메모 + 스크롤·목업 정비
- 견적 재생성(다음 견적) 엔드포인트 연동 모달: 연결 공급사+협상단계 표시, 마지막 차수에서만
- 차수 체인 타임라인/견적번호 클릭 필터, 생성 응답 슬림(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>
2026-06-23 16:57:29 +09:00

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 };
}