업종을 먼저 고르게 하면 경계에서 멈춘다("우리는 카페인가 음식점인가"). 그런데 상호명은
100% 안다. 그리고 업종은 AI 를 부를 필요가 없다 — 카카오·네이버 검색 응답에 분류가
이미 들어 있고(category_group_code), 지금까지 받아 놓고 안 썼다.
- 단계가 스토어에서 주소창으로: ?step=search|industry|collect|template|generating|editor.
번호가 아니라 이름인 이유 — 단계가 4→3 으로 줄어 옛 북마크가 다른 화면을 연다
- 시작점이 상호명 검색(Step2PlaceSearch)이다. 업종 선택은 못 정했을 때의 갈래로 남는다
- 업종은 후보의 category 로 잡히고, 못 정하면 고르게 하고, 정해져도 [바꾸기] 로 바꾼다.
★ 확정 뒤 변경은 신원 확인부터 다시 받는다 — Req_UpdatePlace 에 category 가 없어
PATCH 로 못 고치고, placeAdapter 가 리페치마다 덮어써서 조용히 되돌아간다
- 랜딩 진입: ?new=1 · ?q=<상호명> · ?industry=<업종>. 한 번 읽고 replace 로 지운다
- ★ ?new=1 이 setSearchParams({}) 로 **모든 쿼리를 날리던 것**을 고쳤다 — q 가 읽히기 전에 사라졌다
- selectIndustry 가 사장님이 친 상호·위치를 지우던 것도 고쳤다(업종이 첫 화면일 땐 늘 빈 값이라 안 보였다)
- 로고는 어디서나 / 로 간다. 에디터에서는 span 이라 아예 안 눌렸다
tsc·eslint·vite build 통과
97 lines
3.9 KiB
TypeScript
97 lines
3.9 KiB
TypeScript
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<WizardStep, number> = {
|
|
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<string, string | null>;
|
|
}
|
|
|
|
/**
|
|
* 지금 단계와, 단계를 옮기는 함수.
|
|
*
|
|
* ★ 다른 쿼리(`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];
|
|
}
|