앞 커밋(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 <noreply@anthropic.com>
104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import {useSite} from '@site/lib/site-context';
|
|
import {heroFacts, sectionBody} from '@site/lib/derive';
|
|
|
|
function dotDate(iso: string): string {
|
|
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
|
|
return m ? `${m[1]}. ${m[2]}. ${m[3]}` : iso;
|
|
}
|
|
|
|
function Post({publishedAt, body}: {publishedAt: string; body: string}) {
|
|
return (
|
|
<article className="post">
|
|
<time dateTime={publishedAt}>{dotDate(publishedAt)}</time>
|
|
<p>{body}</p>
|
|
</article>
|
|
);
|
|
}
|
|
|
|
const HOME_MAX = 3;
|
|
const FULL_FIRST = 5;
|
|
|
|
function Blog({full}: {full?: boolean}) {
|
|
const payload = useSite();
|
|
const posts = [...(payload.posts ?? [])].sort((a, b) => b.publishedAt.localeCompare(a.publishedAt));
|
|
if (posts.length === 0) return null;
|
|
const shown = posts.slice(0, full ? FULL_FIRST : HOME_MAX);
|
|
const older = full ? posts.slice(FULL_FIRST) : [];
|
|
const more = !full && posts.length > HOME_MAX;
|
|
|
|
return (
|
|
<section className="wrap" id={full ? 'blog' : undefined}>
|
|
<p className="label">JOURNAL</p>
|
|
<h2>미니 블로그</h2>
|
|
<p className="lead">사장님이 직접 남기는 소식입니다.</p>
|
|
<div className="posts" data-blog={full ? 'full' : 'home'}>
|
|
{shown.map((item) => (
|
|
<Post key={item.postId} publishedAt={item.publishedAt} body={item.body} />
|
|
))}
|
|
</div>
|
|
{more && (
|
|
<a href="#/story" className="link more-link">
|
|
이야기에서 지난 소식 더 보기
|
|
</a>
|
|
)}
|
|
{older.length > 0 && (
|
|
<details className="fold">
|
|
<summary className="link">지난 소식 {older.length}개</summary>
|
|
<div className="posts">
|
|
{older.map((item) => (
|
|
<Post key={item.postId} publishedAt={item.publishedAt} body={item.body} />
|
|
))}
|
|
</div>
|
|
</details>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export function Intro() {
|
|
const payload = useSite();
|
|
const {narrative, place} = payload;
|
|
const own = sectionBody(payload, 'intro');
|
|
const about = own.length > 0 ? own : narrative.about;
|
|
const facts = heroFacts(payload);
|
|
|
|
return (
|
|
<>
|
|
<div data-pg="story">
|
|
<Blog full />
|
|
</div>
|
|
<div data-pg="home">
|
|
{about.length > 0 && (
|
|
<section className="wrap" id="about" aria-labelledby="about-heading">
|
|
<p className="label">ABOUT</p>
|
|
<h2 id="about-heading">{place.name} 소개</h2>
|
|
{narrative.summary && <p className="lead strong">{narrative.summary}</p>}
|
|
<p className="lead">{about[0]}</p>
|
|
{about.length > 1 && (
|
|
<details className="fold">
|
|
<summary className="link">더 읽기</summary>
|
|
{about.slice(1).map((paragraph, i) => (
|
|
<p key={i} className="lead">
|
|
{paragraph}
|
|
</p>
|
|
))}
|
|
</details>
|
|
)}
|
|
{facts.length > 0 && (
|
|
<div className="facts">
|
|
{facts.map((row) => (
|
|
<div key={row.key ?? row.label}>
|
|
<small>{row.label}</small>
|
|
<b>{row.value}</b>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
)}
|
|
<Blog />
|
|
</div>
|
|
</>
|
|
);
|
|
}
|