import {useCallback} from 'react'; import {useSearchParams} from 'react-router'; /** * 위저드 단계는 **주소창이 소유한다** — `/builder?step=<이름>`. * * ★ 예전엔 스토어(zustand)의 `step` 이 화면을 골랐다. 브라우저가 보기엔 주소가 한 번도 안 바뀌니 * 뒤로가기가 이전 단계가 아니라 위저드 **밖으로** 나갔고, 북마크한 주소는 언제나 첫 화면으로 열렸다. * ★ 번호가 아니라 이름을 쓴다. 단계는 늘고 준다(업종 선택이 첫 화면에서 빠지면서 뒤가 전부 한 칸 * 당겨졌다) — 번호를 주소에 박아 두면 그날 이후 옛 북마크·랜딩 링크가 조용히 다른 화면을 연다. */ export const WIZARD_STEPS = [ /** 상호명으로 내 가게 찾기 — 위저드의 시작점이다. */ 'search', /** 업종 직접 고르기. 검색 결과가 업종을 못 정했을 때, 또는 [바꾸기] 로 들어온다. */ 'industry', 'collect', 'template', 'generating', 'editor', ] as const; export type WizardStep = (typeof WIZARD_STEPS)[number]; /** 위저드가 끝나고 편집기로 넘어가는 단계. 숫자를 화면마다 외우지 않게 한 곳에 둔다. */ export const EDITOR_STEP: WizardStep = 'editor'; /** * 진행 표시(WizardSteps)에 찍히는 번호. * * ★ 업종 화면은 '내 가게 확인' 안의 **갈래**라 같은 1번이다. 업종은 검색 결과가 정하고 * 못 정했을 때만 사람에게 묻는 것이라, 별도 단계로 세면 사장님은 안 겪을 수도 있는 단계를 * 진행 표시에서 계속 보게 된다. * ★ 생성 화면은 진행 표시를 그리지 않는다(0 은 "표시할 번호가 없다"는 뜻이다). */ export const STEP_NUMBER: Record = { search: 1, industry: 1, collect: 2, template: 3, generating: 0, editor: 0, }; function parseStep(value: string | null): WizardStep | null { return WIZARD_STEPS.includes(value as WizardStep) ? (value as WizardStep) : null; } /** * 주소창에 `step` 이 없을 때 열리는 화면. * * ★ `?placeId=` 딥링크는 **편집하러 온 것**이다(내 사이트 목록의 [사이트 편집]) — 위저드를 * 다시 걷게 하지 않는다. 위저드가 방금 만든 사업장(`flow=onboarding`)만 예외다: * 그건 아직 수집·템플릿을 지나는 중이고 그때는 step 이 항상 함께 붙어 있다. */ export function defaultStep(params: URLSearchParams): WizardStep { return params.get('placeId') && params.get('flow') !== 'onboarding' ? EDITOR_STEP : 'search'; } interface GoOptions { /** 히스토리에 쌓지 않는다 — 화면이 바뀌지 않는 정정(주소창 정리)일 때만 쓴다. */ replace?: boolean; /** 같은 이동에서 함께 바꿀 쿼리. null 이면 지운다. 단계와 함께 한 번에 바꿔야 중간 주소가 안 생긴다. */ params?: Record; } /** * 지금 단계와, 단계를 옮기는 함수. * * ★ 다른 쿼리(`placeId`·`flow`·`q`)는 건드리지 않고 `step` 만 갈아 끼운다 — 단계를 옮길 때마다 * 사업장 딥링크가 떨어져 나가면 새로고침 한 번에 어느 가게였는지가 사라진다. */ export function useWizardStep(): [WizardStep, (next: WizardStep, options?: GoOptions) => void] { const [searchParams, setSearchParams] = useSearchParams(); const step = parseStep(searchParams.get('step')) ?? defaultStep(searchParams); const go = useCallback( (next: WizardStep, options?: GoOptions) => { setSearchParams( (prev) => { const params = new URLSearchParams(prev); params.set('step', next); for (const [key, value] of Object.entries(options?.params ?? {})) { if (value === null) params.delete(key); else params.set(key, value); } return params; }, {replace: options?.replace}, ); }, [setSearchParams], ); return [step, go]; }