import {ApiError} from '@/api'; import {notifyApiError} from '@/lib/notify'; import {callSiteApi, type ResultEnvelope} from './siteApi'; /** * 위저드에서 고른 템플릿을 서버에 저장한다(POST /v1/place/{id}/site/template). * * ★ 왜 저장하나: 고른 templateId 가 브라우저 상태로만 남으면 발행 잡(backend services/site_payload)이 * 읽을 곳이 없어 업종 기본 템플릿으로 굽는다 — 사장님이 고른 디자인과 실제 발행본이 갈린다. * ★ 주소(slug)와 달리 발행 뒤에도 저장된다. 서버가 잠그지 않는다 — * 디자인이 바뀌어도 URL 은 그대로라 색인이 깨지지 않기 때문이다(대신 재빌드 대상으로 표시된다). * ★ orval 을 쓰지 않는 이유는 siteApi.ts 참조. */ export async function saveSiteTemplate(placeId: string, templateId: string): Promise { // placeId 가 없으면(데모 경로) 저장할 사이트가 없다 — 네트워크를 한 번도 타지 않고 조용히 통과한다. if (!placeId) return; const res = await callSiteApi(`/v1/place/${placeId}/site/template`, { method: 'POST', body: JSON.stringify({template_id: templateId}), }); // ★ 거절도 200 으로 온다. 여기서 throw 하지 않으면 저장 안 된 템플릿이 저장된 것처럼 넘어가고, // 호출측은 알릴 근거를 잃는다. notifyApiError 가 읽는 자리(error.data)에 응답을 그대로 싣는다. if (res.result?.success === false) throw new ApiError(200, 'SITE_TEMPLATE_REJECTED', res); } /** * 보낸 순서대로 흘리는 저장 대기줄. * * ★ 병렬로 쏘면 먼저 누른 템플릿의 요청이 늦게 도착해 마지막 선택을 덮을 수 있다 — * 화면은 새 템플릿, 서버는 옛 템플릿이 되고 그 차이는 발행하고 나서야 드러난다. */ let saveChain: Promise = Promise.resolve(); /** 마지막 요청만 실패를 알린다 — 뒤엎힌 옛 선택의 실패까지 토스트로 쌓지 않는다. */ let saveSeq = 0; /** * 고른 템플릿을 저장하되 **화면을 기다리게 하지 않는다**. * * ★ 저장 실패가 위저드 진행을 막으면 안 된다. 템플릿은 주소와 달리 발행 뒤에도 바꿀 수 있는 값이라 * 여기서 사장님을 세울 이유가 없다 — 알리기만 하고 다음 단계로 보낸다. */ export function queueSiteTemplateSave(placeId: string | null, templateId: string) { if (!placeId || !templateId) return; saveSeq += 1; const seq = saveSeq; saveChain = saveChain.then(async () => { try { await saveSiteTemplate(placeId, templateId); } catch (error) { if (seq !== saveSeq) return; // 이미 다른 선택이 올라탔다 — 그쪽 결과만 알린다. notifyApiError(error, '템플릿 선택을 저장하지 못했습니다. 발행 전에 다시 골라 주세요.'); } }); }