o2o-site-AEO/solution/shared/src/lib/catalog.ts
Mina Choi 013cdcfc9f [feat] solution/site,shared: 숙박 템플릿 8종 추가 — 라운드·시네마·빅타이포·부티크·일러스트·코랄·미니멀·솔숲
사장님이 고를 수 있는 숙박 템플릿이 고택 하나뿐이었다. 모두 stay2 콘텐츠(날씨·미니 블로그·
지역 이야기·객실·축제·일정·후기·엽서·노래)를 그대로 싣고 모양만 다르다.

- layouts/<id>/: 템플릿별 Frame·섹션·CSS. 데스크톱은 넓은 배치(1024px 이상), 모바일 우선
- layouts/kit/kit.css: 탭 전환·시트·예약 폼·접기 등 공통 구조
- shared templates.json · builder.ts · catalog.ts: 템플릿·LayoutId 등록
- seo/head.ts: 템플릿별 한글 웹폰트 — 서로 겹치지 않게 배정
- paper Frame·Intro: 같은 탭을 다시 눌러도 맨 위로, 미니 블로그를 글 여러 개로
- docs/TEMPLATES.md · TEMPLATE_DESIGN.md: 목록과 화면 규칙(간격·접기·✓ 표시·글꼴)

eslint·tsc 통과, vitest 123 passed, 템플릿 9개 굽기 성공 · 간격 검사 0건
2026-09-30 13:45:10 +09:00

47 lines
1.8 KiB
TypeScript

import catalog from '../data/templates.json';
import type {IndustryDef, IndustryType, LayoutId, TemplateDef} from '../types';
export type TemplateId = keyof typeof catalog.templates;
export const LAYOUT_IDS: readonly LayoutId[] = ['basic', 'paper', 'round', 'cinema', 'bigtype', 'boutique', 'graphic', 'coral', 'minimal', 'pine'];
export const TEMPLATES = catalog.templates as Record<TemplateId, TemplateDef>;
export const INDUSTRIES = catalog.industries as Record<IndustryType, IndustryDef>;
export function isTemplateId(id: string): id is TemplateId {
return Object.hasOwn(TEMPLATES, id);
}
export function templateOf(id: string): TemplateDef {
if (!isTemplateId(id)) throw new Error(`등록되지 않은 템플릿: ${id}`);
return TEMPLATES[id];
}
export function industryTemplates(industry: IndustryType): TemplateId[] {
return INDUSTRIES[industry].templates as TemplateId[];
}
export function resolveTemplateId(industry: IndustryType, stored: string | null | undefined): TemplateId {
const id = stored?.trim() || INDUSTRIES[industry].defaultTemplate;
if (!INDUSTRIES[industry].templates.includes(id)) {
throw new Error(`${industry} 업종에서 쓸 수 없는 템플릿: ${id}`);
}
return id as TemplateId;
}
for (const [id, template] of Object.entries(TEMPLATES)) {
if (!LAYOUT_IDS.includes(template.layout)) {
throw new Error(`템플릿 ${id}의 레이아웃이 등록되지 않았다: ${template.layout}`);
}
}
for (const [industry, def] of Object.entries(INDUSTRIES)) {
for (const id of [def.defaultTemplate, ...def.templates]) {
if (!isTemplateId(id)) throw new Error(`${industry} 업종이 없는 템플릿을 가리킨다: ${id}`);
}
if (!def.templates.includes(def.defaultTemplate)) {
throw new Error(`${industry} 업종의 기본 템플릿이 허용 목록에 없다: ${def.defaultTemplate}`);
}
}