From fad20e8958e6d664df8412ed196435da2285419e Mon Sep 17 00:00:00 2001 From: Mina Choi Date: Wed, 30 Sep 2026 14:58:29 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20solution/site,shared:=20=EC=83=88=20?= =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=208=EC=A2=85=20=EC=97=85=EC=A2=85?= =?UTF-8?q?=20=ED=99=95=EC=9E=A5=20=E2=80=94=20=EC=95=9E=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B=EC=97=90=EC=84=9C=20=EB=B9=A0=EC=A7=84=20=EB=B3=B8?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 앞 커밋(18a2874)에 템플릿 등록과 레이아웃 변경이 빠지고 다른 작업의 새 파일 (services/activity_feed.py, 어디서도 아직 import 하지 않음)이 대신 들어갔다. 같은 작업 폴더를 다른 세션이 함께 쓰고 있었다. - templates.json: cafe · restaurant · clinic 에 라운드~솔숲 8종 - layouts/*/Rooms.tsx: 객실별 '예약 요청' 버튼은 숙박일 때만, 영문 라벨은 unitWord - layouts/*/Frame.tsx · Intro.tsx: 이용안내 머리말 · 미니 블로그 설명을 업종 중립으로 eslint·tsc 통과, vitest 123 passed, 33개 사이트 굽기 성공 Co-Authored-By: Claude Opus 5.5 --- solution/shared/src/data/templates.json | 30 ++++++++++++++++++-- solution/site/src/layouts/bigtype/Frame.tsx | 2 +- solution/site/src/layouts/bigtype/Rooms.tsx | 24 +++++++++------- solution/site/src/layouts/boutique/Frame.tsx | 7 +++-- solution/site/src/layouts/boutique/Intro.tsx | 2 +- solution/site/src/layouts/boutique/Rooms.tsx | 16 +++++++---- solution/site/src/layouts/cinema/Frame.tsx | 2 +- solution/site/src/layouts/cinema/Intro.tsx | 2 +- solution/site/src/layouts/cinema/Rooms.tsx | 16 +++++++---- solution/site/src/layouts/coral/Frame.tsx | 2 +- solution/site/src/layouts/coral/Rooms.tsx | 16 +++++++---- solution/site/src/layouts/graphic/Frame.tsx | 2 +- solution/site/src/layouts/graphic/Rooms.tsx | 11 ++++--- solution/site/src/layouts/minimal/Frame.tsx | 2 +- solution/site/src/layouts/minimal/Rooms.tsx | 16 +++++++---- solution/site/src/layouts/paper/Intro.tsx | 2 +- solution/site/src/layouts/pine/Frame.tsx | 2 +- solution/site/src/layouts/pine/Rooms.tsx | 11 ++++--- solution/site/src/layouts/round/Frame.tsx | 2 +- solution/site/src/layouts/round/Rooms.tsx | 11 ++++--- 20 files changed, 116 insertions(+), 62 deletions(-) diff --git a/solution/shared/src/data/templates.json b/solution/shared/src/data/templates.json index d6e7cc2..f58bfe9 100644 --- a/solution/shared/src/data/templates.json +++ b/solution/shared/src/data/templates.json @@ -502,7 +502,15 @@ "simple", "magazine", "retro", - "paper" + "paper", + "round", + "cinema", + "bigtype", + "boutique", + "graphic", + "coral", + "minimal", + "pine" ], "sections": [ { @@ -629,7 +637,15 @@ "simple", "magazine", "retro", - "paper" + "paper", + "round", + "cinema", + "bigtype", + "boutique", + "graphic", + "coral", + "minimal", + "pine" ], "sections": [ { @@ -754,7 +770,15 @@ "defaultTemplate": "simple", "templates": [ "simple", - "magazine" + "magazine", + "round", + "cinema", + "bigtype", + "boutique", + "graphic", + "coral", + "minimal", + "pine" ], "sections": [ { diff --git a/solution/site/src/layouts/bigtype/Frame.tsx b/solution/site/src/layouts/bigtype/Frame.tsx index 785c14f..ab50385 100644 --- a/solution/site/src/layouts/bigtype/Frame.tsx +++ b/solution/site/src/layouts/bigtype/Frame.tsx @@ -30,7 +30,7 @@ type PageId = 'home' | 'area' | 'stay' | 'story'; const PAGE_HEAD: Record, {title: string; note: string}> = { area: {title: '지역', note: '대문에서 걸어 닿는 자리와, 이 도시가 남긴 이야기입니다.'}, - stay: {title: '이용안내', note: '객실 상세와 요금, 예약하는 방법입니다.'}, + stay: {title: '이용안내', note: '상세 안내와 요금, 예약하는 방법입니다.'}, story: {title: '이야기', note: '사장님이 남기는 글과 다녀가신 분들의 후기, 그리고 엽서 한 장입니다.'}, }; diff --git a/solution/site/src/layouts/bigtype/Rooms.tsx b/solution/site/src/layouts/bigtype/Rooms.tsx index dcebdca..d35103a 100644 --- a/solution/site/src/layouts/bigtype/Rooms.tsx +++ b/solution/site/src/layouts/bigtype/Rooms.tsx @@ -1,9 +1,10 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; import {BOOK_EVENT} from '../paper/Rooms'; +import {unitWord} from '../kit/unitWord'; import {Sheet} from '../paper/Sheet'; import {Idx} from './SectionHead'; @@ -32,6 +33,7 @@ function specRows(unit: UnitView, facts: Parameters[0]) { export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -78,7 +80,7 @@ export function Rooms() { const [main, sub] = splitName(unit.name); return (
- + {unit.images.length > 0 && ( {main} 자세히 + - + {lodging && ( + + )}
); })} diff --git a/solution/site/src/layouts/boutique/Frame.tsx b/solution/site/src/layouts/boutique/Frame.tsx index 3d04fe6..55ea233 100644 --- a/solution/site/src/layouts/boutique/Frame.tsx +++ b/solution/site/src/layouts/boutique/Frame.tsx @@ -30,7 +30,7 @@ const PAGE_HEAD: Record, {en: string; title: string; not stay: { en: 'Stay', title: '이용안내 · 예약', - note: '객실 상세와 요금, 예약하는 방법입니다.', + note: '상세 안내와 요금, 예약하는 방법입니다.', }, story: { en: 'Story', @@ -131,6 +131,7 @@ function useTabs() { export function Frame({children}: {children: ReactNode}) { const payload = useSite(); const {place, site} = payload; + const lodging = place.category === PlaceCategory.LODGING; const tabs = useTabs(); const page = usePage(); const solid = useSolid(page); @@ -245,7 +246,7 @@ export function Frame({children}: {children: ReactNode}) { onClick={() => setMenuOpen(false)} > {pad2(i + 1)} - {PAGE_EN[tab.page]} + {tab.page === 'stay' && !lodging ? 'Guide' : PAGE_EN[tab.page]} {tab.label} ))} @@ -254,7 +255,7 @@ export function Frame({children}: {children: ReactNode}) { {(Object.keys(PAGE_HEAD) as Exclude[]).map((id) => (
-

{PAGE_HEAD[id].en}

+

{id === 'stay' && !lodging ? 'Guide' : PAGE_HEAD[id].en}

{id === 'area' ? (place.addressLocality?.replace(/(시|군)$/, '') ?? PAGE_HEAD.area.title) : PAGE_HEAD[id].title}

diff --git a/solution/site/src/layouts/boutique/Intro.tsx b/solution/site/src/layouts/boutique/Intro.tsx index 324f8c2..ab45a39 100644 --- a/solution/site/src/layouts/boutique/Intro.tsx +++ b/solution/site/src/layouts/boutique/Intro.tsx @@ -31,7 +31,7 @@ export function Blog({full}: {full?: boolean}) { return (
- + 미니 블로그
diff --git a/solution/site/src/layouts/boutique/Rooms.tsx b/solution/site/src/layouts/boutique/Rooms.tsx index 9923f19..43663c0 100644 --- a/solution/site/src/layouts/boutique/Rooms.tsx +++ b/solution/site/src/layouts/boutique/Rooms.tsx @@ -1,9 +1,10 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; import {BOOK_EVENT} from '../paper/Rooms'; +import {unitWord} from '../kit/unitWord'; import {Sheet} from '../paper/Sheet'; function splitName(name: string): [string, string | undefined] { @@ -31,6 +32,7 @@ const book = () => window.dispatchEvent(new Event(BOOK_EVENT)); export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -43,7 +45,7 @@ export function Rooms() {
-

ROOMS

+

{unitWord(payload.place.category).toUpperCase()}

{sectionName(payload, spec.path, `${spec.label} 안내`)}

@@ -77,7 +79,7 @@ export function Rooms() { return (
-

ROOM {String(i + 1).padStart(2, '0')}

+

{unitWord(payload.place.category).toUpperCase()} {String(i + 1).padStart(2, '0')}

{main} {sub && {sub}}

@@ -109,9 +111,11 @@ export function Rooms() { - + {lodging && ( + + )}
diff --git a/solution/site/src/layouts/cinema/Frame.tsx b/solution/site/src/layouts/cinema/Frame.tsx index 000d622..4495d88 100644 --- a/solution/site/src/layouts/cinema/Frame.tsx +++ b/solution/site/src/layouts/cinema/Frame.tsx @@ -36,7 +36,7 @@ const PAGE_HEAD: Record, {label: string; title: string; stay: { label: 'STAY', title: '이용안내 · 예약', - note: '객실 상세와 요금, 예약하는 방법입니다.', + note: '상세 안내와 요금, 예약하는 방법입니다.', }, story: { label: 'STORY', diff --git a/solution/site/src/layouts/cinema/Intro.tsx b/solution/site/src/layouts/cinema/Intro.tsx index ee3739b..3a9e2e7 100644 --- a/solution/site/src/layouts/cinema/Intro.tsx +++ b/solution/site/src/layouts/cinema/Intro.tsx @@ -30,7 +30,7 @@ function Blog({full}: {full?: boolean}) {

JOURNAL

미니 블로그

-

사장님이 남기는 숙소 이야기입니다.

+

사장님이 직접 남기는 소식입니다.

{shown.map((item) => ( diff --git a/solution/site/src/layouts/cinema/Rooms.tsx b/solution/site/src/layouts/cinema/Rooms.tsx index 928f686..c6bd350 100644 --- a/solution/site/src/layouts/cinema/Rooms.tsx +++ b/solution/site/src/layouts/cinema/Rooms.tsx @@ -1,9 +1,10 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; import {BOOK_EVENT} from '../paper/Rooms'; +import {unitWord} from '../kit/unitWord'; import {Sheet} from '../paper/Sheet'; function splitName(name: string): [string, string | undefined] { @@ -32,6 +33,7 @@ const book = () => window.dispatchEvent(new Event(BOOK_EVENT)); export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -45,7 +47,7 @@ export function Rooms() {
-

ROOMS

+

{unitWord(payload.place.category).toUpperCase()}

{sectionName(payload, spec.path, `${spec.label} 안내`)}

{units.slice(0, HOME_UNITS).map((unit) => ( @@ -69,7 +71,7 @@ export function Rooms() { return (
-

ROOM {String(i + 1).padStart(2, '0')}

+

{unitWord(payload.place.category).toUpperCase()} {String(i + 1).padStart(2, '0')}

{main} {sub && {sub}}

@@ -101,9 +103,11 @@ export function Rooms() { - + {lodging && ( + + )}
diff --git a/solution/site/src/layouts/coral/Frame.tsx b/solution/site/src/layouts/coral/Frame.tsx index 8eab08d..a8ec407 100644 --- a/solution/site/src/layouts/coral/Frame.tsx +++ b/solution/site/src/layouts/coral/Frame.tsx @@ -36,7 +36,7 @@ const PAGE_HEAD: Record, {kicker: string; title: string; stay: { kicker: 'Reservation', title: '이용안내 · 예약', - note: '객실 상세와 요금, 예약하는 방법입니다.', + note: '상세 안내와 요금, 예약하는 방법입니다.', }, story: { kicker: 'Story', diff --git a/solution/site/src/layouts/coral/Rooms.tsx b/solution/site/src/layouts/coral/Rooms.tsx index 550c66f..1f5f2ba 100644 --- a/solution/site/src/layouts/coral/Rooms.tsx +++ b/solution/site/src/layouts/coral/Rooms.tsx @@ -1,9 +1,10 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; import {BOOK_EVENT} from '../paper/Rooms'; +import {unitWord} from '../kit/unitWord'; import {Sheet} from '../paper/Sheet'; function splitName(name: string): [string, string | undefined] { @@ -30,6 +31,7 @@ const HOME_MAX = 4; export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -42,7 +44,7 @@ export function Rooms() { <>
-

Room View

+

{lodging ? 'Room View' : unitWord(payload.place.category)}

{sectionName(payload, spec.path, `${spec.label} 안내`)}

{units.slice(0, HOME_MAX).map((unit, i) => ( @@ -74,7 +76,7 @@ export function Rooms() { return (

- Room {String(i + 1).padStart(2, '0')} + {unitWord(payload.place.category)} {String(i + 1).padStart(2, '0')}

{main} {sub && {sub}} @@ -105,9 +107,11 @@ export function Rooms() { - + {lodging && ( + + )}

diff --git a/solution/site/src/layouts/graphic/Frame.tsx b/solution/site/src/layouts/graphic/Frame.tsx index 5011ad2..3f6d09d 100644 --- a/solution/site/src/layouts/graphic/Frame.tsx +++ b/solution/site/src/layouts/graphic/Frame.tsx @@ -29,7 +29,7 @@ type PageId = 'home' | 'area' | 'stay' | 'story'; const PAGE_HEAD: Record, {title: string; note: string}> = { area: {title: '지역 소개', note: '대문에서 걸어 닿는 자리와, 이 도시가 남긴 이야기입니다.'}, - stay: {title: '이용안내', note: '객실 상세와 요금, 예약하는 방법입니다.'}, + stay: {title: '이용안내', note: '상세 안내와 요금, 예약하는 방법입니다.'}, story: {title: '이야기', note: '사장님이 남기는 글과 다녀가신 분들의 후기, 그리고 엽서 한 장입니다.'}, }; diff --git a/solution/site/src/layouts/graphic/Rooms.tsx b/solution/site/src/layouts/graphic/Rooms.tsx index 628662d..90809c6 100644 --- a/solution/site/src/layouts/graphic/Rooms.tsx +++ b/solution/site/src/layouts/graphic/Rooms.tsx @@ -1,5 +1,5 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; @@ -60,6 +60,7 @@ function UnitSheet({unit, onClose}: {unit: UnitView | null; onClose: () => void} export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -136,9 +137,11 @@ export function Rooms() { - + {lodging && ( + + )}
diff --git a/solution/site/src/layouts/minimal/Frame.tsx b/solution/site/src/layouts/minimal/Frame.tsx index 71d0b6b..74f8365 100644 --- a/solution/site/src/layouts/minimal/Frame.tsx +++ b/solution/site/src/layouts/minimal/Frame.tsx @@ -36,7 +36,7 @@ const PAGE_HEAD: Record, {kicker: string; title: string; stay: { kicker: 'Reservation', title: '이용안내 · 예약', - note: '객실 상세와 요금, 예약하는 방법입니다.', + note: '상세 안내와 요금, 예약하는 방법입니다.', }, story: { kicker: 'Story', diff --git a/solution/site/src/layouts/minimal/Rooms.tsx b/solution/site/src/layouts/minimal/Rooms.tsx index bc902ea..32ae09d 100644 --- a/solution/site/src/layouts/minimal/Rooms.tsx +++ b/solution/site/src/layouts/minimal/Rooms.tsx @@ -1,9 +1,10 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; import {BOOK_EVENT} from '../paper/Rooms'; +import {unitWord} from '../kit/unitWord'; import {Sheet} from '../paper/Sheet'; function splitName(name: string): [string, string | undefined] { @@ -30,6 +31,7 @@ const HOME_MAX = 4; export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -42,7 +44,7 @@ export function Rooms() { <>
-

Stay

+

{lodging ? 'Stay' : unitWord(payload.place.category)}

{sectionName(payload, spec.path, `${spec.label} 안내`)}

{units.slice(0, HOME_MAX).map((unit) => ( @@ -71,7 +73,7 @@ export function Rooms() { return (

- Room {String(i + 1).padStart(2, '0')} + {unitWord(payload.place.category)} {String(i + 1).padStart(2, '0')}

{main} {sub && {sub}} @@ -102,9 +104,11 @@ export function Rooms() { - + {lodging && ( + + )}

diff --git a/solution/site/src/layouts/paper/Intro.tsx b/solution/site/src/layouts/paper/Intro.tsx index 5b9d7ff..6c44a4f 100644 --- a/solution/site/src/layouts/paper/Intro.tsx +++ b/solution/site/src/layouts/paper/Intro.tsx @@ -22,7 +22,7 @@ export function Blog({full}: {full?: boolean}) {

BLOG

미니 블로그

- 사장님이 남기는 숙소 이야기입니다. + 사장님이 직접 남기는 소식입니다.

{shown.map((item) => ( diff --git a/solution/site/src/layouts/pine/Frame.tsx b/solution/site/src/layouts/pine/Frame.tsx index 04bf67b..1899c7e 100644 --- a/solution/site/src/layouts/pine/Frame.tsx +++ b/solution/site/src/layouts/pine/Frame.tsx @@ -36,7 +36,7 @@ const PAGE_HEAD: Record, {kicker: string; title: string; stay: { kicker: '이용안내', title: '이용안내 · 예약', - note: '객실 상세와 요금, 예약하는 방법입니다.', + note: '상세 안내와 요금, 예약하는 방법입니다.', }, story: { kicker: '이야기', diff --git a/solution/site/src/layouts/pine/Rooms.tsx b/solution/site/src/layouts/pine/Rooms.tsx index eeb23ba..8b52e95 100644 --- a/solution/site/src/layouts/pine/Rooms.tsx +++ b/solution/site/src/layouts/pine/Rooms.tsx @@ -1,5 +1,5 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; @@ -30,6 +30,7 @@ const HOME_MAX = 4; export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -102,9 +103,11 @@ export function Rooms() { - + {lodging && ( + + )}
diff --git a/solution/site/src/layouts/round/Frame.tsx b/solution/site/src/layouts/round/Frame.tsx index cd95a92..971f39f 100644 --- a/solution/site/src/layouts/round/Frame.tsx +++ b/solution/site/src/layouts/round/Frame.tsx @@ -36,7 +36,7 @@ const PAGE_HEAD: Record, {kicker: string; title: string; stay: { kicker: '이용안내', title: '이용안내 · 예약', - note: '객실 상세와 요금, 예약하는 방법입니다.', + note: '상세 안내와 요금, 예약하는 방법입니다.', }, story: { kicker: '이야기', diff --git a/solution/site/src/layouts/round/Rooms.tsx b/solution/site/src/layouts/round/Rooms.tsx index eeb23ba..8b52e95 100644 --- a/solution/site/src/layouts/round/Rooms.tsx +++ b/solution/site/src/layouts/round/Rooms.tsx @@ -1,5 +1,5 @@ import {useState} from 'react'; -import {factText, sanitizeUnits} from '@o2o/shared'; +import {factText, sanitizeUnits, PlaceCategory} from '@o2o/shared'; import {useSite} from '@site/lib/site-context'; import {sectionName, unitSpec, unitViews, type UnitView} from '@site/lib/derive'; import {Car} from '../paper/Car'; @@ -30,6 +30,7 @@ const HOME_MAX = 4; export function Rooms() { const payload = useSite(); + const lodging = payload.place.category === PlaceCategory.LODGING; const units = unitViews(payload); const spec = unitSpec(payload); const raw = new Map(sanitizeUnits(payload.units).map((unit) => [unit.unitId, unit])); @@ -102,9 +103,11 @@ export function Rooms() { - + {lodging && ( + + )}